My scene management failed

Started by
7 comments, last by Pilpel 8 years, 7 months ago

I read some articles on the subject but I'm still struggling with this.

My system contains the following classes:

Mesh - created only once per file. has a hierarchy of MeshNode's and RenderMeshData's attached to them. It also contains AnimationData (also called AnimationSet) objects, one per animation (i.e "walk", "shoot" etc.)

SceneManager - contains a cache of meshes, and a SceneGraph

SceneGraph - basically contains just a single, "root" SceneNode

SceneNode - a node with a transformation, childs, and a RenderMeshData object (contains vertices, uvs, normals etc) that can be attached to it.

SceneManager has a method for attaching (which is more like copying) a Mesh object to a SceneNode. It copies the Mesh'es MeshNode hierarchy to SceneNode, setting it as the root node of the Mesh'es hierarchy.

This worked fine for static objects until I wanted to implement skeletal animation which I successfully implemented before, in a less adaptive and more hackish system. (it was very basic, no animation blending and "animation adding" that I now want)

I know I should have this AnimationController class to handle this, but I'm not sure what exactly it needs to handle and has permissions to access.

At start I thought I can get rid of a "Skeleton" class, which I had in the previous code, but now I'm not sure if I can.

I'm also not sure on how to set an index for each bone (or SceneNode?) for the vertex shader to use.

Any help will be much appreciated!

Advertisement

There a tremendous amount of interpretation of the words "mesh", "node", "scene graph", so there is no consistency at all. The way I defined and the way I understand and express thing in my engine is this:

A mesh - is a single thing that can be rendered by only one draw call. Aka it has a only one vertex buffer and (optionally) one index buffer(and their descriptions ofc). Also a material could be attached, and skinning data.

A node - is a thing that has "parameters" and "attachment" and child nodes

- "parameters" are all the values needed to express the node(transform for example).

- "attachments" are all the things that are located (and have logical connection) with the node. Meshes, lights stuff like this.

A material - is just a set of parameters.

A scene - is a just a container that has a pointer to the root node(and in my case manages the data needed by the scene) (I Call this a "Model" think it is a bit more correct).

That is all.

Things with a bit more depth.

The node parameters aren't just a vec3f/float/int/matrix/string/ect.. Well for the static nodes they could be, but I really wanted to have one interface for static and animated nodes.

Lets say for example that I have a node that has animated translation with key frames (0sec, x=5) (5sec, x=15)and at the binding moment(the transform when there is no animation, or the binding moment needed for skinning) I call this "static moment" (x=0) for animation called "Jump"

So the node will have the parameter translation looking like this:

NodeParameters :

Translation:

StaticMoment x = 0

Animations:

"Jump":

(t=0, x=5), (t=5, x = 15)

When I need to resolve the scene to be at some moment(for example 0.5second of the "Jump" animation). I have a function that basically goes trough all nodes and call a function called "Evaluate" that is available for every parameter. What this function does is just to interpolate the keyframes and sore the result somewhere for later use.

If you are still wandering: "yes, the animations are actually stored as parameters in the nodes".

A little not for skinning : "Bones" are actually just "nodes". So my SkinningData is just a set of Nodes and a list of vertices influenced by every node.

I do not support "morph target" but you can easily added by adding "parameters" to the mesh(well this is not the best solution, but it is at the top of my head right now).

Also in order to be able to optimize things a little I have a thing that is called MeshBufferData, for scenes that there is no skinning. I store one huuuge MeshBuffer that that hold all the vertices and indices in 2 vertex buffers. And then the "meshes" actually refer to the data stored into the MeshBufferData. BUT this is kind of optional (well it will help you with the performance but it is "postpone"-able.).

Hope that will help.

Thanks for the comment.

At some point I will need to have an index set to every bone in order to feed the vertex shader. That's one of the problems I have.

I'm talking about this line:


uniform mat4 Bones[64];

How do I determine these indices? Could it be that your approach cancels the need for this bone matrix array?

The largest problem I see is that you are using your node tree as the representation of your scene.
A scene manager should have at the very least a linear array of all objects in it. The main list of objects. The array you traverse every frame to update each object.

As far as scene graphs go:
#1: They are secondary. You can store the objects in memory in multiple ways, but the main primary way is always the linear array of objects.
#2: The scene manager doesn’t know what a scene graph is. There is no reason for it to know what a scene graph is, as this knowledge in no way helps manage the scene.
http://lspiroengine.com/?p=566

Animations should also be their own separate class, especially considering they can be their own files and be shared between meshes with similar skeletons.


I am also guessing part of your problem comes from your lack of a MeshInstance class. You have no container for the meshes you copy into the scene. Mesh’es are shared data, MeshInstance’s are instances of that shared data inside the scene. You don’t seem to have anything in place to encapsulate them.


How do I determine these indices? Could it be that your approach cancels the need for this bone matrix array?

That’s a question for another topic.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

@L. Spiro

#1. I do have a cache of Mesh objects in SceneManager (see "MeshCache _meshCache;"). The scene nodes don't store MeshData objects, but point to them.

#2. I basically have a SceneGraph object stored in SceneManager so that the user is able to get the pointer to that SceneGraph objects via sceneManager->getSceneGraphPtr(). Is that still wrong?

How do you handle animated models in your engine? What kind of files do you have? I guess I can create my own file formats for animated meshes (barbarian.mesh, barbarian.skel, barbarian.animdata) but is it really needed?

p.s do you have a good article on scene management? I also read your article a long time ago btw tongue.png


#1. I do have a cache of Mesh objects in SceneManager (see "MeshCache _meshCache;"). The scene nodes don't store MeshData objects, but point to them.

Meshes (i.e. the shared part) are resources. Caching them is a task of resource management. Scene management, on the other hand, is responsible for all the entities that are currently in the scene. That are 2 distinct things.


#2. I basically have a SceneGraph object stored in SceneManager so that the user is able to get the pointer to that SceneGraph objects via sceneManager->getSceneGraphPtr(). Is that still wrong?

If I remember L. Spiro's usage of terms correctly, then the scene management deals with the existence of entities in the scene, while a scene graph propagates properties. That again are distinct concerns, and in this sense having a scene manager handling a scene graph would be wrong.


How do you handle animated models in your engine? [...]

That's the way I'm handling this (L. Spiro does it probably in another way)...

When a game object becomes part of the scene at last step during instantiation, it is represented by a couple of objects. The objects store their own necessary parameters (i.e. those that are unique for the instance) and usually also refer to commonly used resources. It is allowed for clients to overwrite references. Other clients are not interested in how the object is build as long as it provides the parameters the client is interested in.

An animation clip is a resource; it can be used by more than a single game objects. To be actually used, a game object needs an animation runtime object (similar to the MeshInstance mentioned above). The runtime object stores the current state of animation of that particular game object, while it refers to one or animation clips to have access to the common animation definition data. The clue now is that when the animation sub-system is running during the progress of the game loop, it will alter parameters of some runtime objects (besides animation runtime objects). This may be a 3D skeleton pose, a sprite attachment, or whatever. Notice that a skeleton also has a runtime object besides a defining resource.

After running the animation sub-system, all animated game objects are again still for the remaining time until the game loop wraps around. A subsequent (CPU based) skinning process computes a new mesh. For the rendering sub-system there is no difference between animated and non-animated game objects, because the rendering just looks at belonging parameters and finds a mesh, a sprite, or whatever.


[…] What kind of files do you have? I guess I can create my own file formats for animated meshes (barbarian.mesh, barbarian.skel, barbarian.animdata) but is it really needed?

The file representation is detached from the in-memory representation, because the requirements are different. Okay, the file has to store data which later occur as resources. But whether they are stored in individual files or archive files, whether they are compressed or not, whether they grouped into load bundles, … is a question of the resource loading sub-system which itself is a lower level part of the resource management system.

It is not necessary to create an own file format as long as you are well-pleased with an existing one. As soon as you want to gain some loading performance by using in-place loading, support load bundles, support streaming, be interested in a unified resource loader, obfuscating your resources, … you probably need to define your own format, or look out for useable file formats specifically made for game content.

Can I ask how exactly should I manage the scene? I want to know everything starting from a simple Node design to the render_frame() function and how it uses the data structures I made.

If the SceneManager needn't know what a scene graph is, what does it need to know? It seems like I lack all the basic concepts.

I read Game Engine Architecture (all the important stuff) and I still can't grasp this. I also couldn't find a single article in Google that explains all these problems

((EDIT: Damned editor is eating large portions of my post when I embed citations. So here we go without…))
You can ask ;) but any and all answers you get will not unburden you to make your own decisions and learn from what goes well and what goes wrong. There is no single right way. This is because a game is inherently complex. FWIW, I usually follow these guidelines:
1. Solve problems the top-down way; consider how the problem is embedded in the entirety; divide a problem recursively until getting eatable pieces.
2. A unit of software should have 1 concern or should be responsible for 1 thing (how blurry that concern ever is ;) ); if it had more than 1, then bullet point 1 isn't went along far enough.
3. Bullet points 1 and 2 leads naturally to the use of interacting sub-systems; build this as hierarchy; higher level system use lower level systems and work together with same level systems; lower level systems should not use higher level systems; a system should not use another directly that is more than 1 level below.
4. Use the data-driven approach where appropriate.
5. When OOP-ing, do use inheritance where appropriate but prefer composition over inheritance.
Examples:
The game loop rules the coarse order in which things happen. You've seen in the "Game Engine Architecture" book (that of Jason Gregory, right?) that a specific order of animation steps on the level of the game loop helps to solve problems of dependencies. This is also true for other sub-systems. Blindly removing a game object without the
knowledge whether another sub-system is still working with it would be disastrous. Also not good would be to instantiate a new game object at a point in time where AI, animation, or collision detection already have been done. Hence having a defined point in the game loop, e.g. just after input gathering, where all game object addition and removal w.r.t. the scene happens, means that game objects do neither pop up nor disappear at the wrong moment. (So we have considered the environment in which spawning and removal happens, and have seen that it is beneficial to make it synchronously.) Well, such deferred addition and removal requires that (a) we use a kind of job object and (b) we have a sub-system where the jobs can be send to. Here the scene management comes into play. Because the concern of the scene management is to manage all game objects that live in the scene, it is the sub-system that can process said jobs. Furthermore, it shows that the scene management has an own point of time for updating within the game loop.
Now that we have introduced the scene management, should we put the scene graph into it? The scene graph has the purpose of propagating properties. This is a different concern than the existence of game objects, so no, it should not be part of the scene management as defined above. A scene graph is another structure used for another purpose. Similarly, say, an octree used for collision detection is an own structure, as well as a render job queue is, and so on. What structure does a scene manager then needs? It depends on the API. Until now we have said that game objects should be added and removed. We can use a handle concept and IDs for naming game objects, then 2 arrays would be sufficient to hold the indirections and the game objects themselves. This would also be sufficient to serve game object retrieval requests.
Resource management is another good example, because it occurs so often. What is the concern of resource management? The supply of resources for the game. Fact is that resource live persistently on a mass storage, what means that we are working with 2 copies of resources (the other one in RAM). Well, 2 copies are enough; we don't want more. The resource management should hide all these details. So, since we have used a bit fuzzy description of the concern of resource management, we identified 2 tasks belonging to it: resource caching and resource loading. Since these are 2 lower level concerns of resource management, we should implement resource management separated as a front-end that defines the API for clients, and a back-end by 2 delegate objects, one for caching and one for loading. The front-end manager then uses the back-end objects to fulfill the API. This can be done further down, e.g. the loader may use a file format wrapper to do the actual loading).
Well, all the above is somewhat general; it gives no explicit answers to your questions. Feel free to ask more questions, but remember that specific answers need specific questions. smile.png

Thanks for the answer. I got a few more questions:

1. The purpose of the scene graph is to propagate properties. Isn't that already what happens when working with SceneNodes? I mean, do I really need an object called SceneGraph? Or is it just an abstract explanation of what should happen with SceneNode objects? Btw, it seems like Ogre doesn't have a SceneGraph object at all. (just SceneManager and SceneNode)

2. If SceneManager needn't "see" the scene graph, where do I store it? In Ogre, like I said, there is no SceneGraph object, and you can get the SceneNode root via sceneManager.getRootSceneNode(). Isn't that the opposite of what you said before?

3. I created an abstract cache class (here), and that way, when I want to have a cache for anything (Meshes, Cameras, ShaderPrograms) I just inherit from this class and write a load() function. Here's an example of a cache of Texture2D objects. This works great for me. Now my question is where do I store all these caches? Is it okay that I store meshCache in SceneManager?

This topic is closed to new replies.

Advertisement