Game Development Community

dev|Pro Game Development Curriculum

T3D Bravetree Tank Pack Port

by Tim Dix (Raverix) · 05/12/2009 (9:30 pm) · 18 comments

Alright, some disclaimers, this is a quick and dirty port that several people requested on the forums, and there are some sections that are completely untested. If you use this and you notice any bugs, let me know.

This port assumes you're going from a fresh download of the tank pack for TGE1.3, so it includes all the changes to get you to 1.4, TGEA, SFX, and T3D.

There's a lot of changes, but most are pretty simple and straight forward. The changes are in order , so work your way from the top to the bottom of the source file. Also, this port does NOT use SimplePolyList, simply because I haven't ported it yet, but the tanks should work just fine without it.

You will only need tankShape.cc, and tankShape.cpp.

Rename tankShape.cc to tankShape.cpp

tankShape.cpp & tankShape.h


Change all references to
TypeAudioProfilePtr to TypeSFXProfilePtr
AudioProfile* to SFXProfile*
NULL_AUDIOHANDLE to NULL
GameConnection::getServerConnection() to GameConnection::getConnectionToServer()
AUDIOHANDLE to SFXSource*

Change all references to *box*.min to *box*.minExtents, same thing for max, such as :
objectBox.max to objectBox.maxExtents
objectBox.min to objectBox.minExtents
wb.max to wb.maxExtents
wb.min to wb.minExtents


tankShape.cpp


In tankShape.cpp, Change the existing includes to these:
#include "core/stream/bitStream.h"
#include "collision/simplePolyList.h"
#include "sceneGraph/sceneState.h"
#include "collision/clippedPolyList.h"
#include "collision/planeExtractor.h"
#include "T3D/moveManager.h"
#include "T3D/gameConnection.h"
#include "ts/tsShapeInstance.h"
#include "console/consoleTypes.h"
#include "terrain/terrData.h"
#include "sceneGraph/sceneGraph.h"
#include "sfx/sfxSystem.h"
#include "T3D/fx/particleEmitter.h"
#include "math/mathIO.h"
#include "core/resourceManager.h"
#include "terrain/waterBlock.h"
#include "math/mathUtils.h"

#include "collision/boxConvex.h"
#include "collision/earlyOutPolyList.h"
#include "collision/extrudedPolyList.h"

Right after all the includes, find the namespace{...} block, and Delete:
SimplePolyList sPolyList;

In TankShapeData::TankShapeData() - Delete the following:
genericShadowLevel = Vehicle_GenericShadowLevel;
noShadowLevel = Vehicle_NoShadowLevel;

Change
//bool TankShapeData::preload(bool server, char errorBuffer[256]) //OLD
bool TankShapeData::preload(bool server, String &errorStr)

Change
//if (!Parent::preload(server,errorBuffer)) //OLD
if (!Parent::preload(server,errorStr))

Change (There should be two occurrences)
//dSprintf(errorBuffer, 256, "TankShapeData: Incomplete data"); //OLD
Con::warnf("TankShapeData: Incomplete data");

Change
//shapeResource = ResourceManager->load(shapeName,false); //OLD
shapeResource = ResourceManager::get().load(shapeName);

Change
//dSprintf(errorBuffer, 256, "TankShapeData: Couldn't load shape "%s"",shapeName); //OLD
Con::warnf("TankShapeData: Couldn't load shape "%s"",shapeName);

Add IMPLEMENT_CONSOLETYPE(TankCameraData)
IMPLEMENT_CO_DATABLOCK_V1(TankCameraData);
IMPLEMENT_CONSOLETYPE(TankCameraData)  // ADD THIS LINE

IMPLEMENT_GETDATATYPE(TankCameraData)
IMPLEMENT_SETDATATYPE(TankCameraData)

Delete
Con::registerType("TankCameraDataPtr", TypeTankCameraDataPtr, sizeof(TankCameraData*),
                     REF_GETDATATYPE(TankCameraData),
                     REF_SETDATATYPE(TankCameraData));

Add IMPLEMENT_CONSOLETYPE(TankFxData)
IMPLEMENT_CO_DATABLOCK_V1(TankFxData);
IMPLEMENT_CONSOLETYPE(TankFxData)    // ADD THIS LINE

IMPLEMENT_GETDATATYPE(TankFxData)
IMPLEMENT_SETDATATYPE(TankFxData)

Delete
Con::registerType("TankFxDataPtr", TypeTankFxDataPtr, sizeof(TankFxData*),
                     REF_GETDATATYPE(TankFxData),
                     REF_SETDATATYPE(TankFxData));

Change
//OLD CODE BELOW
bool TankFxData::preload(bool server, char errorBuffer[256])
{
   if (!Parent::preload(server,errorBuffer))
      return false;

//NEW CODE
bool TankFxData::preload(bool server, String errorStr)
{
   if (!Parent::preload(server,errorStr))
      return false;

AddIMPLEMENT_CONSOLETYPE(TankMiscData)
IMPLEMENT_CO_DATABLOCK_V1(TankMiscData);
IMPLEMENT_CONSOLETYPE(TankMiscData) 

IMPLEMENT_GETDATATYPE(TankMiscData)
IMPLEMENT_SETDATATYPE(TankMiscData)

Delete
Con::registerType("TankMiscDataPtr", TypeTankMiscDataPtr, sizeof(TankMiscData*),
                     REF_GETDATATYPE(TankMiscData),
                     REF_SETDATATYPE(TankMiscData));

Delete
mGenerateShadow = true;

Replace
//OLD CODE BELOW:
   if (mEngineSoundA != NULL_AUDIOHANDLE)
      alxStop(mEngineSoundA);
   if (mEngineSoundB != NULL_AUDIOHANDLE)
      alxStop(mEngineSoundB);

//NEW CODE
   if (mEngineSoundA != NULL)
	   mEngineSoundA->stop();
   if (mEngineSoundB != NULL)
	   mEngineSoundB->stop();

Add in function TankShape::onNewDataBlock before mObjBox = getDataBlock()->objectBox;
if ( getDataBlock()->fx->engineSoundA )
         mEngineSoundA = SFX->createSource( getDataBlock()->fx->engineSoundA, &getTransform() );
   if ( getDataBlock()->fx->engineSoundB )
         mEngineSoundB = SFX->createSource( getDataBlock()->fx->engineSoundB, &getTransform() );

Change
//Con::executef(getDataBlock(),4,"onEnterLiquid",scriptThis(), Con::getFloatArg(mWaterCoverage), Con::getIntArg(mLiquidType)); //OLD CODE
Con::executef(getDataBlock(),"onEnterLiquid",scriptThis(), Con::getFloatArg(mWaterCoverage), mLiquidType);

Change
//Con::executef(getDataBlock(),3,"onLeaveLiquid",scriptThis(), Con::getIntArg(mLiquidType)); //OLD CODE
Con::executef(getDataBlock(),"onLeaveLiquid",scriptThis(), mLiquidType);

Change TankShape::updateCollisionSound()
//alxPlay(playSound,&getTransform()); // OLD
SFX->playOnce(playSound, &getTransform());

Change TankShape::updateEngineSound()
if (mEngineSoundA != NULL)
	mEngineSoundA->stop();
if (mEngineSoundB != NULL)
	mEngineSoundB->stop();

Delete
if (getDataBlock()->fx->engineSoundA && mEngineSoundA == NULL_AUDIOHANDLE)
      mEngineSoundA = alxPlay(getDataBlock()->fx->engineSoundA,&getTransform());
if (getDataBlock()->fx->engineSoundB && mEngineSoundB == NULL_AUDIOHANDLE)
      mEngineSoundB = alxPlay(getDataBlock()->fx->engineSoundB,&getTransform());

Replace
//OLD CODE BELOW
   if (alxIsValidHandle(mEngineSoundA))
   {
      alxSourceMatrixF(mEngineSoundA, &getTransform());
      alxSourcef(mEngineSoundA,AL_PITCH,pa);
      alxSourcef(mEngineSoundA,AL_GAIN_LINEAR,va);
   }
   if (alxIsValidHandle(mEngineSoundB))
   {
      alxSourceMatrixF(mEngineSoundB, &getTransform());
      alxSourcef(mEngineSoundB,AL_PITCH,pb);
      alxSourcef(mEngineSoundB,AL_GAIN_LINEAR,vb);
   }

//NEW CODE
   if (mEngineSoundA)
   {
	   mEngineSoundA->setTransform(getTransform());
	   mEngineSoundA->setPitch(pa);
	   mEngineSoundA->setVolume(va);
   }
   if (mEngineSoundB)
   {
	   mEngineSoundB->setTransform(getTransform());
	   mEngineSoundB->setPitch(pb);
	   mEngineSoundB->setVolume(vb);
   }

tankShape.h


Change
//#include "game/shapeBase.h" //OLD
#include "T3D/shapeBase.h"

Change (Should be two occurrences)
//bool preload(bool server, char errorBuffer[256]); //OLD
bool preload(bool server, String &errorStr);

Add just before the #endif
DECLARE_CONSOLETYPE(TankCameraData)  
DECLARE_CONSOLETYPE(TankFxData)  
DECLARE_CONSOLETYPE(TankMiscData)

One last disclaimer: I wrote the code while playing WoW... so I might have been a bit distracted, G'luck!

#1
05/12/2009 (9:47 pm)
Show off! :P Awesome, works like a charm! Thank you so much!
#2
05/13/2009 (9:32 am)
HA! Saved me some work. Was going to do that this weekend, plus add some other things.

Thanks for taking the time to share the changes.
#3
05/14/2009 (8:45 pm)
@Tim,

Not working for me in T3D, getting several "Min is not a member of Box3F". Checked over and over so I believe I have updated all changes, just not getting a good compile for now.

Did you start with the current download of the Tank Pack or an older version?
#4
05/15/2009 (5:15 am)
Started with the current download of tank pack, if you have some Min is not member, you need to change it to

minExtents

Such as
objectBox.max to objectBox.maxExtents
objectBox.min to objectBox.minExtents
wb.max to wb.maxExtents
wb.min to wb.minExtents
#5
05/17/2009 (8:44 pm)
After looking at this some more, why would you not just use the Vehicle class? Similar to how the Striker IAV does.
#6
05/18/2009 (8:03 am)
As a matter of fact, that's precisely what I'm doing in my game. However, that requires a bit more changes.

This here was just a quick port of the tank pack so that it'll work with T3D.

However, if you follow the above resource, you're a good chunk of the way there. To get this to work with the vehicle class is actually as simple as changing what class it derives from ShapeBase, to Vehicle. But to actually use the physics from the vehicle class, you'll have to modify updatePos, updateMove, and several other related functions to fit in with the Vehicle way. You'll want to take a look inside one of the existing Vehicles, like WheeledVehicle for example of how they do processTic, updateMove, and updateForces. Can't say more without looking at the source code.

Such updates were just beyond the scope of this port.
#7
02/17/2010 (5:09 pm)
I was just successful in using this to port the pack to TGEA 1.8.2 with some modifications. I will post a new resource with the changes for that.
#8
02/18/2010 (2:25 pm)
Thanks Sean, this is something that's been wanted done for ages now.
#9
02/21/2010 (2:20 pm)
Here is the TGEA port that I am working on, some small issues still present in the tanks.

www.torquepowered.com/community/resources/view/19367
#10
04/09/2010 (12:28 am)
I'm getting some errors trying to add the code in T3D 1.1 Beta 3.
#11
03/17/2011 (6:53 am)
Also having issues with 1.1 B3.

These lines are not liked by the compiler:

in tankshape.h:
DECLARE_CONSOLETYPE(TankCameraData)
DECLARE_CONSOLETYPE(TankFxData)
DECLARE_CONSOLETYPE(TankMiscData)

in tankshape.cpp:
IMPLEMENT_CONSOLETYPE(TankCameraData)
IMPLEMENT_CONSOLETYPE(TankFxData)
IMPLEMENT_CONSOLETYPE(TankMiscData)

Were these commands deprecated or something when T3D went from 1.01 to 1.1? Because this resource apparently works in T3D, which I assume based on the date must have been version 1.01.

I know lots of things changed when upgrading from 1.01 to 1.1, anyone have any ideas on what part of this port needs to be tweaked for 1.1B3?

Many thanks
#12
05/18/2011 (9:40 am)
There is a simple solution..

Rebuild the Tank shape and create the Hubs.
Remove the wheels from the shape and make 2-3 wheel shapes from them.
Then make it a wheeled vehicle until we can sort out the Animation of
the treads properly. I had to remove 4 of the inner wheels as they were
a bit too close together and had issues on the last 2 colliding with
the rear outer wheel, so I dropped to a 12 wheel tank instead of 16.

I've been playing around with this setup in 1.1 preview, as the treads
dont have collision (Not included in the Collision shape), the tires
when added ignore the tread and collide with the ground instead.

Skipped Spring animation and just tweaked spring datablocks for the
inner and outer wheels so the outer wheels stay up higher with less
drop then the inner wheels. I split off the Top Turret and gun and
made it a weapon shape with a Fire animation for the Barrel.

At the moment, I just finished the modifications to get it moving
forward and back. I'm going to have to do a completly seperate
class for it tho that derives from wheeledvehicle class so I
can deal with turning the thing by adjusting the speed of the
tires for one side or the other.

This is still a work in progress, but a quick workaround to make a
basic looking tank for the moment until we get animations sorted out.
#13
05/18/2011 (9:43 am)
Heres the actual DataBlocks I used for the Sherman tank:

datablock WheeledVehicleTire(TankFrontTire)
{
   shapeFile = "art/shapes/vehicles/sherman/sherman_frontwh.dts";
   staticFriction = 4.2;
   kineticFriction = 3.15;

   // Spring that generates lateral tire forces
   lateralForce = 18000;
   lateralDamping = 6000;
   lateralRelaxation = 1;

   // Spring that generates longitudinal tire forces
   longitudinalForce = 18000;
   longitudinalDamping = 4000;
   longitudinalRelaxation = 1;
};
datablock WheeledVehicleTire(TankInnerTire)
{
   shapeFile = "art/shapes/vehicles/sherman/sherman_innerwh.dts";
   staticFriction = 4.2;
   kineticFriction = 3.15;

   // Spring that generates lateral tire forces
   lateralForce = 18000;
   lateralDamping = 6000;
   lateralRelaxation = 1;

   // Spring that generates longitudinal tire forces
   longitudinalForce = 18000;
   longitudinalDamping = 4000;
   longitudinalRelaxation = 1;
};
datablock WheeledVehicleSpring(OuterTankSpring)
{
   // Wheel suspension properties
   length         = 0.04;             // Suspension travel
   force          = 2800;              // Spring force
   damping        = 2800;             // Spring damping
   antiSwayForce  = 3;         // Lateral anti-sway force
};
datablock WheeledVehicleSpring(InnerTankSpring)
{
   // Wheel suspension properties
   length         = 0.2;             // Suspension travel
   force          = 2500;              // Spring force
   damping        = 2500;             // Spring damping
   antiSwayForce  = 3;         // Lateral anti-sway force
};

Im still tweaking, these are mostly just brought over from the
buggy, but gets it moving. More tweaks to get it right are needed.
#14
05/18/2011 (9:45 am)
All I did for adding the tanks was to override the ::onAdd to setup
the wheels correctly:

function ShermanTankVehicle::onAdd(%this, %obj)
{
   //Parent::onAdd(%this, %obj);

   echo("ShermanTankVehicle::onAdd("@ %this.getName() @", "@ %obj.getClassName() @")");

   %obj.setRechargeRate(%this.rechargeRate);
   %obj.setEnergyLevel(%this.MaxEnergy);
   %obj.setRepairRate(0);

   if (%obj.mountable || %obj.mountable $= "")
      %this.isMountable(%obj, true);
   else
      %this.isMountable(%obj, false);

   if (%this.nameTag !$= "")
      %obj.setShapeName(%this.nameTag);

   //Modified by Tank Type
   //We need to Add the Front/Inner/Rear tire types
   //To the datablock fields

   //%count = %obj.getWheelCount(); 
   //As we have a Left/Right side of even wheels :)
   %countTot = %obj.getDataBlock().wheelCount;
   %countSide = %countTot / 2;
   
   echo( "<--- Wheel Count = "@%countTot);
   
   //Right Side
   for(%i=0; %i<=%countSide-1; %i++)
   {
      %tire    = (%i==0 || %i==%countSide-1) ?  TankFrontTire   : TankInnerTire;
      %spring  = (%i==0 || %i==%countSide-1) ?  OuterTankSpring : InnerTankSpring;

      %obj.setWheelTire(%i, %tire);
      %obj.setWheelSpring(%i, %spring);
      %obj.setWheelPowered(%i, true);
      %obj.setWheelSteering(%i, false);
   }
   //Left Side
   for(%i=%countSide; %i<=%countTot-1; %i++)
   {
      %tire    = (%i==%countSide || %i==%countTot-1) ?  TankFrontTire   : TankInnerTire;
      %spring  = (%i==%countSide || %i==%countTot-1) ?  OuterTankSpring : InnerTankSpring;

      %obj.setWheelTire(%i, %tire);
      %obj.setWheelSpring(%i, %spring);
      %obj.setWheelPowered(%i, true);
      %obj.setWheelSteering(%i, false);
   }
   
   //Now we mount the Turrent and Gun
   %obj.mountImage(TurrentGunImage, 2);
   %obj.mountable = true;

   //hideCrossHair();
   //%obj.setInventory(TankSecondaryWeapon, 1);
   //%obj.setInventory(TankSecondaryWeaponAmmo, 1000);
   //%obj.mountImage(TankSecondaryWeaponImage, 1);
}

Remember this is just an initial tweak/setup to get it moving :)
For this to work and to allow controlling side speed by Hub
your hubs have to be sequential.. (Left side x-xx) (right side y-yy).
#15
05/18/2011 (9:49 am)
Almost forgot the Tank Datablock itself :)

datablock WheeledVehicleData(ShermanTankVehicle)
{
   category             = "Vehicles";
   shapeFile            = "art/shapes/vehicles/Sherman/sherman_body.dts";
   emap                 = true;

   mountable            = true;
   numMountPoints       = 2;        //Actually 5 but 3 are for Parts/Guns 
   mountPose[0]         = sitting;
   mountPose[1]         = sitting;
   wheelCount           = 12;       //2Front / 2Rear / 6*2 Inner
   
   maxSteeringAngle     = 0.185;  // Maximum steering angle, should match animation
   tireEmitter          = TireEmitter; // All the tires use the same dust emitter

   // 3rd person camera settings
   cameraRoll           = true;         // Roll the camera with the vehicle
   cameraMaxDist        = 4.8;         // Far distance from vehicle
   cameraOffset         = 1.5;        // Vertical offset from camera mount point
   cameraLag            = 0.26;           // Velocity lag of camera
   cameraDecay          = 1.25;        // Decay per sec. rate of velocity lag

   // Rigid Body
   mass                 = 800;
   massCenter           = "0 -0.1 0";    // Center of mass for rigid body
   massBox              = "0 0 0";         // Size of box used for moment of inertia,
                              // if zero it defaults to object bounding box
   drag                 = 0.9;                // Drag coefficient
   bodyFriction         = 0.8;
   bodyRestitution      = 0.5;
   minImpactSpeed       = 5;        // Impacts over this invoke the script callback
   softImpactSpeed      = 5;       // Play SoftImpact Sound
   hardImpactSpeed      = 15;      // Play HardImpact Sound
   integration          = 8;           // Physics integration: TickSec/Rate
   collisionTol         = 0.1;        // Collision distance tolerance
   contactTol           = 0.1;          // Contact velocity tolerance

   // Engine
   engineTorque         = 3000;       // Engine power
   engineBrake          = 1000;         // Braking when throttle is 0
   brakeTorque          = 8000;        // When brakes are applied
   maxWheelSpeed        = 50;        // Engine scale by current speed / max speed

   // Energy
   maxEnergy            = 100;

   // Sounds
   engineSound         = ShermanEngineSound;
//   engineSoundB         = ShermanEngineSound;
//   softImpactSound    = SoftImpactSound;
//   hardImpactSound    = HardImpactSound;
//   wheelImpactSound   = WheelImpactSound;

//   explosion          = VehicleExplosion;

   // Dynamic fields accessed via script
   nameTag              = 'ShermanTank';
   maxDismountSpeed     = 10;
   maxMountSpeed        = 5;

   impactForce          = 20;
};
#16
05/18/2011 (9:57 am)
The WheeledVehicle class is limited to [ 8 ] wheels
in the header, you have to increase this value to get it to work.
All this means is that it will LOOK for as many wheels as you
tell it can be in a shape...

wheeledvehicle.h
(modified)

//----------------------------------------------------------------------------
struct WheeledVehicleData: public VehicleData 
{
   typedef VehicleData Parent;

   enum Constants 
   {
      MaxWheels = 22,     // <-- Mythic Modified
      MaxWheelBits = 3
   };
......


#17
05/18/2011 (10:57 am)
As for the beginings of animating the Tank Treads, Each tread will
need a seperate material mapped to it so we can control each
one (Animated Material) seperately... At the moment heres a start:

add a materials.cs of the following with both treads mapped to it
in your dts file.. this just shows the starting point...

singleton Material(tankTread_anim_mat)
{
   mapTo = "tread07";
   diffuseMap[0] = "art/shapes/vehicles/Sherman/tread07";
   animFlags[0] = "0x00000001";
   scrollSpeed[0] = "0.5";
   pixelSpecular[0] = "0";
   scrollDir[0] = "0 -1";
   sequenceFramePerSec[0] = "6";
   sequenceSegmentSize[0] = "0.166667";
   emissive[0] = "1";
};

Keep track of the Material name as we can access that in c++
possibly even script to control it's properties when the tank
moves...
Or at least we should be able to modify those properties...
Havent checked yet.

(edit)
Will need to access the 3 parts of the material:
animFlags[0] = "0x00000001";
scrollSpeed[0] = "0.5";
scrollDir[0] = "0 -1";

animFlags[0] = "0x00000001"; On/Off (Moving/Stopped) :)

scrollDir 0 -1 for forward
scrollDir 0 1 for reverse

scrollSpeed[0] = "0.5"; (Modify with current speed of tank)
#18
05/18/2011 (10:59 am)
I really should have made a seperate resource for this I guess..
Been working too hard this week :)
Sorry about hijacking the thread.