Skip to content

Quick Start

zeganstyl edited this page Jul 17, 2020 · 13 revisions

Table of Contents

  1. Setup IntelliJ IDEA
  2. Setup Gradle
  3. Writing a code

Setup IntelliJ IDEA

Create new project in IDEA: File > New > Project

Choose Gradle

Choose Kotlin/JVM as framework

Write project name, for example "thelema-test-project"

Setup Gradle

Add jcenter to repositories:

repositories {
    jcenter()
}

To create application for desktop platform, add thelema-lwjgl3 to dependecies:

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    implementation "org.ksdfv.thelema:thelema-lwjgl3:0.1.0"
}

You can see build.gradle example

Writing a code

In kotlin directory src/main/kotlin create package and name it for somthing like "org.test"

In this package create object Main. Type main, and IDEA will suggest you create main function, accept it. Code will looks like this:

package com.test

object Main {
    @JvmStatic
    fun main(args: Array<String>) {
        
    }
}

To initialize engine objects, create a window and OpenGL context, write this in the main function:

val app = Lwjgl3App()

Now, we can create graphics objects. We will make a plane.

First, we create a mesh object that may be rendered, then create vertex and index buffers for mesh. The number of vertices need for plane is 4, each vertex consists of 3 position numbers and 2 texture coordinate numbers, and each such number occupies 4 bytes, respectively, the number of bytes occupied is 4 * 5 * 4. The number of indices is 6, since we need to display 2 triangles. 2 bytes are allocated for each index, since the Short type is used.

val mesh = Mesh()

val vertexInputs = VertexInputs(
    VertexInput(size = 3, name = "aPosition", type = GL_FLOAT, normalized = true),
    VertexInput(size = 2, name = "aUV", type = GL_FLOAT, normalized = true)
)

mesh.vertices = IVertexBuffer.build(
    DATA.bytes(4 * 5 * 4).apply {
        floatView().apply {
            // x, y, z    u, v
            put(1f, 0f, 1f,   1f, 1f)
            put(1f, 0f, -1f,   1f, 0f)
            put(-1f, 0f, 1f,   0f, 1f)
            put(-1f, 0f, -1f,   0f, 0f)
        }
    }, vertexInputs)

mesh.indices = IIndexBufferObject.build(
    DATA.bytes(6 * 2).apply {
        shortView().apply {
            put(
                0, 1, 2,
                3, 1, 2
            )
        }
    }, type = GL_UNSIGNED_SHORT)

Our mesh is ready, but we must create shader, so our device will know how to render mesh. So we write shader:

@Language("GLSL")
val shader = Shader(
        vertCode = """
attribute vec3 aPosition;
attribute vec2 aUV;
varying vec2 vUV;
uniform mat4 projViewModelTrans;

void main() {
    vUV = aUV;
    gl_Position = projViewModelTrans * vec4(aPosition, 1.0);
}""",
        fragCode = """
varying vec2 vUV;
uniform sampler2D tex;

void main() {
    gl_FragColor = texture2D(tex, vUV);
}""")

Annotation @Language("GLSL") is used, it is better to use GLSL Support plugin in IDEA and create language inspection for GLSL. It will be much easier to work with shader code.

As you can see, in shader we used texture, so we must create texture object. And also, we asign texture unit 0 to shader uniform:

shader["tex"] = 0
val texture = Texture2D(FS.internal("thelema-logo.png"))

You can take this image and put it to resources directory src/main/resources

Also we defined uniform matrix "projViewModelTrans" in shader. It is our camera projection matrix, so we must use camera object:

ActiveCamera.api = Camera(
    from = Vec3(0f, 2f, 2f),
    to = Vec3(0f, 0f, 0f),
    near = 0.1f,
    far = 100f
)

Camera will look from (0, 2, -2) to (0, 0, 0). ActiveCamera is singleton object, that may be accessed anywhere.

Also enable depth test, set background color to gray and enable alpha blending, in cause our texture have transparency:

GL.isDepthTestEnabled = true
GL.glClearColor(0.5f, 0.5f, 0.5f, 1f)
GL.setSimpleAlphaBlending()
GL.isBlendEnabled = true

And now, we want to render our mesh. We must do it in render loop. For this purpose, Thelema have array for function objects in GL and we can create some render function, that will be called each loop iteration:

GL.render {
    GL.glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)

    shader.bind()
    texture.bind(0)
    shader["projViewModelTrans"] = ActiveCamera.viewProjectionMatrix
    mesh.render(shader)
}

In render function we clear screen with gray, setup shader uniform for projection matrix and render mesh.

After all, we must start our render loop:

app.startLoop()

After execution window must appear:

screen

See full code, also see project example

If you want more code examples, see tests