Skip to content

Load a scene document

Run the scene from the last page from a scene document instead of Kotlin: save it as a resource, load it with SceneLoader, and hand it to the scene runtime.

components: camera · light · mesh_renderer · spin_control Vulkan WebGPU

A scene document is a *.scene.json file that describes ECS entities as data. It is the file AwakeKt Studio saves, and it describes the same content as the scene DSL, so a scene can move between the two. This page builds on Your first scene and reuses its Turntable system and GameRenderPlan.

Save the document

Save the scene from the last page as src/commonMain/resources/scenes/first.scene.json:

scenes/first.scene.json
{
  "version": 1,
  "name": "first-scene",
  "nodes": [
    {
      "name": "camera",
      "components": [
        {
          "component": "camera",
          "eye": { "x": 0.0, "y": 1.5, "z": 4.0 },
          "center": { "x": 0.0, "y": 0.5, "z": 0.0 },
          "fovYDegrees": 45.0
        }
      ]
    },
    {
      "name": "sun",
      "components": [
        {
          "component": "light",
          "type": "Directional",
          "direction": { "x": 0.4, "y": 0.8, "z": 0.4 }
        }
      ]
    },
    {
      "name": "cube",
      "transform": { "position": { "x": 0.0, "y": 0.5, "z": 0.0 } },
      "components": [
        { "component": "mesh_renderer", "mesh": "cube", "material": "lit" },
        { "component": "spin_control", "speed": 1.0 }
      ]
    }
  ]
}

The document has a schema version and a list of nodes. Each node becomes one entity: its name, an optional transform, and components, each tagged with its component id.

Load it

Load the document in onReady and switch the scene to it:

commonMain/kotlin/Game.kt
import com.awakekt.awake.asset.shaderpack.LitShadowUniformLayout
import com.awakekt.awake.core.geometry.generate.generate
import com.awakekt.awake.engine.bootstrap.dsl.app
import com.awakekt.awake.engine.platform.lifecycle.AwakeAppLifecycle
import com.awakekt.awake.render.renderer.createMaterial
import com.awakekt.awake.scene.authoring.scene
import com.awakekt.awake.scene.document.SceneLoader
import com.awakekt.awake.scene.runtime.attachRenderableComponents

fun firstDocumentScene(): AwakeAppLifecycle = app {
    window {
        title = "Hello AwakeKt"
        size(1280, 720)
    }
    scene("first-scene") {
        assets {
            mesh("cube") { renderer.createMesh(generate { cube(size = 1f, colored = true) }) }
            material("lit") { renderer.createMaterial(LitShadowUniformLayout) }
        }
        frameSystem("turntable") { Turntable() }
        onReady {
            val document = SceneLoader.loadFromResource("scenes/first.scene.json")
            val scene = sceneManager.switchTo(document)
            // Each mesh_renderer names a mesh and a material; build them from assets { }.
            scene.attachRenderableComponents { request -> requireAssetLibrary().resolve(this, request) }
        }
    }
}

Launch it from desktopMain as before:

desktopMain/kotlin/Main.kt
fun main() = runVulkanDesktopGame(firstDocumentScene(), GameRenderPlan)

Run it: the window shows the same spinning cube as the scene DSL version, frame for frame. Here is what onReady did:

  1. SceneLoader.loadFromResource reads and decodes the file. On desktop it reads from the classpath; in the browser it fetches the path relative to the page.
  2. sceneManager.switchTo checks the document, then creates one entity per node and one ECS component per component id. It tears down whatever scene it loaded before.
  3. A mesh_renderer only names a mesh and a material. attachRenderableComponents looks each name up in assets { } and adds the MeshRenderer.

The assets { } names are the link between the two: the document says "mesh": "cube" and "material": "lit", and the app declares both.

How it works

The scene document and the scene DSL are two ways to write the same ECS content. Loading a document does not start a second engine: the entities land in the scene runtime's World, and the same Turntable, TransformSystem, and RenderSystem3D run over them.

If you already hold the document when you build the app, scene(document) inside scene { } loads it and resolves its meshes in one call. To decode a string you already have, use SceneLoader.decode(text).

Documents are checked before they load

switchTo rejects a document that breaks a rule, such as a camera whose far is not greater than its near, with a SceneValidationException listing every problem and the node it is on. A document whose version is newer than this engine supports throws SceneSchemaVersionException.

Older component ids

Documents written with camelCase ids (meshRenderer, spinControl, pbrMaterial, prefabLink) still load: SceneLoader rewrites them to the snake_case ids. Write new documents with snake_case ids.

See also