Sign in to follow this  
Daishim

Renderer implmentation

Recommended Posts

For my engine, I've been using a renderer that resides within the main engine object. The main engine object retrieves rendering information from various objects and then gives it to the renderer. However, this is leading to some unneeded complexities and is starting to get really complicated and bloated really fast. All most every implementation of a renderer that I have seen since I started this project has not been using this method. It seems that in a lot of projects, the drawing is left up to the objects that need drawing. The actual API calls are hidden in a renderer object still. Also most of those projects have a pointer to the renderer as a member. What I can't quite seem to find consistently is the how this is managed. Is there a common method of providing the drawing objects with a pointer to the rendering interface? Just passing the pointer as an argument to the constructor or a singleton? If someone even just has a good source of information on this, I'd really appreciate it. I can't seem to find what I'm looking for, but I don't think I'm looking for the right thing, because I don't exactly know what it is, I guess. Thanks.

Share this post


Link to post
Share on other sites

So, make another class called the renderer (or even put it in a separate dll and use an abstract interface to access it).

The renderer class handles all the drawing logically. In order to make it efficient the renderer class can provide kind of renderable objects : meshes, skinned meshes.

Some pseudo:

Interface_Renderable *pRenderable = Renderer->CreateRenderableObject(Geometry);

pRenderable->SetTransform(TransformMatrix);
pRenderable->Render();

The object itself knows how to draw itself and it has the required pointers to the actual renderer but this is hidden somewhere so that the caller doesn't need to know about the actual implementation.

etc...

Is this what you are looking for ? It is probable that many objects need to be aware of the renderer at least in some sense, but it is possible to split the drawing to sub classes too and most of the time that should be enough for game objects.

Cheers !

Share this post


Link to post
Share on other sites
Ahh yes, deciding whether objects should know more or let the renderer know more. Its a common problem that can go either way. It depends on how you feel things should be structured. I go the route where objects/entities only store data about themselves and have no idea how to render/collide/etc. I have processes that run through the objects that know how to deal with them. My Renderer is a process that knows how to render the elements of an object.

Of course, thats my oppinion and there is no right way of doing things. Keep in mind that things change and I like to think that I can change out pieces that I want to update or enhance, like my renderer and not have to worry about the rest of my game because I know what I'm getting and dealing with.

Jeff.

Share this post


Link to post
Share on other sites
Yea, that's pretty much what I'm struggling with at the moment. I guess, what it is that I don't really understand, is how the objects get the pointer to the renderer? I seem to only be able to find quick snippets of code about the rendering but not actually any code that shows how they get the renderer pointer. I can think of several ways (singleton, global instance, passing the pointer to the constructor, etc...). These all work, and are used to have objects know about other objects, some seen as bad, and others more bloated than others. I guess I almost expect there to be an elegant solution that is used for these situations that I'm just not catching.

Share this post


Link to post
Share on other sites

Global and singletons ... you'll end up in troubles if you plan to implement it that way. Consider that in the future your objects might reside inside another DLL which has it's own scope of globals etc.

I don't see any problem with passing the pointer to the object on creation.

Remember, most of the time constructors shouldn't take any parameters. Constructor merely initialized the class (sets variables to null etc), it shouldn't create any new objects. Also, it becomes really difficult to pass variables to ctors if using Factory Pattern for example.

I suggest of using another method for the actual creation. Perhaps something like:

Object->Create(Interfaces,Parameters);

Interface can be an object which can provide the required classes. That is, instead of passing a huge list of objects.

I suggest studying abstract classes (or interface classes). This way you can always pass the interface class anywhere you want, but the actual implementation class is always hidden. This is quite robust and elegant. It works also with dynamically linked libraries etc.

Consider:

class AbstractRenderer
{
public:
virtual void DrawSomething() = 0;
};

class Renderer : public virtual AbstractRenderer
{
public:
void DrawSomething(); //the actual implementation.
}

Now you can pass the abstractrenderer everywhere without worrying about dependancys etc.

Cheers!

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Consider two alternatives of the design:

- object can render itsaelf. You must pass the renderer ref or store it inside object.

- object is just a container which holds the data. To render a object you call something like renderer->renderObject(object). The good thing here is you may theretically create different renderers and the way they treat the same object (good feat imo!).

Or analogy with painter and shapes:

Each shape knows all the data and know how to draw itself and uses painters tools to do it (painter knows the canvas and how to operate pen to paint on it).

or

Each shape knows its data (circle know the radius and center, triangle know three vertices etc) and painter looks at particular shape and draw it with his artistic intention :)

It is clear that latter method is more closer to reality and more powerfull! However! you must create two separate classes or just functions to add one new object (this is exactly why some people hate this approach, they want self-contained autonomous objects added to the system).

so you end up with something like:

shape classes
Circle (radius and center)
Cube (six vertices, or corner and three dimensions or ...)
...

methods of renderer:
renderer::renderCircle
renderer::renderCube
etc..

or do class for general/specific renderer:
CircleRenderer -> uses renderer to render circle
etc..


ok now to more advanced stuff. As you may have already noticed, this is not the most efficient way to render such a objects... In fact we want to draw all the shapes at once with one vertex array and maybe with some shader. So this clever renderer may just render our circle as a series of vertices into some vertex array which is drawn later in one batch of the same material(here line style). We can go even further - why to make vertices the circle everytime it is being rendered? We may cache the vertices and store just index, reclaculate only when needed.

Again if you do all these things in the object itself, you will get incredibly fat classes with a LOT of details about renderer.

Just hint :)

MaR

Share this post


Link to post
Share on other sites
Quote:
Original post by Demus79
Remember, most of the time constructors shouldn't take any parameters.


Wow, that has to be one of the more odd assertions I've seen in awhile, and I certainly can't say I agree with it.

Anyways, I recommend that you go with something like

class Renderable
{
public:
virtual void Render(RenderContext &context) = 0;
};

where RenderContext provides, well, context for the rendering.

Share this post


Link to post
Share on other sites
The crux of your question is: How does each object know where the renderer is?

The solution comes down to a pointer or a reference as you've already found out. I think your confusion stems from the fact that it seems slow, stupid or otherwise "wrong" to pass this pointer/reference to each and every renderable object you create. You'd be right to think so, because obviously you do not want to store this pointer in each and every renderable object -- It would eat up memory. A solution, in this case, is to create a static member variable in the base renderable class (or interface, as the case may be.) This one instance of the pointer/reference needs only be set once, and no object instance needs to be told about it explicitly. It must be initialized to a renderer object before it is usefull, but once its set all the renderable objects know it, and when it changes, they know that too. If you're unfamiliar with static members, google or read up on them in a good book.


Another, different solution is a common design pattern known as the Command Pattern. Basically, you would have a renderer which is responsible for 2 primary things 1) abstracting the underlying API and 2) maintaining a list of renderable objects. It only knows how to render things in the generic sense -- in other words, it doesn't know how to draw Hero A or Monster B without being told explicitly. When the game needs to be rendered, it goes through its list (probably batching similar objects for performance) and calls their Draw() method, passing in a pointer to itself. Upon recieving this interface (the pointer), each object's Draw() method tells the renderer how to draw itself using the renderer's generic rendering interface. Each object knows how to draw itself; but, because it does so through the renderer's interface, objects remains insulated from the low-level details of the underlying API.

Share this post


Link to post
Share on other sites
Quote:
Original post by Demus79
Remember, most of the time constructors shouldn't take any parameters. Constructor merely initialized the class (sets variables to null etc), it shouldn't create any new objects.


And the constructor parameters determine what the members should be initialized to. Just how much work the constructor is supposed to perform is highly dependent on the situation at hand. In extreme cases of the RAII idiom, all the work the class ever performs is done in the constructor and the destructor.

Share this post


Link to post
Share on other sites
The latest evolution of my system works like so:

The world is composed of entities, contained in one or more scene managers. (Entities are not necessarily renderable, but only renderable ones are of interest to us.) A scene manager defines some kind of implementation for a query interface, for example an octree or dynamic AABB tree. There's also a Renderer. The Renderer knows how to draw actual types of entities, like models, particle systems, etc. It also has a couple misc functions, for doing things like setting up matrices, setting the current material, etc. Lastly, there is a Render Command Queue (RCQ), built from RenderCommands. Each RenderCommand maps logically to some Renderer function, or alternatively a control structure or something (like, for each pass in material would be a render command, and it would have child commands to execute).

An RCQ can be built from one or more scene managers. This results in what is basically an instruction stream for the renderer, probably optimized to minimize state changes and the like. Once an RCQ is built, it can be dumped to the Renderer, which will cause the commands to actually be executed.

So there are three responsibilities here. One is the responsibility of maintaining the state of the world, and managing scene updates, AI, etc. That responsibility is spread amongst the various scene manager implementations. Second, we have the responsibility of doing the actual rendering. This has been assigned to the renderer. Lastly, we have the responsibility of determining exactly what to render, in what order to render it, etc. That's the job of the RCQ. Entities and render commands serve as supporting classes in this setup. Note in particular that the entity types are part of the core graphics engine, but the scene managers and RCQs are completely external to it. Entities themselves are generally inherited from to create things like enemy and player classes. SRP, and conveniently YAGNI, are preserved throughout the system. Don't want/need to write a scene manager or RCQ implementation? Fine, just switch to an immediate mode rendering process. And it scales cleanly too; all you need is an SM and RCQ that can handle whatever you want to accomplish, and you're good to go.

Share this post


Link to post
Share on other sites
I would strictly seperate data and the classes that work with that data.

Data classes should only offer an interface for retrieval of information that can be used by another class to do some fancy stuff with it.

In your case:

You have a renderer implementation which is optimized for a certain effect and all the data you want to render is passed to this renderer as a stream. Your renderer implementation should expect one and only one data format which has to be delivered somehow.


E.g.:

[CODE lang="cpp"]
renderer->setVertexStream(VERTICES,vertexdata,length);
renderer->setVertexStream(NORMALS,normalsdata,normalslength);
renderer->setVertexStream(COLORS,colordata,colorlength);
renderer->setVertexStream(UVCOORDS0,uv0data,uvlength);
renderer->setVertexStream(UVCOORDS1,uv1data,uvlength);
renderer->setTextureStream(LAYER0,texture1reference);
renderer->setTextureStream(LAYER1,texture2reference);
renderer->setTextureStream(NORMALMAP0,normalmaptexturereference);
[/CODE]

Your (renderer/effectplayer) should be described in a text file(maybe XML) to specify the required data to render the objects and which effects it can play

Your data classes are used to load data at startup.
Since meshes are usually assiciated with materials, you put the textual reference to the effects into the materials, even as callback for bullet hit sounds( we abstract the whole representation of data to the user with effects that are displayed by effect players)


Each effect has its own unique name and can be played by multiple effect players that are available depending on your hardware specs.

So starting the engine works like this:
- load all required data
- load all effect descriptions
- load all effectplayer descriptions
- check all effect references to attach the proper effectplayer to the effect
- now generate possibly missing data required by the effectplayers in a preprocess


Runtime actions looks as follows
- find data to display/play(graphics/sound)
- get reference to the effect object used to play
- pass data reference to the effect
- effect object retrieves additional data generated in a pre process
- effect object streams this data to the effect player
- effect player displays/plays the effect



Pros:
+ easy to extend with now effects
+ categorizeable into pre frame, runtime, post frame effects
+ effects can me maintained in a queue to control the process of playing these effects
+ seperation of local(rendering mesh) and global(outdoor shadowmaps)

Cons:
- you need to create a sufficient database that allows the fast retrieval of loadtime data and pre proccessed data that has been associated with each other in the starting phase
- lots of code to get a basic system running, but this will pay back when creating more complex systems


Share this post


Link to post
Share on other sites
Quote:
Original post by JasonBlochowiak
Quote:
Original post by Demus79
Remember, most of the time constructors shouldn't take any parameters.


Wow, that has to be one of the more odd assertions I've seen in awhile, and I certainly can't say I agree with it.

Anyways, I recommend that you go with something like

class Renderable
{
public:
virtual void Render(RenderContext &context) = 0;
};

where RenderContext provides, well, context for the rendering.


Wow, that's nice quoting. Taking the phrase out of context.

As I explained, there are cases where constructors cannot take parameters such as when using factory pattern (at least it is difficult in this case) or when creating arrays. "Most of the time" doesn't mean always.

It is just fine for a vector class for example to take initializing parameters. I was giving a suggestion of a pattern for Daishim to follow. Doing too many things in the ctors may lead in bad in the early phases.

Forgive me for such a silly assertion. My thoughts were elsewhere ...

Cheers

[Edited by - Demus79 on October 25, 2006 4:56:11 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this