Game Development Community

Shadows on WheelVehicle Wheels - progress

by Duncan Gray · in Torque Game Engine · 12/05/2005 (3:55 pm) · 17 replies

I have been looking into this and so far have observed two ways to get this working.

The 'better' way would be to make the wheel a shapebase derived class with a datablock etc and then mount the wheels to the vehicle hub nodes. Reason?, mounted shapes get there shadows drawn when the vehicle shadow gets drawn. The wheel rotation would then need to be acheived by rotating the hub nodes themselves rather than the current system of rotating the wheel mesh directly. I don't know if thats plausable at this stage.

It seemed like a bit too much work so I began with a simpler, perhaps more CPU intensive approach.
I looked at/extracted code from the shapebase::drawshadow() method and created a similar method for the wheeledvehicle class, but making necessary changes to use the wheel shapeinstance and not the vehicle shapeinstance.

The reason I say its more CPU intensive is that a lot of the checks and balances done prior to drawing the shadow are duplicated in this method, thus they get done once for the vehicle and then once for each wheel. The top "better way" would only do those checks once, regardless of wheel count.

It does work, with one small problem, its only drawing half a shadow. I don't know at this stage if its the shadow bitmap which is too small or limited in size. How big can a shadow be?

Anyway here is what I have so far.
Add the following includes to the top of wheeledvehicle.cc.
#include "platform/profiler.h"
#include "game/shadow.h"

Add the following to the very end of the WheeledVehicle::renderImage method.
for (Wheel* wheel = mWheel; wheel < wend; wheel++) {
	   if (wheel->shapeInstance) {
		   
		   PROFILE_START(PlayerRenderShadow);
           renderWheelShadow(wheel->shapeInstance,state);		 
		   PROFILE_END();
	   }
	  }
Then create a new wheelvehicle method as follows
void WheeledVehicle::renderWheelShadow(TSShapeInstance* wShapeInstance,SceneState* state)
{
	
	
	if (Shadow::getGlobalShadowDetailLevel()<mDataBlock->noShadowLevel)
        return;
	Point3F cameraOffset;
	getRenderTransform().getColumn(3,&cameraOffset);
	cameraOffset -= state->getCameraPosition();
	F32 dist = cameraOffset.len();
	
	bool fogExemption = false;
	GameConnection *con = GameConnection::getConnectionToServer();
	if(con && con->getControlObject() == this && con->isFirstPerson() == true)
		fogExemption = true;
	F32 fogAmount = fogExemption ? 0.0 : state->getHazeAndFog(dist,cameraOffset.z);
	
	TSMesh::setOverrideFade( mFadeVal );
	//------------------
	
	Shadow * mShadow = new Shadow();
	
	if (Shadow::getGlobalShadowDetailLevel() < mDataBlock->genericShadowLevel)
		mShadow->setGeneric(true);
	else
		mShadow->setGeneric(false);
	
	
	// Get a real light dir...
	Point3F lightDir = gClientSceneGraph->getLightManager()->getShadowLightDirection();
	
	F32 shadowLen = 10.0f * wShapeInstance->getShape()->radius;
	Point3F pos = wShapeInstance->getShape()->center;
	
	// this is a bit of a hack...move generic shadows towards feet/base of shape
	if (Shadow::getGlobalShadowDetailLevel() < mDataBlock->genericShadowLevel)
		pos *= 0.5f;
	
	pos.convolve(mObjScale);
	getRenderTransform().mulP(pos);
	
	// pos is where shadow will be centered (in world space)
	mShadow->setRadius(wShapeInstance, mObjScale);
	if (!mShadow->prepare(pos, lightDir, shadowLen, mObjScale, dist, fogAmount, wShapeInstance))
		return;
	
	F32 maxScale = getMax(mObjScale.x,getMax(mObjScale.y,mObjScale.z));
	
	if (mShadow->needBitmap())
	{
		mShadow->beginRenderToBitmap();
		//mShadow->selectShapeDetail(wShapeInstance,dist,maxScale);
		mShadow->renderToBitmap(wShapeInstance, getRenderTransform(), pos, mObjScale);
		mShadow->endRenderToBitmap();
	}
	
	mShadow->render();
	
	//----------
	
	TSMesh::setOverrideFade( 1.0 );
}
Heres the half shadow.
www.wheelracer.info/other/halfshadow.jpg

#1
12/05/2005 (4:02 pm)
The reason I commented out the //mShadow->selectShapeDetail(wShapeInstance,dist,maxScale); is because I set the wheel LOD in the renderImage method as follows.

wheel->shapeInstance->animate();
// add the following -------------------------------------------------------------------------
S32 num = mShapeInstance->getCurrentDetail();
// if the wheel is rotating fast, u can't see the high detail so jump to a lower detail
if((wheel->avel >3)&&(num < 2 ))num =1;
wheel->shapeInstance->setCurrentDetail(num,mShapeInstance->getCurrentIntraDetail());
// add till here --------------------------------------------------------------------------------
wheel->shapeInstance->render();

Which solves the missing LODs on wheels. (They never get used in the standard code)
#2
12/05/2005 (4:04 pm)
Hi Duncan,

The Torque Lighting Kit casts wheel shadows, you can check out the latest Torque racing demo to see them in action (the Torque demo uses TLK).

-John
#3
12/05/2005 (4:06 pm)
@John, thanks but TLK aint free.
#4
12/05/2005 (4:09 pm)
It may be attributed by your final pos value. If you raise your z value, you might get a better shadow instead of being buried in the terrain.

Try this for size:
Point3F pos = wShapeInstance->getShape()->center;
pos.z += 0.5f; // or higher but < 1.0f

And do your remaining computations and leave out:
pos *= 0.5f;
#5
12/05/2005 (4:22 pm)
Actually, you might raise the pos.z value higher and see what you get.

Below are my wheel shadow functions. I posted it to the resources but it's not quite there yet. I don't think it's any better than what you have but shadow rendering occurs on the second stage of the rendering sequence, where image->transparent flag is set. You may want to check for the flag for use what's shown below.

Header Files
Prototypes.
[b][ShapeBase.h][/b]
   virtual void addShadowDetail(F32 dist, F32 fogAmount, Shadow *shadow);
*
[b][Vehicle.h][/b]
   void addShadowDetail(F32 dist, F32 fogAmount, Shadow *shadow);
*
[b][WheeledVehicle.h][/b]
   void addShadowDetail(F32 dist, F32 fogAmount, Shadow *shadow);
*

Source Files
[b][ShapeBase.cc][/b]
void ShapeBase::addShadowDetail(F32 dist, F32 fogAmount, Shadow *shadow)
{
   // empty stuff
}

and in this function, add the following:
void ShapeBase::renderShadow(F32 dist, F32 fogAmount)
{

//after this condition code and before the end
   if (mShadow->needBitmap())
   {

// add this call
      addShadowDetail(dist, fogAmount, mShadow);
.
.
}

*
[b][Vehicle.cc][/b]
void Vehicle::addShadowDetail(F32 dist, F32 fogAmount, Shadow *shadow)
{
   //empty for now
}

*
[b][WheeledVehicle.cc][/b]
void WheeledVehicle::addShadowDetail(F32 dist, F32 fogAmount, Shadow *shadow)
{
   Parent::addShadowDetail(dist, fogAmount, shadow);

   if ( shadow )
   {
      Wheel* wend = &mWheel[mDataBlock->wheelCount];
      for (Wheel* wheel = mWheel; wheel < wend; wheel++)
      {
         if (wheel->shapeInstance)
         {
            MatrixF mat = getRenderTransform();
            Point3F rdpos = mat.getPosition();

            // Steering & spring extension
            MatrixF hub(EulerF(0,0,mSteering.x * wheel->steering));
            Point3F pos = wheel->data->pos;
            pos.z -= wheel->spring->length * wheel->extension;
            hub.setColumn(3,pos);
            mat.mul(hub);

            // Rotation the tire to face the right direction
            // (could pre-calculate this)
            MatrixF wrot(EulerF(0,0,(wheel->data->pos.x > 0)? M_PI/2: -M_PI/2));
            mat.mul(wrot);

            // shadow details
            #ifdef LOD_WHEEL_SHADOW
            shadow->selectShapeDetail(wheel->shapeInstance, dist, 1.0f);
            #else
            wheel->shapeInstance->setCurrentDetail(0);
            #endif
            shadow->renderToBitmap(wheel->shapeInstance, mat, rdpos, Point3F(1,1,1));
         }
      }
   }
}


Define LOD_WHEEL_SHADOW if you want to use LOD wheel shadows.
#6
12/05/2005 (4:23 pm)
@Akio, nope that did not do it, but you are on to something. When I F11 and then manually lift the vehicle a meter or so off the ground, the shadow is drawn correctly, so it does seem position related. Now that I know where to look, I'm sure Ill find it soon.
#7
12/05/2005 (4:34 pm)
Would passing a modifed render transformation to renderToBitmap() help?
#8
12/05/2005 (4:42 pm)
@Akio, I posted the previous one before I saw your post. I like your method better since the checks done before rendering shadows are only done once in shapebase. Nice.

I'll try your approach now.
#9
12/05/2005 (4:45 pm)
You may find it does the same thing because I see it on my right rear wheel as well. Need some tweaking:)
#10
12/05/2005 (5:22 pm)
Akio your method works perfectly, but with some odd behavior, probably specific to my vehicle.

When I rotate the steering, the shadow immiediatly rotates even though the vehicle is still slowly catching up due to its physics.

I'll look into that next. Thanks for the help Akio!
#11
12/05/2005 (5:30 pm)
K, no prob.

Change this portion from the code since your wheel is actually a body.
MatrixF hub(EulerF(0,0,mSteering.x * wheel->steering));

and change it to
            MatrixF hub(1);

This change removes the steering angle.
#12
12/05/2005 (5:31 pm)
Oh, I forgot to mention it but I think your game and game concept is great:)
#13
12/05/2005 (7:18 pm)
Thanks. Its been a long slog. The next release aimed for Feb, is so far, looking so much much better than the previous bunch that I'm embarrased to look back at those previous versions.

Yeah, I spotted the mSteering in the code and took it out. I also added
// Wheel rotation
            MatrixF rot(EulerF(wheel->apos * M_2PI,0,0));
            mat.mul(rot);
To get the shadow to rotate with the wheel. Probably not required for solid wheels.

Two minor issues. The pic below, right hand arrow shows where the shadow is clipped but no one is going to notice it during all the action. It might be a shadow bitmap size issue but I'm not going to bother with fixing it at this stage.

The left green arrow shows the axel (part of the vehicle, not the wheel) which seems to stick out the side of the shadow, indicating that vehicle and wheel shadow calcs are not perfectly alligned.
www.wheelracer.info/other/shadow.jpg
But again, only the developer would notice that so it's not important for now.
#14
12/05/2005 (9:08 pm)
That's looking pretty sharp if you ask me. Yes, I removed the wheel rotation eqn and forgot to mention that; glad you caught that. I do see the misaligned axel shadow after looking at it very closely. It looks like rendering position offset is wrong, but I agree that gamers would not see the minor flaw while moving/racing.

Looks good overall:)
#15
12/05/2005 (11:30 pm)
Wow guys... this is really good work...
#16
12/06/2005 (3:30 am)
Unfortunately on some maps where the sun is positioned lower (long shadows) the above flaws become very visible, so I had to delve into it some more and got it fixed. Here is the final calcs I'm using in the wheel loop

if (wheel->shapeInstance)
         {			
           
			 MatrixF mat = getRenderTransform();           
			 Point3F rdpos = mat.getPosition();            // Steering & spring extension  
			 MatrixF hub(1); //EulerF(0,0,mSteering.x * wheel->steering)
			 Point3F pos = wheel->data->pos;           
			 pos.z -= ((wheel->spring->length * (wheel->extension)) + wheel->tire->radius );        
			 hub.setColumn(3,pos);           
			 mat.mul(hub);
			// Wheel rotation
            MatrixF rot(EulerF(wheel->apos * M_2PI,0,0));
            mat.mul(rot);

			S32 num = mShapeInstance->getCurrentDetail();
		 // if the wheel is rotating fast, u can't see the detail so jump to a lower detail
		    if((wheel->avel >3)&&(num < 2 ))num =1;
		    wheel->shapeInstance->setCurrentDetail(num,mShapeInstance->getCurrentIntraDetail());
			shadow->renderToBitmap(wheel->shapeInstance, mat, rdpos, mObjScale);
         }

The main difference seems to be in the pos.z calculation. I also added mObjScale because I assume that if you scale your vehicle, the wheel shadows should scale with it.
#17
12/06/2005 (8:07 am)
Glad to hear it's corrected:)
Wheels are not scaled w/ vehicles, but no biggie 'cause most vehicles are unit scaled.

I can't wait to see your new demo in Feburary:)