How to build a non-trivial scene graph structure?

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • How to build a non-trivial scene graph structure?

      I've spend hours on reading the source code of the TeapotWars looking for the process of building the scene graph structure, and here is what I could find out:
      1. The actor factory creates an actor along with a render component.
      2. The actor factory calls Actor::PostInit(), which calls BaseRenderComponent::VPostInit().
      3. BaseRenderComponent::VPostInit() triggers an event of EvtData_New_Render_Component.
      4. The Scene is triggered by the event. Then the Scene calls Scene::NewRenderComponentDelegate(...). (The Scene is the only object that is listening to EvtData_New_Render_Component, if I didn't miss anything.)
      5. In the delegate function, the SceneNode object (the kid) inside the render component is added to the RootNode object (the m_Root) inside the Scene as a child. That is, m_Root->VAddChild(kid) is called.
      (If there is anything wrong here, please tell me! :) )

      As I could find out, no SceneNode other than the RootNode has any child in TeapotWars. Is the scene graph structure sufficient for non-trivial games?
      For example, in a turn based strategy game like Fire Emblem or Super Robot Wars, we have to draw units on the grid map, and draw stat bars on each unit. It seems to be natural to build a scene graph structure in which the stat bar is a child node of a unit which is a child node of a map which is a child node of the RootNode.
      Is this structure good?
      - If yes, when, where and how should we call SceneNode::VAddChild(...) (or, how can we build the structure)?
      - If no, what is the better solution? :)
    • babygogogo wrote:

      I've spend hours on reading the source code of the TeapotWars looking for the process of building the scene graph structure, and here is what I could find out:
      1. The actor factory creates an actor along with a render component.
      2. The actor factory calls Actor::PostInit(), which calls BaseRenderComponent::VPostInit().
      3. BaseRenderComponent::VPostInit() triggers an event of EvtData_New_Render_Component.
      4. The Scene is triggered by the event. Then the Scene calls Scene::NewRenderComponentDelegate(...). (The Scene is the only object that is listening to EvtData_New_Render_Component, if I didn't miss anything.)
      5. In the delegate function, the SceneNode object (the kid) inside the render component is added to the RootNode object (the m_Root) inside the Scene as a child. That is, m_Root->VAddChild(kid) is called.
      (If there is anything wrong here, please tell me! :) )

      That sounds about right, yes.


      As I could find out, no SceneNode other than the RootNode has any child in TeapotWars. Is the scene graph structure sufficient for non-trivial games?

      No, not by a long shot. Most games, especially big games, use some kind of spatial partitioning, like BSP trees, quadtrees, octrees, etc. The idea is that you partition the scene graph into different chunks that you can easily detect whether or not you should be rendering. Right now, it's basically just a list of nodes to render. In a real game, you'd realize that you're in section 4 and jump straight to that child, rendering all the children underneath it. As dynamic actors move around, they also move around in the scene graph.


      For example, in a turn based strategy game like Fire Emblem or Super Robot Wars, we have to draw units on the grid map, and draw stat bars on each unit. It seems to be natural to build a scene graph structure in which the stat bar is a child node of a unit which is a child node of a map which is a child node of the RootNode.
      Is this structure good?
      - If yes, when, where and how should we call SceneNode::VAddChild(...) (or, how can we build the structure)?
      - If no, what is the better solution? :)

      Not sure about Super Robot Wars, but I'm big fan of Fire Emblem (I was literally just playing Shadow Dragon before responding to this post) so I'll speak to that. Fire Emblem is a 2D tile-based game where every unit is guaranteed to be on a tile

      I wouldn't actually use a scene node structure here, I'd use a tile map.With the 2D camera bounds, you can easily calculate which tiles are in view and can only render those tiles and the objects on them (with maybe a one tile buffer to catch units that are slightly taller or wider than the tiles). Each tile would basically have a list of objects on that tile. Most tile-based games I know of do something similar.

      That having been said, creating attachments to other objects is often handled using a scene node structure. If you are using a scene graph, this is a reasonable way to make it work since it will automatically move with the parent. But again, for Fire Emblem, I wouldn't do it exactly like that.

      The lesson here is to use the structure that makes sense for the project that you're building. Every game is different.

      -Rez
    • ...(I was literally just playing Shadow Dragon before responding to this post)...​

      I'm a big fan of Fire Emblem too! Actually I'm trying hard to write a simple tile-based strategy game! :D :D :D

      ​I wouldn't actually use a scene node structure here, I'd use a tile map.With the 2D camera bounds, you can easily calculate which tiles are in view and can only render those tiles and the objects on them (with maybe a one tile buffer to catch units that are slightly taller or wider than the tiles). Each tile would basically have a list of objects on that tile. Most tile-based games I know of do something similar.

      I've considered something like this but I couldn't figure it out...There are so many questions and I don't know what's the better way to deal with them. :S

      Is there any good resource on how to code a tile-based game? I want to learn something more so that I won't flood this forum with my questions. Thank you Rez! :D
    • George wrote:

      babygogogo, Are you trying to understand, how component structure of the hierarchical objects should be organized in that case, am I right? I mean, how RenderableComponent handles that.

      Yes, you are right. :)
      My English is not good so that I couldn't express my thought precisely... By the original question, I want to know that if I have two SceneNode, namely A and B, and B should be a child node of A, A is a child node of the RootNode, then when and where should I call A->VAddChild(B)?

    • I've considered something like this but I couldn't figure it out...There are so many questions and I don't know what's the better way to deal with them. :S

      Is there any good resource on how to code a tile-based game? I want to learn something more so that I won't flood this forum with my questions. Thank you Rez! :D

      I'm sure there are, but I don't really know any. Most of the tutorials I've seen are really simple. The basic idea is that you have an array of tile objects where each tile has a pointer to an actor. This pointer is nullptr if nothing is attached. It could also be a list of actors if you support multiple actors on the same tile (Fire Emblem: Shadow Dragon doesn't, but I think Awakenings does).

      It might look like this:

      Source Code

      1. // This class represents the tile data that never changes for a tile type.
      2. class TileData
      3. {
      4. // This is the image for the tile. If you're using a GCC-style resource cache, this would
      5. // be a resource handle.
      6. Texture* m_pTexture;
      7. // These are the flags for this tile. In Fire Emblem, every tile has data for who can
      8. // walk on a tile, what the movement penalty is, any defensive bonuses the tile gives,
      9. // and other special properties of the tile. One classic way to model this is with bit
      10. // flags, which I have below. You could just as easily use real variables for each one,
      11. // which is probably what Fire Emblem does.
      12. unsigned long m_flags;
      13. public:
      14. // Add some public functions here. These are probably mostly getters.
      15. };
      16. // This class represents a single instance of a tile. All dynamic data (stuff that can change
      17. // during gameplay) lives here.
      18. class TileInstance
      19. {
      20. // This is a smart ptr that points to the actor on this tile. If no actor is
      21. // here, it's set to nullptr. This could easily be a list if you support multiple
      22. // actors.
      23. ActorPtr m_pActor;
      24. // Pointer to the static tile data for this tile.
      25. TileData* m_pTileData;
      26. public:
      27. // Add some public functions here. These are interface functions for things like
      28. // rendering, getting the data, etc.
      29. };
      Display All


      Then you have a world class that manages the tiles.

      Source Code

      1. class World
      2. {
      3. // The width and height of the world, in tiles.
      4. int m_width, m_height;
      5. // The tile map itself. This holds a number of tiles equal to m_width * m_height.
      6. std::vector<TileInstance*> m_pTileMap;
      7. public:
      8. // There are a bunch of functions here for rendering and so on.
      9. };
      Display All


      So, how do you know what tiles to render? The answer is surprisingly simple. Here's a copy & paste of my rendering function for my simple tile world:

      Source Code

      1. void SimpleTileWorld::AddVisibleTilesToScene(void)
      2. {
      3. _CHECK(m_pScene);
      4. Rect viewport(m_pScene->GetCameraViewport());
      5. // Snap the viewport position to the upper left of the current tile. This is to make the tile position math
      6. // easier to deal with so we render in the right place.
      7. int dx = (-(viewport.left % m_tileWidth));
      8. int dy = (-(viewport.top % m_tileHeight));
      9. viewport.MoveDelta(dx, dy);
      10. viewport.bottom += m_tileHeight; // need to draw an extra row at the bottom
      11. // Attach a render command for each tile. Note that we render an additional row & column. This is because
      12. // we're probably not perfectly aligned on the tile grid.
      13. for (int y = viewport.top; y <= viewport.bottom; y += m_tileHeight)
      14. {
      15. for (int x = viewport.left; x <= viewport.right; x += m_tileWidth)
      16. {
      17. TileData* pTile = GetTileAtPoint(x, y);
      18. if (pTile)
      19. {
      20. // create the object
      21. SpriteRenderCommand* pRenderCommand = new SpriteRenderCommand;
      22. // set the transform
      23. pRenderCommand->SetX((float)x);
      24. pRenderCommand->SetY((float)y);
      25. pRenderCommand->SetWidth((float)m_tileWidth);
      26. pRenderCommand->SetHeight((float)m_tileHeight);
      27. // set the texture data
      28. TextureResource* pTexture = static_cast<TextureResource*>(pTile->GetTextureResource());
      29. if (pTexture)
      30. {
      31. pRenderCommand->SetTextureId(pTexture->GetId());
      32. pRenderCommand->SetTexture(pTexture->GetTexture());
      33. }
      34. // render stage
      35. pRenderCommand->SetRenderStage(m_renderStage);
      36. // add to the scene
      37. m_pScene->AddObjectToScene(pRenderCommand);
      38. }
      39. }
      40. }
      41. }
      Display All


      First, I grab the camera viewport from the scene, which gives me a rectangle describing the camera bounds. The next block snaps the camera to the tile boundary so it's easier to deal with. The next block is the set of for loops that does the actual rendering. Notice how I loop through the tiles? I'm looping through only the set of tiles that can possibly be on screen.

      Inside the for loops, I build up a render command based on the tile data and send it over to the scene. That's it.

      babygogogo wrote:

      George wrote:

      babygogogo, Are you trying to understand, how component structure of the hierarchical objects should be organized in that case, am I right? I mean, how RenderableComponent handles that.

      Yes, you are right. :)
      My English is not good so that I couldn't express my thought precisely... By the original question, I want to know that if I have two SceneNode, namely A and B, and B should be a child node of A, A is a child node of the RootNode, then when and where should I call A->VAddChild(B)?

      It depends. If you're going with the GCC model, that connection would happen in the editor. When you read in the XML for the parent actor, it would see that there's an attachment and it would load that actor as well, attaching them to the parent. GCC doesn't support attachments as it stands, so you'd need to add this functionality.

      -Rez