Creating a render manager

Started by
13 comments, last by lukesmith123 12 years, 2 months ago
Hi,

I'm trying to build a render manager which handles the creation and rendering of mesh objects.

The render manager receives the list of mesh objects to draw from the scene manager.

This list is a collection of model instance objects which store a pointer to the mesh as well as the matrices etc to position it.


The thing I'm confused about is that after the render manager creates the mesh objects, should it then give this list to the scene manager so that model instances can copy the pointer to the mesh that they describe?

or should the model instance objects not hold a pointer to the mesh but simply store some id to describe which mesh they want to use and then the render manager can then use this to render the correct mesh?

The second option seems like it would make rendering slower as the render manager would have to sort through the list of meshes to determine which mesh in the list the model instance objects id was describing.


I've also read somebody talk about having a separate object to load the objects but I couldn't see how this would work as the graphics device is needed to load content and only the render manager can know about the graphics device.


thanks so much if somebody can clear this up for me!
Advertisement
The render manager shouldn't create or load meshes, that should be done by your resource system.
Once the resource system has loaded the mesh, a renderable instance is created. This is the handle into the renderer engine, to let you manipulate the renderable object.

You create the renderable by giving it pointers to vertex data, and storing the number of triangles to draw, etc. The renderable will also store which kind of material to use for the draw and the world orientation of the object.

In parallel you create a game object, or other scene object that controls ownership of the renderable. A character object may hold a reference to his mesh renderable. A sword obj to a sword renderable. You can use the entity pattern here.

At that point the game objects simply use their renderable handle to manipulate the rendering. They can set its transform, its material properties through the renderable instance reference.

You will have lots of other resources that need to be managed and loaded only once, such as meshes, textures, shaders, materials. The interaction can be fairly complex and it would best to just start with an implementation that you can make work, analyse its weaknesses and refactor or rewrite it.
There was a discussion about renderer architectures a while ago which I recommend reading.
Thanks for your reply.

The thing that's confusing me is that the graphics device is needed to load a mesh and also to render it. as I'm working with DirectX LPD3DXMESH's

As the render manager is the only thing that knows about the graphics device how can the resource manager load the mesh?
You are mixing responsibilities because you are not properly assessing the relationships between components.
For example, it may very well be that the only system in your “engine” using matrices is your graphics module. That doesn’t mean matrices are part of the graphics library; even if no other system uses them, they are still part of the basic math module in your engine.

Likewise, a model has no place within the graphics module. While implementations and responsibilities between graphics modules may vary, a general theme is that the graphics module simply handles the commands needed to render things, and provides wrappers for textures, vertex buffers, index buffers, and shaders.

It only understands these basic components, and a few render states such as lighting on/off (which is then processed by the active shader) etc.


You consider that models are “what the graphics module draws”, so you plan to put them into the graphics module.
No, models are not what the graphics module draws. The graphics module draws whatever it is told to draw. It could be a model. It could be terrain. It could be a 2D sprite. It could be completely custom, done by the user.

Not only that, but you are forgetting that graphics are only a small part of models. What about collision boxes? Animations can be made faster using the GPU, but they are not a “graphics asset”. What about the bounding boxes that allow the model to be part of an octree?
Are you planning to incorporate all of this into your graphics module? If I link to your graphics module, am I also required to link to a collision-detection library or physics library?





As the render manager is the only thing that knows about the graphics device how can the resource manager load the mesh?

Why are these mutually exclusive?
If the render manager knows about the graphics device, it should probably provide an interface so that other modules can use that graphics device.
And that is basically its only job. Once it does anything more than this, including loading models, it has overstepped its responsibilities.

Additionally, the job of loading/managing models is not on the “resource manager”. Such a term is too broad in definition. Virtually anything can be a resource, and making a single manager to handle all of them is not a happy idea.

If you consider that the job of the rendering module is simply to provide an interface to the graphics hardware and draw whatever it is told to draw, then you can think of the model library as a way of telling the graphics module what to draw.
And your terrain library will tell it how to draw terrain.

Models and terrain both need to render. This does not make them part of the graphics module.
Models and terrain are both resources. This does not mean it is necessary to make some kind of mega resource manager.


The only “mega” in your engine should be the highest-level engine library, which knows what a renderer is, knows what models are, knows what terrain is, and knows how to connect all these parts together to make your game world.
Just in case that causes a bit of confusion, remember that some modules can connect to other modules on their own, without the need of the mega module making the connection for them. That is, the model library knows what the graphics library is regardless. The mega engine module doesn’t connect models to the renderer, but it manages models in the physical (virtual?) game world and allows them to interact with other components such as terrain.


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

<p>[quote name=&#39;lukesmith123&#39; timestamp=&#39;1327274248&#39; post=&#39;4905246&#39;]As the render manager is the only thing that knows about the graphics device how can the resource manager load the mesh? [/quote]I would use something like this:class AssetLoader</p>
<div>{</div>
<div>public:</div>
<div>template<class T> typename T::Type* Load( const char* name, T&amp; receiver )</div>
<div>{</div>
<div>char* data; uint size;</div>
<div>LoadFile( &amp;data, &amp;size );</div>
<div>receiver.OnLoaded( data, size );</div>
<div>}</div>
<div>};</div>
<div> </div>
<div>struct Mesh {};</div>
<div> </div>
<div>class MeshManager</div>
<div>{</div>
<div>public:</div>
<div>MeshManager(ID3DDevice9* d) : device(d) {}</div>
<div>typedef Mesh Type;</div>
<div>Mesh* Load( const char* name, AssetLoader&amp; loader )</div>
<div>{</div>
<div>return loader.Load( name, *this );</div>
<div>}</div>
<div>private:</div>
<div>friend class AssetLoader;</div>
<div>Mesh* OnLoaded( char* data, uint size )</div>
<div>{</div>
<div>return new Mesh;//...</div>
<div>}</div>
<div>ID3DDevice9* device;</div>
<div>};</div>
<div> </div>
<div>class GraphicsManager</div>
<div>{</div>
<div>public:</div>
<div>GraphicsManager(ID3DDevice9* d) : device(d), meshManager(d) {}</div>
<div>MeshManager meshManager;</div>
<div>private:</div>
<div>ID3DDevice9* device;</div>
<div>};
</div>
As the render manager is the only thing that knows about the graphics device how can the resource manager load the mesh?
I would use something like this:class AssetLoader
{
public:
template<class T> typename T::Type* Load( const char* name, T& receiver )
{
char* data; uint size;
LoadFile( &data, &size );
return receiver.OnDataLoaded( data, size );
}
};

struct Mesh {};

class MeshManager
{
public:
MeshManager(ID3DDevice9* d) : device(d) {}
typedef Mesh Type;
private:
friend class AssetLoader;
Mesh* OnDataLoaded( char* data, uint size )
{
return new Mesh;//...
}
ID3DDevice9* device;
};

class GraphicsManager
{
public:
GraphicsManager(ID3DDevice9* d) : device(d), meshManager(d) {}
MeshManager meshManager;
private:
ID3DDevice9* device;
};
[font=courier new,courier,monospace]Mesh* mesh = assetManager.Load( "foo", graphicsManager.meshManager );[/font]
The resource manager is responsible for ensuring there is only one instance of each resource loaded. (one version of grass01.bmp, one version of terrain.obj, etc). It has the responsibility of determining the file location on the target device, and retrieving that file from the storage medium.

The data is likely already stored in a format specifically for the target platform. For example, directx targets may store their textures in dx5 compression, rather than .tga. They may store the meshes data exactly as it will be copied to VRAM.

Once the resource system has found the resource to be loaded, it will retrieve from disk. At that point it will be passed off to the load caller in such a way that it may be directly read into VRAM, or it may be copied from a temporary file cache to VRAM. Your resource system may have a set of loaders, maybe one per resource type. In the case of the mesh and texture loaders they will be given a renderer handle which we expose the device if necessary.

Your Loaders can be specialized to act like a factory and to determine file path extensions or what ever other data you might need.



Resources.AddLoader( new Loader<Mesh>( RenderDevice ) );
Resources.AddLoader( new Loader<Texture>( RenderDevice ) );


struct RenderableObject
{
scoped_ptr<MeshInstance> mi;

void Configure( Scene& scene, const FilePath& path )
{
Mesh* m = scene.Resources.FetchResource<Mesh>( path );
mi = new MeshInstance( m );
scene.AddRenderable( mi );
}
void Move( const Matrix& m )
{
mi->Transform = m;
}
};

The resource manager is responsible for ensuring there is only one instance of each resource loaded. (one version of grass01.bmp, one version of terrain.obj, etc). It has the responsibility of determining the file location on the target device, and retrieving that file from the storage medium.

etc.

Here is the problem with a mega resource manager.

A single resource manager needs to know about every type of resource in your engine.
It knows about individual textures, and also high-level classes such as models.

That means if I am just a lowly sprite class and I gain access to the resource manager, suddenly I also know about terrain, models, etc. All these things I don’t need to know.


Want to prevent the duplicate loading of textures?
Make a texture manager.

Want to prevent the duplicate loading of models?
Make a model manager.

The model manager, implicitly in need of texture information, can know about the texture manager and request one-time instances of textures.
Each manager only knows about the bare minimum of classes it needs to operate correctly, and thus using these managers in later projects avoids unnecessary knowledge of unrelated classes in projects that want to use those managers.

My sprite class wants to know about textures and textures only. It doesn’t need to link to a mega resource manager that includes information about models and terrain.

Just don’t do it.


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

Great, thanks so much for taking the time to explain these things, its really hard to find any information on these kinds of things.

This topic is closed to new replies.

Advertisement