Zernikalos
Quick Start

Integrating Zernikalos

Learn how to initialize the engine, load resources, and implement your first scene on Android, iOS, and Web.

1. Engine & View Setup

The first step is to prepare the surface where the engine will render. Each platform uses its own native view.

Zernikalos provides a custom ZernikalosView (which wraps a GLSurfaceView). Add it to your layout:

<zernikalos.ui.ZernikalosView
    android:id="@+id/render_surface"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Then, initialize the engine in your Activity:

val engine = Zernikalos()
val renderSurface = findViewById<ZernikalosView>(R.id.render_surface)

2. Core Concepts

Before diving into code, here are the three pillars of a Zernikalos application:

Loading Resources (.zko)

All 3D assets (models, textures, animations) must be bundled into a .zko file using the Nest App.

Place your .zko files in the src/main/assets/ directory of your Android project.

The ZContext

The ZContext is passed to all lifecycle methods. It gives you access to the active scene, the activeCamera, and the underlying rendering state.

Lifecycle Methods

You control the engine via the ZSceneStateHandler interface:

  • onReady: Called once when the engine is initialized. Ideal for loading models.
  • onUpdate: Called every frame. Use this for game logic and for ZActionPlayer.update() when playing skeletal clips (see Playing skeletal animations).
  • onResize: Called when the view dimensions change.
  • onRender: Called every frame for the final draw pass.

3. Basic State Handler Snippets

These snippets show the minimal logic required to get the engine running.

engine.initialize(renderSurface, object : ZSceneStateHandler {
    override fun onReady(context: ZContext, done: () -> Unit) {
        val bytes = assets.open("model.zko").use { it.readBytes() }
        val zko = loadFromProto(bytes)
        
        val scene = ZScene()
        scene.addChild(zko.root)
        
        context.scene = scene
        done()
    }
    override fun onUpdate(context: ZContext, done: () -> Unit) = done()
})

4. Complete Samples

Finally, here are full, copy-pasteable examples for each platform including camera setup and a basic rotation.

import android.os.Bundle
import androidx.activity.ComponentActivity
import kotlinx.coroutines.*
import zernikalos.*
import zernikalos.context.ZContext
import zernikalos.loader.loadFromProto
import zernikalos.objects.*
import zernikalos.scenestatehandler.ZSceneStateHandler
import zernikalos.search.findFirstCamera
import zernikalos.ui.ZernikalosView

class MainActivity : ComponentActivity() {
    private val scope = CoroutineScope(Job() + Dispatchers.Main)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val renderSurface = ZernikalosView(this)
        setContentView(renderSurface)

        val engine = Zernikalos()
        engine.initialize(renderSurface, object : ZSceneStateHandler {
            private var rootGroup: ZGroup? = null

            override fun onReady(context: ZContext, done: () -> Unit) {
                scope.launch {
                    val bytes = assets.open("scene.zko").use { it.readBytes() }
                    val zko = loadFromProto(bytes)
                    rootGroup = zko.root as? ZGroup

                    val scene = ZScene()
                    val camera = ZCamera()
                    scene.addChild(camera)
                    scene.addChild(zko.root)

                    context.scene = scene
                    context.activeCamera = findFirstCamera(scene)
                    done()
                }
            }

            override fun onUpdate(context: ZContext, done: () -> Unit) {
                rootGroup?.transform?.rotate(0.1f, 0f, 1f, 0f)
                done()
            }
        })
    }
}

What's next

On this page