For the 3D rendering part, I know support full scene graphs (unidirectional: from parent to child, but not from child to parent).
Originally, I only used scene graphs for models (a subset of my world objects), since model formats like .OBJ contain hierarchies of models. In practice, my Model class contains a vector of SubModels (determined by the model file). This allows for instance the repositioning of furniture relative to the room.
On top of that, I know added to the base class WorldObject, a vector of other WorldObjects (determined by the user). This allows for instance the repositioning of lights relative to the camera (like a flashlight).
I do not want to gather the camera and lights from the scene graphs. Therefore, I store them in my scene itself.
In the game loop of my engine, I update my scripts -> update and propagate (dirty) transforms -> render (among other things).
The problem is that lights must be located in the scene and could be located as child of some other world object. Consequently, I attempt to update their transforms twice:
// 1. Traverse the scene graphs (root world objects) which can contain lights as childs.
m_world->ForEachModel([](WorldObject &world_object) {
world_object.UpdateTransform();
});
// 2. Traverse the camera (no transformations will be re-calculated).
m_camera->UpdateTransform();
// 3. Traverse the lights (no transformations will be re-calculated).
m_world->ForEachLight([](WorldObject &world_object) {
world_object.UpdateTransform();
});
I start with the scene graphs, since the updated ancestor transforms will be propagated as well. Then, I explicitly go over the lights and camera. In this order (1->2->3), transformations will not be re-calculated, since dirty bits will be toggled as opposed to (2->3->1) order. The latter order will first update the parent-to-object and object-to-parent transforms and afterwards possibly pass updated parent-to-world and world-to-parent transforms of the ancestor in the scene graph. So the cached object-to-world and world-to-object transforms will be updated twice.
With my order (1->2->3), I only incur the overhead of a virtual function call and and an is-dirty check. This of course does not drop my framerate, but it does not feel like a clean design.
Edit: I realize that I only use scene graphs on world objects for transforms not for anything else (i.e. rendering). For the latter I still use flat vectors. So instead of a design issue, I rather have a question: how and where do you handle relative movement? Scripting?