Game Development Community

Plastic Gem #34: TGEA Decals

by Anthony Rosenbaum · 07/28/2008 (6:35 am) · 13 comments

Download Code File


i936.photobucket.com/albums/ad202/vincismurf/banner.jpg


TGEA Decals

You might have noticed that the latest incarnation of TGEA 1.7 does not have decals and the decal manager activated. This resource will show you how to turn the decals back on, by the end you will be able to restore footprints and bullets holes, included in stock TGEA.



In sourcesceneGraphdecalManager.h after the end brace of the DecalData class add

1) Expose DecalData to the console.

// > pg decals
DECLARE_CONSOLETYPE(DecalData)
// < pg decals


2) Impliment the DecalData on the console

In sourcesceneGraphdecalManager.cpp after the bool DecalData::preload(bool server, char errorBuffer[256]) function ad

// > pg decals
IMPLEMENT_CONSOLETYPE(DecalData)
IMPLEMENT_SETDATATYPE(DecalData)
IMPLEMENT_GETDATATYPE(DecalData)
 // < pg decals

3) Add DecalData to the Core Datatypes

In consoleconsoleTypes.h after class ParticleEmitterData; ADD
class DecalData;


AND after DefineConsoleType( TypeParticleEmitterDataPtr, ParticleEmitterData* ); ADD
// > pg decals
DefineConsoleType( TypeDecalDataPtr, DecalData*);
// < pg decals


Now we have to uncomment code to turn the decals on players and add code to projectiles
.
4) Load the images into the datablock.

In T3D/player.cpp
In bool PlayerData::preload(bool server, char errorBuffer[256]) Un comment out
// > pg decals
   if (!decalData && decalID != 0 )
      if (!Sim::findObject(decalID, decalData))
         Con::errorf(ConsoleLogEntry::General, "PlayerData::preload Invalid packet, bad datablockId(decalData): 0x%x", decalID);
// < pg decals

5) Expose the field for the footstep decals.

in void PlayerData::initPersistFields() uncomment
// > pg decals
addField("decalData",         TypeDecalDataPtr, Offset(decalData, PlayerData));
// < pg decals

6) Make the decals supported by the network

in void PlayerData::packData(BitStream* stream) uncomment
// > pg decals
   if( stream->writeFlag( decalData ) )
   {
      stream->writeRangedU32( decalData->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
   }
   stream->write(decalOffset);
// < pg decals


in void PlayerData::unpackData(BitStream* stream)in comment
// > pg decals

   if( stream->readFlag() )
   {
      decalID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
   }
   stream->read(&decalOffset);
// < pg decals


Walla you will have foot steps if decalData is set on the playerData.

Now lets get us some bullet holes for the projectiles. This bullet hole code is a straight port from TGE.

7) Forward associate the DecalData to the ProjectileData class.

In T3d/projectile.h after class TSThread;; add
// > pg decals
class DecalData;
// < pg decals


8) Declare some variables to hold the decal data.

Within the ProjectileData class Add

// > pg decals
   enum DecalConstants {               // Number of decals constant
      NumDecals = 6,
   };
   DecalData* decals[NumDecals];       // Decal Datablocks
   S32 decalId[NumDecals];             // Decal IDs
   U32 decalCount;   
   // < pg decals

9) Tell the compiler where to find the decalManager.

In T3d/projectile.cpp after the includes add
// > pg decals
#include "sceneGraph/decalManager.h"
// < pg decals

10) Initialize the new decal variables.

In ProjectileData::ProjectileData() at the bottom add
// > pg decals
   decalCount = 0;
   for (U32 i = 0; i < NumDecals; i++)
   {
      decals[i] = NULL;
      decalId[i] = 0;
   }
   // < pg deacls


11) Expose the decal field in the datablock to the console.

In void ProjectileData::initPersistFields() at the bottom add

// > pg decals
addField("decals", TypeDecalDataPtr, Offset(decals, ProjectileData), NumDecals);
   // < pg decals

12) Load the decals if there are specified in the datablock.

In bool ProjectileData::preload(bool server, char errorBuffer[256]) before return true; add
// > pg decals
   // load up all the supplied decal datablocks
   //  move non-null ones to the front of the array
   //  for our random decal picker later
   U32 i;
   DecalData *tmpDecals[NumDecals];
   for (i = 0; i < NumDecals; i++)
   {
      tmpDecals[i] = NULL;
      if(!decals[i] && decalId[i] != 0)
         if(!Sim::findObject(decalId[i], decals[i]))
            Con::errorf( ConsoleLogEntry::General, "ProjectileData::preload Invalid packet, bad datablockId(decals): 0x%x", decalId[i]);

      if (!server && decals[i])
      {
         tmpDecals[decalCount] = decals[i];
         decalCount++;
      }
   }
   if (!server && decalCount > 0)
      for (i = 0; i < NumDecals; i++)
         decals[i] = tmpDecals[i];
   // < pg decals

13) Send the decal information over the network.

In void ProjectileData::packData(BitStream* stream) before if(stream->writeFlag(hasLight)) ADD
// > pg decals 
   for (U32 i = 0; i < NumDecals; i++)
      if (stream->writeFlag(decals[i] != NULL))
         stream->writeRangedU32(decals[i]->getId(), DataBlockObjectIdFirst,
		                                                  DataBlockObjectIdLast);   
   // < pg decals


In void ProjectileData::unpackData(BitStream* stream) before hasLight = stream->readFlag(); ADD
// > pg decals
   for (U32 i = 0; i < NumDecals; i++)
      if (stream->readFlag())
         decalId[i] = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
// < pg decals


14) Render the decals when the the projectiles explodes.

In void Projectile::explode(const Point3F& p, const Point3F& n, const U32 collideType) after the if (pExplosion->registerObject() == false) block add

// > pg decal
         if(mDataBlock->decalCount > 0)
         {
            if(collideType & (TerrainObjectType | InteriorObjectType))
            {
               // randomly choose a decal between 0 and (decal count - 1)
               U32 idx = (U32)(mCeil(mDataBlock->decalCount * Platform::getRandom()) - 1.0f);

               // this should never choose a NULL idx, but check anyway
               if(mDataBlock->decals[idx] != NULL)
               {
                  DecalManager *decalMngr = gClientSceneGraph->getCurrentDecalManager();
                  if(decalMngr)
                     decalMngr->addDecal(p, n, mDataBlock->decals[idx]);
               }
            }
         }
		 // < decal pg

You'll need to be sure to make decal Datablocks and associate them to the playerData and projectileData like so:

In YOURGAMENAME/server/scripts/player.cs somewhere before PlayerData
// > decal pg
datablock DecalData(PlayerFootprint)
{
   sizeX       = 0.25;
   sizeY       = 0.25;
   textureName = "~/data/shapes/player/footprint"; // use a texture you have
};
// < decal pg

In PlayerData ADD
// > decal pg
   // Foot Prints
   decalData   = PlayerFootprint;
   decalOffset = 0.25; 
// < decal pg

In YOURGAMENAME/server/scripts/crossbow.cs somewhere before ProjectileData
datablock DecalData(crossbowHole0){   
   sizeX       = 0.03;   
   sizeY       = 0.03;   
   textureName = "~/data/shapes/particles/bullethole"; // use a texture you have
};
datablock DecalData(crossbowHole2: crossbowHole0){   
   textureName = "~/data/shapes/particles/bullethole1";
};
datablock DecalData(crossbowHole3: crossbowHole0){   
   textureName = "~/data/shapes/particles/bullethole2";
};
datablock DecalData(crossbowHole4: crossbowHole0){   
   textureName = "~/data/shapes/particles/bullethole3";
};
datablock DecalData(crossbowHole5: crossbowHole0){   
   textureName = "~/data/shapes/particles/bullethole4";
};
In the ProjectileData Add
decals[0] = crossbowHole0;   
   decals[1] = crossbowHole1;   
   decals[2] = crossbowHole2;   
   decals[3] = crossbowHole3;   
   decals[4] = crossbowHole4;   
   decals[5] = crossbowHole5;

Enjoy!

#1
07/28/2008 (1:11 pm)
Thanks for the resource! I went through it under 1.7.1, and found a few things:

At step 7 class WindEmitter is not used in TGEA, might want to make that class TSThread.

Then you get some compiler errors, so to fix these, in ConsoleTypes.h after class ParticleEmitterData; add

class DecalData;

Then, still a number of errors coming up. To fix these, comment out the line added in step 1. We already have a DECLARE_CONOBJECT(DecalData); above the new declaration, which is probably good enough, so:

DECLARE_CONOBJECT(DecalData);
   static void initPersistFields();

   // > pg decals
[b]//[/b]   DECLARE_CONSOLETYPE(DecalData);
   // < pg decals

Then it works great!
#2
07/28/2008 (8:31 pm)
Very useful. Something TGEA has needed stock for a long time.
#3
07/29/2008 (5:06 am)
@Konrad thanks for the input, the resource and zip reflct your changes
#4
11/30/2008 (11:21 am)
Hi! I compile the project fine exept i got a little problem.
Each time i enter a game, and shoot, it crash...
In debug mode::: it says :::

Projectile(CrossbowProjectile)::Explode : couldn't register explosion
FATAL ... /source/scenegraph/sceneobject.cpp @ 382 Error, Sceneobject not properly removed from sceneGraph.

In my crossbow script, in the end, it shows the MissionCleanup group routine so i don't think it's a script
problem but in the engine side. I must do something wrong in the step 14, Render the decals when the projectiles explode. That's probably this. I'm gonna check the code again... Please, if someone can point me to the right direction it will be great.

thank you very much!
#5
01/03/2009 (7:06 am)
This doesn't work for Atlas terrains. Do you guys now how to make it work with Atlas terrains?
#6
01/06/2009 (5:36 pm)
Vasily,

In decalManager.cpp, change:

U32 DecalManager::smDecalMask = TerrainObjectType | InteriorObjectType | StaticObjectType;

to:

U32 DecalManager::smDecalMask = TerrainObjectType | InteriorObjectType | StaticObjectType | AtlasObjectType;
#7
01/07/2009 (4:49 am)
Oh, thanks Adam. I've probably missed that line when I tried to figure out myself. Thanks, I'll try it out.
#8
01/07/2009 (5:00 am)
No, I haven't missed that. In scenegraph/decalManager.cpp there's not smDecalMask section... I'm using TGEA 1.7.1 + AFX if that matters. So, it doesn't work stil... :-(
#9
01/07/2009 (7:45 am)
Vasily - perhaps the decal mask is something we ADDED for this resource? So you have to infact ADD the line indicated by Adam? The gist is that somewhere the decal code needs to know what sort of objects to collide with and that is what this variable is for.
#10
01/07/2009 (11:29 am)
Thanks for your suggestion, Paul. I tried it, but without success. May be you could tell me which line should it be? Thanks in advance.
#11
03/11/2009 (1:02 pm)
im having a problem where it only shows the bullet holes on the ground. not on any objects, interiors, etc
#12
03/11/2009 (7:09 pm)
figured it out. the function to change is in projectile.cpp.

in void Projectile::explode()

make it:
...
if(collideType & (TerrainObjectType | InteriorObjectType | StaticObjectType | AtlasObjectType))
...
#13
09/18/2009 (6:12 am)
I`m having the same problem as Vasily, the decals don`t work on Atlas terrains, and the line of code that Adam posted for decalManager.cpp doesn`t exist anywhere in 1.8.1 (or 1.7.1 as far I can tell). Anyone have a solution?