I have put a pin in the Renderer for now, it’s in a good spot do that I feel.

There are plenty more fun things to dig into of course, but the engine architecture needs some TLC.

Somehow I’ve lasted this long with the imperative SceneGraph. That is, scenes being put together functionally line by line:

  • Create SceneGraph
  • Create Camera
  • Set Camera properties
  • Create a Transform
  • Set Transform properties
  • Create a Shape
  • Parent Shape to Transform
  • Create a Material
  • Set Material properties
  • Assign Material to Shape
  • etc. etc.

While a decent start and quick enough to get running, creating and switching between them got tiresome pretty quick. I mentioned that during my first foray into shadows, and even then I had been thinking about it for some time.

Being able to switch between multiple test setups will really help speed up development.

The Scene Description

Rather than providing step by step instructions on how to build a scene, it’d be much nicer if we could just provide a description of it instead…

{
    "name": "Scene",
    "entities": [
        {
            "name": "Camera",
            "components": {
                "transform": {
                    "position": [100, 45, 100],
                    "lookAt": [0, 25, 0]
                },
                "camera": {
                    "focalLength": 35
                }
            }
        },
        {
            "name": "My Object",
            "components": {
                "transform": {
                    "position": [0, 0, 0]
                },
                "mesh": {
                    "geometry":{ "type": "model", "src": "/path/to/model" }
                },
                "material": {
                    "name": "My Material",
                    "roughness": 0.25,
                }
            }
        }
    ]
}

OK, but why?

There are many reasons why this is better, but to name a few:

  • Declaring whats in the scene means we can handle what gets loaded and when
  • We can save the scene state and reload it (serialisation)
  • A scene description could be read/written by other applications
  • You can have multiple scenes or “levels” and switch between them

I don’t want to mislead you, this isn’t just better, it’s necessary. A game built on an imperative style scene graph and inheritance would not scale at all.

At the moment, my scene description is simple JSON though this will likely change at some point. If designed well however, even changing a scene description could have minimal impact. Just rejig a systems or two and you’re away laughing.

The World

At the core of the new scene structure (and the foundation of the forming Entity Component System (ECS)) is the World.

The World holds a reference to all entities as an incremental id and stores typed components keyed by that id. It also provides the methods to edit/query/destroy them.

Just to elaborate a tiny bit, when you want to add a new entity you call World.createEntity() and that gives you an id, for example 123.

If you then want to add a Transform component to that entity, you call World.addComponent(123, 'transform', {"position": [0, 0, 0], ...})

The SceneGraph has a similar pattern, sort of. When you create objects from a single place (World.addComponent(entitiy_id, component), rather than World.createEntitiy() then entity.addComponent(component) for example), it’s much easier/faster to track them.

The whole point here though is that you’re not supposed to do this yourself, that takes you back to imperative style scene management again. Instead the SceneLoader parses the SceneDescription and does this for you.

📌Editorial Note
After posting this I realised I shouldn’t have said that you’re not supposed to do this. You can do this, and very likely will. If you want to dynamically create entities at runtime for example.

The Render System

The RenderSystem queries the World each frame, it gathers lights, allocates shadow maps, builds render bundles, and produces a FrameSubmission and hands that to the Renderer to render.

Prior to the RenderSystem, the responsibility was split between the ResourceManager and the render passes. This never felt quite right, it was doing too much and I found myself getting lazy and adding more to it out of convenience.

Now the RenderSystem is in place there is no longer a need for the ResourceManager, it has been split into smaller more explicit classes. One of those classes is the Engine which I’ll get into in another post.

The Current State

  • h toggles the render options
  • ~ opens the terminal
  • Mouse look with left/right mouse buttons
  • w, a, s, d to move
  • Ctrl and Spacebar to fly vertical

The Change Log

Static Rectangle V.12
  • App layer now only handles browser concerns
  • Engine owns World, Systems, Renderer, assets, events
  • Renderer now owns GPUResourceManager, which holds bind groups and in/out resources for passes
  • ~Render passes properly utilise the async ReadyState pattern and createRenderPipelineAsync
  • RenderOptions now owned by Engine, passed through FrameSubmission
  • EventManager now owned by Engine, used for scene loading progress
  • Removed ResourceManager, render bundles are now prepared by the RenderSystem
  • RenderBundle carries entity references, renderer resolves bind groups at draw time after buffer updates
  • ~Homogenised RenderPass design across all passes
  • ~Fixed bug where light colours were stored inconsistently
  • ~Fixed a bug with blue noise bind group capturing stale fallback texture
  • ~Fixed a pipeline cache key collision from using layout count instead of layout identity

The End (Of This Post)

If you have been following a long, you’ll notice features have disappeared. You can no longer enable the light shapes or select objects to edit their properties. These are currently on hiatus until I finish splitting up the SceneGraph and convert the current scene nodes into their components for the ECS.