Lysa  0.0
Lysa 3D Engine
Scene tree

SceneTree.ixx and SceneTree.cpp

SceneTree wraps lysa::Scene and exposes virtual callbacks that the Application event loop calls each tick. Concrete scenes such as RotatingAssetScene derive from it and override only the callbacks they need.

Note
The Lysa Nodes library provides a Node-based scene graph including a complete SceneTree.

lysa::Scene : what it manages

lysa::Scene is the engine's GPU-facing scene container. It owns:

  • A flat set of registered lysa::MeshInstance objects.
  • Per-frame SceneFrameData buffers (one per frame in flight) that store instance transforms, AABBs, and draw-call data in GPU-accessible memory.
  • A list of lysa::Light objects and their shadow-map resources.
  • A lysa::Environment ambient term.

The renderer reads from the current frame's SceneFrameData to build the G-buffer and lighting passes; it never touches your SceneInstance tree directly.

lysa::SceneConfiguration

SceneTree constructs its base lysa::Scene with a default lysa::SceneConfiguration. For larger scenes you can tune the limits before construction:

lysa::SceneConfiguration sceneConfiguration {
.maxLights = 20,
.maxMeshInstances = 10000,
.maxPipelines = 50,
};
SceneTree::SceneTree(Camera& camera)
: Scene(sceneConfiguration), camera(camera) {}
Field Default Meaning
maxLights 10 Maximum lights active in the scene at one time
maxMeshInstances 5000 GPU buffer capacity for instance data
maxPipelines 25 Maximum distinct graphic pipeline objects
asyncObjectUpdatesPerFrame 50 Instances updated per frame in async mode

Interface

class SceneTree : public lysa::Scene {
public:
explicit SceneTree(Camera& camera);
virtual void onPhysicsProcess(double delta) {}
virtual void onProcess(double alpha);
virtual void onInput(const lysa::InputEvent& event) {}
virtual void onResize(const vireo::Extent& extent) {}
protected:
Camera& camera;
SceneInstance root{"Root"};
void addInstance(SceneInstance& instance);
void updateInstance(SceneInstance& instance);
};

root is the single top-level SceneInstance. All loaded subtrees and custom nodes are attached to it as children.

Registering instances with the renderer

addInstance performs the first registration of a subtree. It walks the tree recursively and calls lysa::Scene::addInstance on every SceneMeshInstance it encounters:

void SceneTree::addInstance(SceneInstance& instance) {
if (auto* mi = dynamic_cast<SceneMeshInstance*>(&instance)) {
Scene::addInstance(mi->meshInstance);
}
for (const auto& child : instance.children) addInstance(*child);
}

lysa::Scene::addInstance allocates a slot in the GPU instance buffer and marks the instance as participating in culling and rendering. An instance that has never been passed to addInstance is invisible to the renderer regardless of its visible flag.

updateInstance is called every frame. It calls lysa::Scene::updateInstance for already-registered mesh instances, or addInstance for instances that were added dynamically after the initial registration:

void SceneTree::updateInstance(SceneInstance& instance) {
if (auto* mi = dynamic_cast<SceneMeshInstance*>(&instance)) {
if (haveInstance(mi->meshInstance)) {
Scene::updateInstance(mi->meshInstance);
} else {
Scene::addInstance(mi->meshInstance);
}
}
for (const auto& child : instance.children) updateInstance(*child);
}

lysa::Scene::updateInstance copies the latest transform, AABB, visibility, and shadow-cast flag from the lysa::MeshInstance into the GPU per-frame buffer. It is inexpensive when the instance is not dirty (only a flag check), so calling it every frame for all instances is safe.

Process tick

The base onProcess implementation drives the two-step renderer update: it first recomputes all global transforms by calling root.update(), then pushes the results to the GPU by calling updateInstance(root):

void SceneTree::onProcess(const double /*alpha*/) {
root.update();
updateInstance(root);
}

Derived classes that override onProcess mutate the scene (e.g. by calling root.setTransform(…)) and then call SceneTree::onProcess to flush the changes to the renderer.

Removing instances

To remove a mesh instance from the scene at runtime call lysa::Scene::removeInstance. The instance slot is freed and the mesh stops being rendered at the start of the next frame. Remember also to detach the corresponding SceneInstance from the tree so it is no longer visited by updateInstance.

Next : Camera