- The scenegraph
Everything I've read about the subject has led me to believe that spatial ordering and other optimization techniques don't belong in a scene graph. Currently I have a very limited SceneGraph class that consists of GroupNode and GeometryNode objects only (both are derived from SceneNode). GroupNodes can have children, GeometryNodes can't have children, so all geometry nodes are leaves of the tree.
This works exactly the way I thought a scene graph is supposed to work (I can add parent-child relations between objects in a scene, and transformations in each node are relative to the parent node). Though many threads I've read about scene graphs add more types of nodes, I probably won't be doing that in the near future because I want things to remain as simple as possible until I've grown more comfortable with the design.
So far so good, but my understanding starts lacking when it comes to passing objects from the scene graph to the Renderer. This has to do with the way in which GeometryNodes are defined in my engine:
- GeometryNode The only thing that makes a GeometryNode different from its parent, SceneNode, is that it contains a BaseGeometry object. The BaseGeometry class is the parent class for all geometry in the engine. So you can make a BoxGeometry object, for example, that inherits from BaseGeometry and which defines the geometry for a box. I'm also working on classes like 'CaligariGeometry', or '3DSMaxGeometry' that will load geometry from disk. Similarly, I imagine there would be TerrainGeometry objects. Because all these Geometry classes inherit from BaseGeometry, they can all be passed to GeometryNodes in the scene graph. The scene graph doesn't really care what the geometry represents, it only knows about GeometryNodes, but what they contain is not of the scene graph's concern.
- BaseGeometry BaseGeometry, as said, contains geometry data. I currently distinguish between many types of buffers. Think IndexBuffer, VertexBuffer, NormalBuffer, TexcoordBuffer etc. The reason why I did this is because I'm using the OpenGL buffer objects extension, and the framerate was higher when I used this system of many buffers for one object, rather than one large buffer with Vertex objects that contain everything from vertex position data to texture coordinates. I'm quite sure my tests are to be taken too seriously as I've never used any substantial number of vertices. Thus far all I'm rendering is a 100x100 grid with a couple of boxes on it, because I cannot load arbitrary 3D file formats yet that would allow me to more easily load more data.
Engine design questions
Thus far I've found the gamedev forums, and this forum in particular, to provide very useful information on everything that has to do with game engines. I've read many 'classic' threads where Yann L. explains everything from shadow mapping to scene graph implementations, and they've proven very helpful.
However, in the process of working on my own rendering, I'm finding it difficult to ty all these theories and 'best designs' together in a coherent design that makes sense for me. I'll list the issues I'm running into and my thoughts about them one by one below...
Also note that I'm using OpenGL, and though the renderer is designed to easily allow support for other libraries such as Direct3D I see no need to this yet, so I may sometimes refer to OpenGL specific things.
I'm gonna do some shameless advertising first ;)
I've written a series of articles about engine architecture, intended for beginners here :
http://www.beyond3d.com/content/articles/98 (Part 1)
http://www.beyond3d.com/content/articles/102/ (Part 2)
(More written, but not published yet :( )
You might find something useful in there.
I tend to rename things, giving good names is difficult, but I find important to name things as accurately as possible in order to be understood easily.
So in my engine design (not necessarily the best, it's just one solution to the problem, probably not the worst at least ;) ), I have a SceneTree (a Tree is a Graph, but everyone gets what a tree is w/o any further specifications), a SpatialGraph (Directed Acyclic Graph), and I used to have a RenderGraph, but I replaced it by a kind of RenderQueue.
The SceneTree is solely used for hierarchical animation. (Be it skeletal animation or a sword held in a character's hand.) This node type has an update(...) function that allows animation, and the nodes make a tree (a single parent, any number of children).
The SpatialGraph is used for culling (in fact finding what's visible). It's its only purpose in life to make culling fast.
The RenderQueue, is filled during culling in the spatialgraph. It's not a single array though, it's a little more complex than that. I did write something directly inspired from Yann Lombard thread about his material system.
The only task of this RenderQueue is to render whatever has been found visible FAST. (ie means sorting sub arrays per key, each key holding data such as depth, shader ID...; whatever is appropriate.)
Examples:
Terrain
It has a single node in the SceneTree, that does nothing.
(Nothing happens based on time on this terrain.)
It's made of a number of nodes in that SpatialGraph, making a Quadtree, which root is the Terrain's SpatialNode, and Leaves would contain mesh or pointers to meshes.
Character
It has a number of nodes in the SceneTree, for Meshs and Bones, which both might be animated hierarchicaly. It might also have something attached to his hand(s), that would need hierarchical relationship.
It has one SpatialLeaf node per mesh.
I call Geometry a Mesh instead.
I might be a bit synthetic, ask if I'm not clear enough.
[Edited by - Ingenu on November 20, 2008 5:14:29 AM]
I've written a series of articles about engine architecture, intended for beginners here :
http://www.beyond3d.com/content/articles/98 (Part 1)
http://www.beyond3d.com/content/articles/102/ (Part 2)
(More written, but not published yet :( )
You might find something useful in there.
I tend to rename things, giving good names is difficult, but I find important to name things as accurately as possible in order to be understood easily.
So in my engine design (not necessarily the best, it's just one solution to the problem, probably not the worst at least ;) ), I have a SceneTree (a Tree is a Graph, but everyone gets what a tree is w/o any further specifications), a SpatialGraph (Directed Acyclic Graph), and I used to have a RenderGraph, but I replaced it by a kind of RenderQueue.
The SceneTree is solely used for hierarchical animation. (Be it skeletal animation or a sword held in a character's hand.) This node type has an update(...) function that allows animation, and the nodes make a tree (a single parent, any number of children).
The SpatialGraph is used for culling (in fact finding what's visible). It's its only purpose in life to make culling fast.
The RenderQueue, is filled during culling in the spatialgraph. It's not a single array though, it's a little more complex than that. I did write something directly inspired from Yann Lombard thread about his material system.
The only task of this RenderQueue is to render whatever has been found visible FAST. (ie means sorting sub arrays per key, each key holding data such as depth, shader ID...; whatever is appropriate.)
Examples:
Terrain
It has a single node in the SceneTree, that does nothing.
(Nothing happens based on time on this terrain.)
It's made of a number of nodes in that SpatialGraph, making a Quadtree, which root is the Terrain's SpatialNode, and Leaves would contain mesh or pointers to meshes.
Character
It has a number of nodes in the SceneTree, for Meshs and Bones, which both might be animated hierarchicaly. It might also have something attached to his hand(s), that would need hierarchical relationship.
It has one SpatialLeaf node per mesh.
I call Geometry a Mesh instead.
I might be a bit synthetic, ask if I'm not clear enough.
[Edited by - Ingenu on November 20, 2008 5:14:29 AM]
I very much like the SceneTree, SpatialGraph and RenderView nomenclature. It says what it does and this is a huge advantage over the word SceneGraph. I will use Ingenu terms from now on.
If all of the boxes are the same, then why would you need so many vertex buffers? Can't you reuse them?
It's very hard to think of some kind of general 3D engine architecture which can handle everything in a nice object-oriented way and is also extremely fast and efficient. If something is for everything - it's for nothing.
Why not to keep all subsystems separately? Terrain could store all its nodes in quad-tree structure, characters could have its own hierarchy tree, and world geometry might be handled/occluded by its own BSP/PVS, or oct-tree structure (depending on what we need).
All subsystems write renderable items into render manager (render queue), which perform sorting by depth, material ID, priority, or whatever we want.
Using this approach we can optimize every single subsystem without touching the others.
Why not to keep all subsystems separately? Terrain could store all its nodes in quad-tree structure, characters could have its own hierarchy tree, and world geometry might be handled/occluded by its own BSP/PVS, or oct-tree structure (depending on what we need).
All subsystems write renderable items into render manager (render queue), which perform sorting by depth, material ID, priority, or whatever we want.
Using this approach we can optimize every single subsystem without touching the others.
I suppose I'll throw my design into the discussion too, although it doesn't deal with the scene graph side of things as much as you are looking for:
Hieroglyph Design
You don't actually need to have many buffers to represent many objects. You can simply have a single buffer (or multiple large ones if you like) and each object indexes into that big buffer instead of each object having its own. Then you just need to centralize the access to the buffer, which can be coordinated through your renderer interface.
Hieroglyph Design
You don't actually need to have many buffers to represent many objects. You can simply have a single buffer (or multiple large ones if you like) and each object indexes into that big buffer instead of each object having its own. Then you just need to centralize the access to the buffer, which can be coordinated through your renderer interface.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement