Game Development Community

Terrain texture properties

by Lidwine Simon · in Torque Game Engine · 06/05/2004 (11:26 am) · 19 replies

Hi,

I was searching how to add properties to the textures (to decrease maxWheelSpeed on sand for example), and it seems the infrastructure was already there but not used. I don't know if it was a good idea to use it (maybe some old hack?), but with some code it works. If it may help someone, I post it :

in terrain/terrData.cc, in terrainBlock::onAdd(), replace
StringTableEntry fn = mMaterialFileName[0];

with

for (int i=0; i<TerrainBlock::MaterialGroups; i++)
{								
   //StringTableEntry fn = mMaterialFileName[0];
   StringTableEntry fn = mMaterialFileName[i];
   if (fn != NULL)
   {

and replace
mMPMIndex[0] = pMatMap->getIndexFromName( fn );

with
mMPMIndex[i] = pMatMap->getIndexFromName( fn );
   }
   else
     mMPMIndex[i] = -1;
}
So you get the index of the textures and their properties defined in the file ~/data/terrain/propertyMap.cs
I add a "speed" property for the sand in this file :
addMaterialMapping( "racing/data/terrains/highplains/grass" , "sound: 0" , "color: 0.46 0.36 0.26 0.4 0.0" ); 
addMaterialMapping( "racing/data/terrains/highplains/sand" , [b]"speed:15"[/b], "sound: 0" , "color: 0.46 0.36 0.26 0.4 0.0" );

To add this new parameter, in dgl/materialPropertyMap.h, after the line
ColorF            puffColor[2];
add
F32	speed;

To access this new parameter, in dgl/materialPropertyMap.cc, in MaterialPropertyMap::addMapping(..), after
pEntry->matFlags       = 0;
add
pEntry->speed       = 40;

and after
pEntry->detailMapName = StringTable->insert(pColon);
    }
add
else if (dStrnicmp(param, "speed:", dStrlen("speed:")) == 0) {
         // Set the detail map
         const char* pSpeed = dStrchr(param, ':');
         pSpeed++;
         while (*pSpeed == ' ' || *pSpeed == '\t')
            pSpeed++;

         pEntry->speed = dAtof(pSpeed);
      }

Now, to get the texture and its properties under the car, in game/vehicle/wheeledVehicle.cc, add
S32 WheeledVehicle::getTerrainMapIndex(float distance, Point3F& pt)
{
	RayInfo rInfo;
	U8 alphas[TerrainBlock::MaterialGroups];
	int mapIndex = -1;

    if (gClientContainer.castRay(pt, 
            Point3F(pt.x, pt.y, pt.z - distance ),
            TerrainObjectType, &rInfo)) {
         
		TerrainBlock* ter = static_cast<TerrainBlock*>(rInfo.object);
		Point2I gPos;
		const MatrixF & mat = ter->getTransform();
		Point3F origin;
		mat.getColumn(3, &origin);
		F32 squareSize = (F32) ter->getSquareSize();
		F32 halfSquareSize = squareSize / 2;

		float x = (pt.x - origin.x + halfSquareSize) / squareSize;
		float y = (pt.y - origin.y + halfSquareSize) / squareSize;

		ter->getMaterialAlpha(Point2I(x,y), alphas);

		MaterialPropertyMap* matMap = static_cast<MaterialPropertyMap*>(Sim::findObject("MaterialPropertyMap"));
		if (matMap != NULL)
		{
			U8 maxAlpha = alphas[0];
			U8 maxAlpha2;
			int indexTexture = 0;
			
			for (int i=1; i<TerrainBlock::MaterialGroups; i++)
			{
				maxAlpha2 = getMax(alphas[i],maxAlpha);					
				if (maxAlpha2 != maxAlpha)
				{
					indexTexture = i;
					maxAlpha = maxAlpha2;
				}
			}
			
			mapIndex = ter->mMPMIndex[indexTexture];
			
		}
		
	}
	
	return mapIndex;
}
(Thanks for the getTerrainTexture function!)

and you can use it with code like
MaterialPropertyMap* matMap = static_cast<MaterialPropertyMap*>(Sim::findObject("MaterialPropertyMap"));
S32 mapIndex = getTerrainMapIndex(2.0f,rInfo2.point);
if (mapIndex != -1)
{
   const MaterialPropertyMap::MapEntry *pEntry = matMap->getMapEntryFromIndex(mapIndex);
   if (pEntry != NULL)
   {
      F32 new_speed = pEntry->speed;
      .......
   }
}

I m not proud of this code, seems like a hack, but it works...

sorry for bad english!

#1
06/05/2004 (11:39 am)
Very nice! We plan on something pretty much exactly the same for squads moving through poor terrain, and this is quite a head start!
#2
06/05/2004 (12:29 pm)
I'll test this code tonight... but you may have just fixed the footpuff color problem that has been around for 2 years
#3
06/05/2004 (6:41 pm)
Wow Super Great :)
I got the materialmapping working with the player .
Really great Lidwine i cant thank you enoth for this,i think i have to buy you a beer Later :):)
#4
06/05/2004 (10:44 pm)
OK made some changes to kick this over to being more general.

Below are the changes:


In terrain/terrData.cc, in terrainBlock::onAdd(), replace

StringTableEntry fn = mMaterialFileName[0];

with

for (int i = 0; i < TerrainBlock::MaterialGroups; i++)
   {
	   StringTableEntry fn = mMaterialFileName[i];
	   if (fn != NULL)
	   {

and replace

mMPMIndex[0] = pMatMap->getIndexFromName( fn );

with

mMPMIndex[i] = pMatMap->getIndexFromName( fn );
	   }
	   else
		   mMPMIndex[i] = -1;
   }


Now the big change is in the getTerrainMapIndex function, we need a generic function that can be used anywhere. So what we do is this:

in terrData.h add the function declaration:

before:

void setBaseMaterial(U32 x, U32 y, U8 matGroup);

   U8 *getMaterialAlphaMap(U32 matIndex);

After adding the function declaration
void setBaseMaterial(U32 x, U32 y, U8 matGroup);

   S32 getTerrainMapIndex(Point3F& pt);
   U8 *getMaterialAlphaMap(U32 matIndex);

Now in terrData.cc we need to add the following function:

S32 TerrainBlock::getTerrainMapIndex(Point3F& pt)
{
	U8 alphas[MaterialGroups];
	int mapIndex;

	const MatrixF & mat = getTransform();
	Point3F origin;
	mat.getColumn(3, &origin);
	F32 squareSize = (F32) getSquareSize();
	F32 halfSquareSize = squareSize / 2;
	float x = (pt.x - origin.x + halfSquareSize) / squareSize;
	float y = (pt.y - origin.y + halfSquareSize) / squareSize;
	getMaterialAlpha(Point2I(x,y), alphas);
	MaterialPropertyMap* matMap = static_cast<MaterialPropertyMap*>(Sim::findObject("MaterialPropertyMap"));
	if (matMap != NULL)
	{         
		U8 maxAlpha = alphas[0];         
		U8 maxAlpha2;         
		int indexTexture = 0;                  
		for (int i=1; i<MaterialGroups; i++)
		{
			maxAlpha2 = getMax(alphas[i],maxAlpha);
			if (maxAlpha2 != maxAlpha)
			{
				indexTexture = i;
				maxAlpha = maxAlpha2;
			} 
		} 
		mapIndex = mMPMIndex[indexTexture];
	} 
	return mapIndex;
}


Usage will depend on the class you're using this with.

For example with the player you might do something like:
Point3F rot, pos;
    RayInfo rInfo;
    MatrixF mat = getRenderTransform();
    mat.getColumn(1, &rot);
    mat.mulP(Point3F(offset,0.0f,0.0f), &pos);
    if (gClientContainer.castRay(Point3F(pos.x, pos.y, pos.z + 0.01f),
       Point3F(pos.x, pos.y, pos.z - 2.0f ),
       TerrainObjectType, &rInfo))
    {
       if( rInfo.object->getTypeMask() & TerrainObjectType)
       {
          TerrainBlock* tBlock = static_cast<TerrainBlock*>(rInfo.object);
		
		S32 mapIndex = tBlock->getTerrainMapIndex(rInfo.point);
          
          if (mapIndex != -1)
          {
             MaterialPropertyMap* pMatMap = static_cast<MaterialPropertyMap*>(Sim::findObject("MaterialPropertyMap"));
             const MaterialPropertyMap::MapEntry* pEntry = pMatMap->getMapEntryFromIndex(mapIndex);
             if(pEntry)
             {
             ..........................
             }
          }
       }
    }
#5
06/06/2004 (1:54 am)
Nice work Harold
I was to tired to write anything.
#6
06/06/2004 (1:59 am)
Post as resource please and you get a 5 from me!

Very nice work - this will work out fine on stuff I'm working on
#7
06/06/2004 (1:27 pm)
Thanks Harold, pretty much better like that.
a guiness please Billy :)
#8
06/14/2004 (11:36 am)
Added update to allow for arbitrary terrain properties to head...
#9
08/13/2004 (10:20 am)
Can this be used to change player/vehicle movement speeds over different types of terrain?? :)

Also, the SceneImmerse engine had the capacity to make different footfall sounds depending on terrain, TERRAIN_SAND, TERRAIN_METAL etc. This could do that too, no?
#10
08/13/2004 (1:00 pm)
Yes to both. I think Tribes 2 did have code to do the latter feature; not sure it made it into the current incarnation of Torque, though.
#11
08/13/2004 (4:35 pm)
@Andrew
The footfall sounds & puffcolors work great after this change !
#12
08/16/2004 (7:14 pm)
Incidentlally, I've seen these in a player block:
// Footstep Sounds
FootSoftSound = FootLightSoftSound;
FootHardSound = FootLightHardSound;
FootMetalSound = FootLightMetalSound;
FootSnowSound = FootLightSnowSound;
FootShallowSound = FootLightShallowSplashSound;
FootWadingSound = FootLightWadingSound;
FootUnderwaterSound = FootLightUnderwaterSound;
That is sound from texture, isn't it? It's in the .cs but I can't find any link or chain through to actual code. Is it a lost Tribes2 feature maybe?
#13
08/17/2004 (12:05 pm)
@Andrew
You set what sounds playing in propertymap.cs
like this
addMaterialMapping("texture" , "sound:0" ," color:0.46 0.46 0.46 0.4 0.0 ");

FootSoftSound = sound: 0
FootHardSound = sound: 1
..
#14
10/26/2005 (12:31 pm)
This code is in Torque. (Resolving issue #16.)
#15
10/26/2005 (12:46 pm)
I've been doing something like this in my game, but for more properties.

One thing I plan to change about it is to add some sort of material datablock, similar to what TSE does I think, then map that material datablock to the texture. That way, if you have multiple rock textures, but they should all have the same sorts of properties, sounds footpuffs etc, you'd only have to change the values in one spot when tweaking later on.

so it would be something more like
datablock MaterialDataBlock(MaterialRock)
{
    sound = blahprofile;
    color = "blahblah";
    slipperiness = infinity;
    otherproperty = othervalue;
};

addMaterialMapping("rock1", MaterialRock);
addMaterialMapping("rock2", MaterialRock);
addMaterialMapping("rock3", MaterialRock);
addMaterialMapping("rock4", MaterialRock);
#16
11/19/2005 (7:08 pm)
My propertymap.cs uses full paths to distinguish terrain textures of the same name from each other.

i.e.
addMaterialMapping( "~/data/terrains/grassland/sand" , "sound: 0" , "color: 1.0 0.50 0.16 0.8 0.0" );

Under these circumstances the getIndexFromName does not locate the index because the
bool TerrainBlock::onAdd() strips the path off.

To fix, modify the bool TerrainBlock::onAdd()
for (int i = 0; i < TerrainBlock::MaterialGroups; i++)
as follows
for (int i = 0; i < TerrainBlock::MaterialGroups; i++)
   {                        
      StringTableEntry fn = mMaterialFileName[i];
      if (fn != NULL)
      {   
    	 if(!dStrncmp(fn, "terrain.", 8))
            fn += 8;

         char nameBuff[512];
		 /*
         dStrcpy(nameBuff, mTerrFileName);
		 const char *p = dStrrchr(fn, '/');
		 if(p) p++;
		 else p = nameBuff;

		 mMPMIndex[i] = pMatMap->getIndexFromName( p );
		 */
		 //duncan auto-Foliage mod - getIndexFromName requires the whole path - "~/data/terrains/grassland/sand"
		 nameBuff[0] = '~';
		 const char *p = dStrchr(fn, '/');
		 if(p)dStrcpy(&nameBuff[1], p);
		 else dStrcpy(nameBuff,fn);
		 mMPMIndex[i] = pMatMap->getIndexFromName( nameBuff ); 
      }
      else mMPMIndex[i] = -1;
   }
#17
02/08/2006 (9:28 am)
I noticed something odd about propertyMap.cs and the textures/mission/terrain files.

Running the stock demo, I 'thought' I had footstep sounds playing. I added in the Tim A. Environment Pack I and it had some textures with common names. I believe "grass" is the culprit here/now for this example. It overwrote my default texture in that mission and that seemed to break the functionality, while the other mission files I maintain in my assets it didn't. Fine, great, I expect that. What's odd is that even if the mission doesn't contain any of the textures with the addMaterialMapping properties in it, the sounds play. My example for this is the BraveTree Environment Pack I, Plateau mission. I get footstep sounds in that mission, without any of the textures in the Terrain Texture Editor having been reference in the addMaterialMapping properties file???? And to fix my Stronghold.mis file I had to use full paths to the textures!!??!! I don't understand textures and the Editor/MissionTree structure.

And what is the difference between the textures in the Terrain Texture Editor and the Terrain Texture Painter??? I find having two GUI's a bit confusing[v1.3], and don't understand fully how they function. I noticed that I had paths that were 'bad'/nonexistent and the textures seemed to be in the mission, while I've had other textures not be in the right folder and they appear as grey space on the terrain...it seemed to be dependent on which Editor they appear in?? Not entire sure about that...

Also, on the Editor/Creator: I notice that when I invoke the Mission Editor, it compiles and loads some files and then I always get a warning about the "grass" texture not being found?? Even though it's in the right location??? Is this a hard coded C++ thing??

some of my MissionEditor/Creator questions about texturing...
Thanks, sorry to go off topic a little...
Rex
#18
03/02/2006 (2:49 pm)
Are these updates in TGE 1.4?
#19
03/02/2006 (4:45 pm)
The stuff prior to Ben's post is in TGE1.4