Skip to content
zeganstyl edited this page Jun 28, 2020 · 7 revisions

Writing a code

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

In test 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()

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)
        }
    },
    VertexAttributes(VertexAttribute.Position, VertexAttribute.UV[0])
)

mesh.indices = IIndexBufferObject.build(
    DATA.bytes(6 * 2).apply {
        shortView().apply {
            put(
                0, 1, 2,
                3, 1, 2
            )
        }
    },
    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 a_position;
attribute vec2 a_texCoord0;
varying vec2 vUV;
uniform mat4 projViewModelTrans;

void main() {
    vUV = a_texCoord0;
    gl_Position = projViewModelTrans * vec4(a_position, 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"))

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

ActiveCamera.api = Camera().apply {
    lookAt(Vec3(0f, 2f, 2f), IVec3.Zero)
    near = 0.1f
    far = 100f
    update()
}

Camera will look from (0, 3, -3) 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()
    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()

As you can see, creating meshes and shaders manually is a tedious task and the code is cumbersome. There are modules for Thelema that allow generating shaders by kotlin code and loading meshes from files created in graphic editors. But sources of these modules will appear after reaching a certain level of fundraising. You can support the project on Patreon

Clone this wiki locally