okinawaengine GitHub

Scene

OkScene is a container for a hierarchy of OkObjects. It stores only the root objects (those without a parent) and drives their per-frame step and draw, recursing into children. You add a scene to the engine through the scene handler (see Handlers).

OkScene methods

Method Purpose
OkScene(const std::string &name) Construct a named scene.
void addObject(OkObject *object) Add a root object to the scene.
void step(float dt) Step every object (called by the engine).
void draw() Draw every object (called by the engine).
void activate() / void deactivate() Toggle the scene's active state.
bool isActive() const Whether the scene is active.
bool isCurrent() const Whether the scene is the current one.
size_t getObjectCount() const Number of root objects.
const std::string &getName() const The scene name.

The scene owns its objects: deleting the scene deletes the objects it holds, so do not delete added objects yourself.

Example

OkScene *scene = new OkScene("MainScene");

OkSceneHandler *handler = OkCore::getSceneHandler();
handler->addScene(scene, "MainScene");
handler->setScene(0);

scene->addObject(myItem);

Draw order, and what a node may decide

A frame is drawn in two passes. Opaque geometry first, nearest to furthest — the depth buffer then rejects what is hidden, which is the cheapest defence against overdraw in a scene full of occluders. Blended geometry afterwards, furthest to nearest: what is seen through or adds light deliberately does not write depth, so anything opaque drawn later would pass the depth test and paint over it, and a surface seen through is mixed with what is already behind it, so the far one has to be down first.

Which pass an object draws in is its own answer, drawsInPass(), and it is asked per object. The scene walks the whole tree in each pass. Most objects draw in one of them, the one isBlended() names; an item whose ranges of faces have materials of both kinds draws in both, each time only the ranges that belong there, and OkObject::inBlendedPass() tells it which pass is asking. That matters as soon as objects have children: decided per root, a single glow inside a group would carry that group's walls and roofs into the late pass with it.

The order is refreshed every few frames rather than every frame — it only has to be roughly right, and sorting thousands of objects every frame costs more than it saves. It is an order of root objects: the blended pass walks the same list backwards, so two blended surfaces under one root are not sorted against each other.

Skipping a subtree

draw() and step() ask two questions before doing anything, and any object may answer them -- they are OkObject's, so every item, camera, avatar or group of your own already has them and answers yes by default:

bool shouldDraw() const override;      // me and everything under me
bool shouldStep(float dt) const override;

Answer no and neither the object nor its children are visited. That is the whole of it: an object that owns a part of the world -- a district, a room, a level -- answers once for everything inside it, instead of each of those things answering for itself.

There is no class in the engine for that owner, and none is needed: whatever you already use for it overrides the two methods.

They are questions and not flags on purpose. A flag has to be put back, and a flag left false is a piece of the world that has disappeared with nothing to say why.

Drawing a subtree on its own — an inspection view, a thumbnail — is draw(), which does both passes in the right order. A frame goes through drawPass() instead, one pass at a time, so that all the opaque geometry in the scene is down before any of the blended.