4.6 KiB
Scene System: Cameras, DrawContext, and Instances
Thin scene layer that produces RenderObjects for the renderer. It gathers opaque/transparent surfaces, maintains the main camera, and exposes simple runtime instance APIs.
Components
-
SceneManager(src/scene/vk_scene.h/.cpp)- Owns the main
Camera,GPUSceneData, andDrawContext. - Loads GLTF scenes via
AssetManager/LoadedGLTFand creates dynamic mesh/GLTF instances. - Updates per‑frame transforms, camera, and
GPUSceneData(view/proj/viewproj, sun/ambient).
- Owns the main
-
DrawContext- Two lists:
OpaqueSurfacesandTransparentSurfacesofRenderObject. - Populated by scene graph traversal and dynamic instances each frame.
- Two lists:
-
RenderObject- Geometry:
indexBuffer,vertexBuffer(for RG tracking),vertexBufferAddress(device address used by shaders). - Material:
MaterialInstance* materialwith bound set and pipeline. - Transform and bounds for optional culling.
- Geometry:
Frame Flow
SceneManager::update_scene()clears the draw lists and rebuilds them by drawing all active scene/instance nodes.- Renderer consumes the lists:
- Geometry pass sorts opaque by material and index buffer to improve locality.
- Transparent pass sorts back‑to‑front against camera and blends to the HDR target.
- Uniforms: Passes allocate a small per‑frame UBO (
GPUSceneData) and bind it via a shared layout.
Sorting / Culling
- Opaque (geometry): stable sort by
materialthenindexBuffer(seesrc/render/vk_renderpass_geometry.cpp). - Transparent: sort by camera‑space depth far→near (see
src/render/vk_renderpass_transparent.cpp). - An example frustum test exists in
vk_renderpass_geometry.cpp(is_visible) and can be enabled to cull meshes.
Dynamic Instances
-
Mesh instances
addMeshInstance(name, mesh, transform),removeMeshInstance(name),clearMeshInstances().- Useful for spawning primitives or asset meshes at runtime.
-
GLTF instances
addGLTFInstance(name, LoadedGLTF, transform),removeGLTFInstance(name),clearGLTFInstances().
GLTF Animation / “Actions”
GLTF files can contain one or more animation clips (e.g. Idle, Walk, Run). The loader (LoadedGLTF) parses these into LoadedGLTF::Animation objects, and SceneManager exposes a thin API to pick which clip is currently playing.
Note: a
LoadedGLTFis typically shared by multiple instances. Changing the active animation on a sharedLoadedGLTFwill affect all instances that point to it. If you want per‑character independent actions, load separateLoadedGLTFobjects (one per character) or duplicate the asset in your game layer.
Static scenes (loaded via loadScene)
Example: engine default scene in VulkanEngine::init():
structureis loaded and registered via:sceneManager->loadScene("structure", structureFile);
To control its animation:
- By index:
scene->setSceneAnimation("structure", 0); // first clipscene->setSceneAnimation("structure", 1, true); // second clip, reset time
- By name (matches glTF animation name):
scene->setSceneAnimation("structure", "Idle");scene->setSceneAnimation("structure", "Run");
- Looping:
scene->setSceneAnimationLoop("structure", true); // enable loopscene->setSceneAnimationLoop("structure", false); // play once and stop at end
All functions return bool to indicate whether the scene name was found.
Runtime GLTF instances
GLTF instances are created via:
scene->addGLTFInstance("player", playerGltf, playerTransform);
You can treat each instance as an “actor” and drive its current action from your game state:
- By index:
scene->setGLTFInstanceAnimation("player", 0);
- By name:
scene->setGLTFInstanceAnimation("player", "Idle");scene->setGLTFInstanceAnimation("player", "Run");
- Looping:
scene->setGLTFInstanceAnimationLoop("player", true);
These helpers forward to the underlying LoadedGLTF’s setActiveAnimation(...) and animationLoop fields. SceneManager::update_scene() advances animations every frame using a per‑frame dt, so once you select an action, it will keep playing automatically until you change it or disable looping.
GPU Scene Data
GPUSceneDatacarries camera matrices and lighting constants for the frame.- Passes map and fill it into a per‑frame UBO, bindable with
DescriptorManager::gpuSceneDataLayout().
Tips
- Treat
DrawContextas immutable during rendering; build it fully inupdate_scene(). - Keep
RenderObjectsmall; use device addresses for vertex data to avoid per‑draw vertex buffer binds. - For custom sorting/culling, modify only the scene layer; render passes stay simple.