|
SleakEngine 1.0.0
C++23 multi-backend game engine
|
Sleak::CullingSystem (include/public/Culling/CullingSystem.hpp, ~500 lines of implementation in src/Culling/CullingSystem.cpp) is a CPU visibility system: view-frustum culling plus software occlusion culling against a low-resolution depth buffer rasterized from game-submitted occluder volumes. It is backend-agnostic and does no GPU work. It is a separate system from Sleak::Physics::DynamicAABBTree (see Physics and Spatial Partitioning); neither references the other anywhere in the engine.
| Type | Role |
|---|---|
| Sleak::CullingSystem | All-static visibility system; frame protocol, occluder buffer, visibility tests. |
| Sleak::CullingSystem::Stats | Per-frame counters: submitted, rasterized, tested, culled, rasterize time. |
| Sleak::ViewFrustum | Six planes extracted from a view-projection matrix; the frustum test itself. |
| Sleak::Math::AABB | The box type every submission and visibility test speaks in. |
The camera opens the frame, the game fills the depth buffer with solid occluders, and every visibility test after FinalizeOccluders reads that buffer.
Occluder submission (steps 2 and 3) is optional; IsVisible degrades to frustum-only culling if no occluders were submitted that frame. BeginFrame is called automatically by the engine's active camera (src/Scene/Camera.cpp, Camera::RecalculateViewMatrix builds VP = View * Projection, refreshes the static s_frustum through ViewFrustum::ExtractFromVP, then calls CullingSystem::BeginFrame(s_frustum, VP, Position)). MeshComponent (src/Scene/MeshComponent.cpp) calls CullingSystem::IsVisibleFrustumOnly on its owner's world bounds before submitting a draw, so frustum culling is already wired into the default component draw path; full occlusion culling (submitting occluder boxes/triangles) is left to the game.
IsVisible never falsely culls a box that is actually visible, given valid occluders (conservative test).
Adaptive occlusion skips rasterization for probeInterval frames once a rasterized frame culls nothing, then probes again; queries degrade to frustum-only while a skip is in effect. This keeps a mostly-static camera from paying the rasterization cost every frame while still catching new occlusion when the camera moves.
Occluder boxes and triangles submitted through SubmitOccluderBox/SubmitOccluderTriangles must represent fully solid volumes; an occluder placed over open space or a cave mouth would incorrectly hide geometry behind it.
Two more properties are worth knowing before you submit occluders. Both submission calls reject anything outside the frustum before it reaches the queue, so submitting the whole world costs a plane test per volume rather than a rasterization. FinalizeOccluders then sorts what survived by squared distance to the camera and rasterizes only the nearest MaxOccluders of them, which means a badly scoped submission loop loses the far occluders rather than the near ones.
Bounds accuracy matters as much as solidity. An occludee box larger than the geometry it stands for pokes past the real silhouette, every depth sample behind it passes, and the object is never culled while still paying the test. Keep submitted bounds at exact vertex extent.
| Question | File |
|---|---|
| The full frame protocol and rasterizer | src/Culling/CullingSystem.cpp |
| The public API and Stats layout | include/public/Culling/CullingSystem.hpp |
| Plane extraction and the frustum test | include/public/Camera/ViewFrustum.hpp |
| Where BeginFrame is called from | src/Scene/Camera.cpp |
| The default frustum-culled draw path | src/Scene/MeshComponent.cpp |