Role of the renderer

Started by
1 comment, last by Toji 18 years, 8 months ago
Perhaps this should have been re-named "Implementation of the renderer". So far, in the things that I've programmed, I've had the objects rendering themselves, but it has occured to me that maybe this isn't the best way to do it. I was thinking while watching "House" last week (great show), and I jotted down some code for a renderer where the object registers its vertices to be rendered, and the renderer keeps pointers to these vertices and has that in whatever data structure it needs.

class COGLRenderer
{
public:
	/**
	 * An object to be rendered
	 */
	struct RenderObject{
		char* name;			///< The name of the object
		float* vertex;		///< The vertices of the object (model should be in triangles)
		float* normal;		///< Normals for the vertices
		float* color;		///< Colors for the vertices
		int num_vertex;		///< (num_vertex*3) = sizeof(vertex)
	};

private:
	std::vector<RenderObject> rlist;	///< A list of all the objects to be rendered

public:

	/**
	 * Register an object for rendering
	 * \param r A refernce to a RenderObject object
	 * \return True if the item was added properly, else false
	 */
	bool Register(RenderObject& r)
	{
		// Search for identical name in the rlist
		// If found, return false
		for(int i=0; i < rlist.size(); i++)
			if( strcmp(rlist.name, r.name) == 0 )
				return false;

		// add the object to the list
		rlist.push_back( r );
		return true;
	}

	/**
	 * Render the object connected with a RenderObject
	 * \param r A const refernce to the RenderObject to render.
	 */
	void Render(const RenderObject& r)
	{
		char	flag = 0;
		char	vertex_flag = 0x1,
				normal_flag = 0x10,
				color_flag  = 0x100;

		if( r.vertex )	// if there is an array attatched to the pointer
			flag |= vertex_flag;
		if( r.normal )  // if there is an array attatched to the pointer
			flag |= normal_flag;
		if( r.color )  // if there is an array attatched to the pointer
			flag |= color_flag;

		glBegin(GL_TRIANGLES);
			for(int i=0; i < num_vertex*3 ; i++ ){
				if( flag & color_flag )
					glColor3fPointer( &r.color );
				if( flag & normal_flag )
					glNormalPointer( &r.normal );
				if( flag & vertex_flag )
					glVertex3fPointer( &r.vertex );
			}//for
		glEnd();
	}
};

I was thinking about something along these lines, but perhaps with a pointer to the object inside the RenderObject struct. I'm looking for ideas, critisism, constructive, or a good definition and explanation of the actual implementation of a renderer.
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Advertisement
It looks like you search information on designing a SceneGraph. You should try to google for "scenegraph tutorials". There's a lot of articles describing how to implement such a thing.

Basically, I use a tree, made of abstracts "SceneNode". Each SceneNode can be:
- Material (derivative: RenderState of TextureStage)
- Transformation (derivative: Position, Scalar, Rotation, Camera)
- Mesh (derivative: GUIElement, 3DObject)

I use a structure like this:

+ RootNode  + Camera    + WoodTexture     (Material)      + TreePosition1 (Position)        + Mesh        (3DObject)      + TreePosition2        + Mesh      + TreePosition3        + Mesh


Each object "renders" it's sub elements. Actually rendering a material is setting everything in the engine to show the selected material (textures states, and so on).

Each Material, Texture and Mesh are loaded from a static class, which handles IO, loading, saving, and data managment.
You seem to be going down much the same path as I was ^_^ I created a scene graph at first that had each object rendering itself, and while that worked nicely it ended up mixing a lot of Render API code with teh game logic, which I didn't like at all... (Neither did the other programmers on the team!)

So I've been working on a generic render system that let's you pass in some of the basic elements of a scene (Verticies, Textures, Shaders, etc.) and then code out all the exact render functions within a nicely encapsulated class.

Ah, what the heck. Let's post some code!

class aQRenderWorld{public:			aQRenderWorld( void );			~aQRenderWorld( void );	virtual int	Create( HWND hWnd, aQViewParams *params ) = 0;	virtual int	Destroy( void ) = 0;		virtual void	Begin( void ) = 0;	virtual void	End( void ) = 0;		//Handle the processing and rendering of the scene graph.	virtual void	Render( aQNode *scene ) = 0;			//Flips the back buffer to the front.	//This is seperate from End() to allow the GPU to process	//the scene while other game logic is preformed.	virtual void	Flip( void ) = 0;	virtual void	SetMatrix( dword matID, aQMat4 *matrix ) = 0;	//Geometry Handeling	virtual void	CreateVertBuffer( dword flags, uint elements, uint* buffer ) = 0;	virtual void	CreateIndexBuffer( uint elements, uint* buffer ) = 0;	virtual void	FreeBuffer( uint buffer ) = 0;	virtual	void	SetVertBuffer( uint buffer ) = 0;	virtual	void	SetIndexBuffer( uint buffer ) = 0;	virtual void	FillVertBuffer( void* data ) = 0;	virtual void	FillIndexBuffer( uint* data ) = 0;        //Push (or Render) the currently set Vertex and Index Buffers.	virtual void	PushStream( dword flags ) = 0;	//Texture/Material Handeling	virtual void	LoadTexture( aQString path, uint* texture ) = 0;	virtual void	FreeTexture( uint texture ) = 0;	virtual void	SetTexture( uint texture ) = 0;	//Text Printing	virtual void	Print( int x, int y, char *str, ... ) = 0;protected:	bool		loaded;	//Copy of the view parameters used to create this window.	aQViewParams	viewParams;};


This is ripped straight from my current game code. You can take from it what you want with the understanding that it's still a work in progress, but a few things to point out: Everyting is virtual here, so the actual implementation is compeletely seperate from the structure. In this case, I have an inherited class called aQRenderWorldGL that controld the OpenGL implementation, but it could just as easily be Direct X. Also, just about every graphics resource here is controlled by simple unsigned int handles. This fit's well with the OpenGL design anyways, but it also greatly simplifies data management.

The great thing is, though, that the game code simply passes in a few high level commands and moves on, and how you handle that is up to you. For example, the Push() function could render immediately, but in my implementation it actually sorts the render command onto a stack to be rendered later. The game code never has to know though.
// The user formerly known as Tojiro67445, formerly known as Toji [smile]

This topic is closed to new replies.

Advertisement