Game Development Community

Render Server Objects

by Michel De Messieres · 01/31/2007 (2:41 pm) · 4 comments

The basic plan is to add a link between every server object and client object and then use this to render the server when the client renders. You can use this link for other debugging purposes as well but this code will just set up rendering.

Eventually you may want to wrap the code in TORQUE_DEBUG #ifdef statements.

First we'll add a link variable to NetObject.
Add to the public variables of netObject.h:

NetObject *m_myLocalPartner; // direct link to local ghost/server object for debugging

Add this to the netObject::netObject() constructor at the top of netObject.cc

m_myLocalPartner = NULL; // direct link to local ghost/server object for debugging

Now to form the link as objects are added. Modify onAdd() in netObject.cc

bool NetObject::onAdd()
{
   if(mNetFlags.test(ScopeAlways))
      setScopeAlways();

   // this debug code will only run on the server machine
	if( isClientObject() && !m_myLocalPartner )
	if( NetConnection::getConnectionToServer()  )
	if( NetConnection::getLocalClientConnection()  )
	{
	    S32 ghostIndex = NetConnection::getConnectionToServer()->getGhostIndex( this );
	    NetObject * pMatch = NetConnection::getLocalClientConnection()->resolveObjectFromGhostIndex( ghostIndex );
		m_myLocalPartner = pMatch;
		if( pMatch )
			pMatch->m_myLocalPartner = this;
	}

   return Parent::onAdd();
}

Now we want the objects to render - I use ShapeBase.cc as the entry point

Add the following code to the very beginning of ShapeBase::prepRenderImage() in ShapeBase.cc

if( TSMesh::smRenderServer )
{
	if( m_myLocalPartner && isClientObject() )
		( (ShapeBase*) m_myLocalPartner )->prepRenderImage( state, stateKey, startZone, modifyBaseState );
}

Now a little further down in prepRenderImage() is a call to animate(), change it to be like this:

if (mShapeInstance && isClientObject() )
         mShapeInstance->animate();

The reason for the above is you definitely do not want the server object to change itself from the render calls - normally it won't get those calls and you want to see how the server object is behaving when the debug feature is off. There may be other places in your code you'll need to make adjustments to make sure prepRenderImage is not letting your server object update.


Add the following code to the very beginning of ShapeBase::renderObject() in ShapeBase.cc

if( TSMesh::smRenderServer )
{
if( m_myLocalPartner && isClientObject() )
	( (ShapeBase*) m_myLocalPartner )->renderObject( state, image );
}

Now we have to fix a few things that will crash if you try to render a server object.

First, the server object won't have materials, which is ok since they will just render a nice ghostly gray and that will make it easy to tell it apart from the client. We just have to make a small fix so it won't crash.

In tsMesh.cc find the following code. I added the first line to check if materials is NULL

if( materials )  // Added this line 
if ( ((TSShapeInstance::smRenderData.materialIndex ^ draw.matIndex) &
     (TSDrawPrimitive::MaterialMask|TSDrawPrimitive::NoMaterial)) != 0)
         setMaterial(draw.matIndex,materials);

Another spot is sceneState.cc - server objects won't have a valid zone - I have not investigated this carefully and someone with experience here can probably suggest a cleaner method.

Find the following line

AssertFatal(pWalk != NULL, "Error, object must exist in at least one zone to call this!");

and change it to

if( !pWalk )
	   return; // debug rendering a ghosted object so this is ok
   AssertFatal(pWalk != NULL, "Error, object must exist in at least one zone to call this!");

Last thing is to add a console option so you can toggle the system on and off

Add to tsShapeInstance.cc at the bottom of TSShapeInstance::init()

Con::addVariable("$pref::TS::RenderServer",  TypeBool, &TSMesh::smRenderServer); // RenderServer

Add to tsMesh.h after the comment shown below
/// on load...optionally convert primitives to other form

static bool smRenderServer;

Add the following line to tsMesh.cc before this line:
bool TSMesh::smUseTriangles = false; // convert all primitives to triangle lists on load

bool TSMesh::smRenderServer = false; // render all the server objects

Now link it up in the script - I have it mapped to the g key

Add to default.binds.cs

moveMap.bind(keyboard, "g", toggleRenderServer );  // RenderServer

function toggleRenderServer(%val)  // RenderServer
{ 
   if( %val )
   {
      if( $pref::TS::RenderServer )
         $pref::TS::RenderServer = false;
      else
         $pref::TS::RenderServer = true;
   }
}

And finally add to the config.cs file

moveMap.bind(keyboard, "g", toggleRenderServer);

Now you should be able to haunt your game with server objects by pressing g.

About the author

Recent Blogs


#1
02/05/2007 (9:25 pm)
great for debug, thanks!!
#2
03/06/2007 (9:13 pm)
Michel:
I have a mounted image and I receive fatal errors from Player::renderMountedImage() with this resource.
#3
03/07/2007 (4:47 pm)
hi,

I never used mount objects so I missed something that needs to be turned off.
You might just try adding a check to not render that function if it's the server object - that should fix it - but will probably need some tweaking to actually get mount objects to render.
Unfortunately Im back in school right now so my Torque days are done, at least till summer time.
Michel
#4
07/05/2007 (3:08 am)
Cool resource :) It would be nice to change it so that you can easily specify which type of network objects you would like rendered (ie. TSStatics vs. Players vs. Vehicles, etc.).

One crash that I had (and resolved) was to do with shadow rendering.

Inside of:

TSMesh::renderShadow()


There was another use of the "materials" pointer which needed a NULL check on, so you need to change:

if (!(primitives[0].matIndex & TSDrawPrimitive::NoMaterial) && (TSMaterialList::Translucent & materials->getFlags(primitives[0].matIndex & TSDrawPrimitive::MaterialMask)))
      // if no material...it would be nice to just exit...but may have some real polys back there...
      return;

to

if ((!materials) || (!(primitives[0].matIndex & TSDrawPrimitive::NoMaterial) && (TSMaterialList::Translucent & materials->getFlags(primitives[0].matIndex & TSDrawPrimitive::MaterialMask))))
      // if no material...it would be nice to just exit...but may have some real polys back there...
      return;