Renderers

Started by
5 comments, last by Kibble 19 years, 7 months ago
I got something in the back of my head and I wonder if the idea is any good. The structure I have is as follows: - Object (a space ship for example, or an asteroid or an explosion. Having coordinates and orientation) - An object has a resource that can be rendered How the resource should be rendered depends on the type of resource. For example a mesh is rendered differently than an explosion or a menu. My idea was to use an ID to identify the type of renderer that should be used. Then when in the render loop, find the right renderer (based on the ID) and call the render() function.

void renderloop()
{
  int i;
  for (i=0; i<objects.size(); i++)
  {
    if (objects->resource->getType() == RESOURCE_MESH_MD2)
      MD2Renderer->render(objects);
    else if (objects->resource->getType() == RESOURCE_MESH_MENU)
      MenuRenderer->render(objects);
  }
}

Or something along those lines. How about this? I'm worried about performance, but I can't directly think of any better way of doing this.
STOP THE PLANET!! I WANT TO GET OFF!!
Advertisement
Objects should know how to render themselves. Use a virtual draw() function. For example:

class Object{public:  virtual void draw() = 0;};class SpaceShip : public Object{public:  virtual void draw()  {    ...  }};class Player : public Object{public:  virtual void draw()  {    ...  }};void renderloop(){  for (blahblah)  {    objects->draw();  }}


This presents a much more maintanable solution. If you get lots of different types of objects, think about how nasty that if/else if block is going to get :)

Now, menus you may want to do a little differently... personally I wouldn't store them with the rest of the game objects, they don't seem to fit in that category.
Only the render class should know how to render objects. This way the rendering code is contained, If you should ever want to switch renderers, having rendering code centralized will make it much easier to do the conversion. Decentralized rendering code is a PITA to change if you decide to change renderers.
I have had thoughts about making a draw() function but I don't think it fits a correct Object Oriented model.
I find this hard to explain, but drawing something to the screen belongs to a rendering subsystem, not to an individual object.


About my implementation, the if/else blocks would be changed in an array of renderer subscriptions.
void addRenderer(Renderer* r, int type){  renderers[type] = r;}void renderloop(){  for (i=0; i<objects.size(); i++)  {    renderer* r = renderers[objects->resource->getType()];    if (r)      r->render(objects);  }}

Like that. At startup all renderers would register themselves. That's my idea at this moment. Thereby the number of renderers would be limitted to a mesh, particle engine and menu renderer (at this moment). I will not make a difference between ships or asteroids or players. They are in essence represented by a mesh (I use MD2). Like this:
class Object{};class ObjectWithMD2 : public Object{};class Ship : public ObjectWithMD2{};class Asteroid : public ObjectWithMD2{};class Player : public ObjectWithMD2{};




Lastly, what you say about the menu is exactly what is bothering me most. They don't fit in with other game objects because they are not in the actual game. Then yet what are they? They can be interacted with by the user, so somewhere they would belong to an input manager/handler. On the other hand they are drawn to the screen, which would make them a renderable object.
STOP THE PLANET!! I WANT TO GET OFF!!
Quote:Original post by aaron_ds
Only the render class should know how to render objects. This way the rendering code is contained, If you should ever want to switch renderers, having rendering code centralized will make it much easier to do the conversion. Decentralized rendering code is a PITA to change if you decide to change renderers.


Actually you can split the functionality, with high-level stuff in the objects and low-level stuff in the renderer. Something like (roughly):
void SpaceShip::draw(){  renderer->DrawMesh(engineMesh);  if (blowingUp)    renderer->DrawMesh(explosionMesh);}

Quote:Original post by Structural
I got something in the back of my head and I wonder if the idea is any good.
The structure I have is as follows:

- Object (a space ship for example, or an asteroid or an explosion. Having coordinates and orientation)
- An object has a resource that can be rendered

How the resource should be rendered depends on the type of resource. For example a mesh is rendered differently than an explosion or a menu.
My idea was to use an ID to identify the type of renderer that should be used. Then when in the render loop, find the right renderer (based on the ID) and call the render() function.

*** Source Snippet Removed ***

Or something along those lines. How about this? I'm worried about performance, but I can't directly think of any better way of doing this.

I almost agree with uavfun and aaron_ds.

Game objects shouldn't be rendering themselves. However, although the renderer should be doing rendering, no single class should be rendering every kind of renderable object.

You need a class which represents the object in the game world (Actor), and another class which knows how to render that object (Model).

Actor has subclasses for each kind of ship/asteroid/explosion/etc. These classes are provided by the game engine.

Model has subclasses for each kind of renderable -- e.g. SimpleMesh, SkeletalMesh, Sprite, Widget. These classes are provided by the graphics engine.
CoV
I did it a bit differently than all of the above. Why not just abstract almost everything that D3D/OpenGL can do that you need? For example, here is my static mesh class's render function:
CRenderOperation * CStaticMesh::RenderOperation(IScene * Scene){	if(Skin && Skin->GetShader())	{		CRenderOperation * Op = Scene->RenderOperation();		Op->SetShader(Skin->GetShader());		Op->SetVertexBuffer(m_VertexBuffer);		Op->SetIndexBuffer(m_IndexBuffer);		Op->IndexedTriList(0, 0, 0, VertexCount, TriangleCount);		return Op;	}	else	{		return NULL;	}}

Actors use it like this:
void CActor::RenderMesh(IScene * Scene){	if(Graphics.Mesh)	{		CRenderOperation * Op = Graphics.Mesh->RenderOperation(Scene); // calls the above function		if(Op)		{			Op->Transform() = m_Transform;			if(Graphics.Skin)				Op->SetShader(Graphics.Skin->GetShader()); // possibly override mesh's skin			Scene->Render(Op);		}	}}

It still leaves room for totally custom drawing functions, and it is possible to write other renderers.

This topic is closed to new replies.

Advertisement