Game Development Community

Changing terrain textures during gameplay

by CEMA, Monash University · in Torque Game Engine · 10/11/2005 (6:48 pm) · 11 replies

How do I programmatically change the terrain texture? More precisely, I want to change the terrain during gameplay, in response to user actions, game events, etc.

Can TorqueScript do this?

My approach at the moment is to read the Terrain Editor C++ code and figure out what is going on from there. That should get me where I want to go, but is there be a faster way?

#1
10/11/2005 (6:51 pm)
This isn't trivial for several reasons (lighting being a primary one, specifically baked in shadows), and certainly cannot be done out of the box in script.
#2
10/11/2005 (10:25 pm)
Thanks Stephen.

A little bit of background:

I am creating a simulation of simple ant-like critters which are supposed to move around on a large 2d, depositing and moving materials (such as sand, stone, etc.), as they go. The point of this is for the user to experiment with the critters' rules, and hopefully make something interesting.

The representation of the materials on each grid cell can be extremely simple. What I initially had in mind was to simply have a texture for each material, and change the texture on the terrain as the critters do their stuff. The user will be viewing this from high above the grid, so the representation for each cell can be very simple.

I am very much the noob with Torque (and game dev in general), so any pointers to get started here would be great.

How does Torque do the footprints? Is this technique useful here?

Is my problem any easier if I have a pre-determined, small number of textures, and Torque simply blends between them with differing amounts?
#3
10/11/2005 (11:04 pm)
Have you looked at Paul Dana's game, BitShifter? It does what you want, I think. Maybe asking Paul nicely how he did it would be a good start.
#4
10/12/2005 (7:36 am)
Also, as long as the game stays single player, and baked in shadows isn't something that you are really concerned with (and/or you use the Synapse Lighting Pack and accept the minor shadow issues you will still have), you may also be able to adapt some of the terrain texture painter code to be available to your ants (NOT your player, your ants!) in some form...that might at least let you prototype the effect enough to see if it's worth polishing up.
#5
10/12/2005 (8:47 am)
Martin, I'm working on this terrain texture thing too. I havent had allot of time to play with it yet, but it does look fairly simple. I know shadows would be messed up though. Anyone know if this would be eaiser to do in TSE?
#6
10/12/2005 (8:58 am)
I did this a while back in the RTS Kit engine (C++ not script). You won't be able to cut/paste unless you're an RTS Kit owner but the concept should work in regular TGE.

What it does is change the alpha blending level on the terrain square to let the "road" dirt texture shine more than the grass texture.

Inside RTSUnit::processTick after this section but inside the if(block):

// Move us to sit on the ground...
   TerrainBlock* block = NULL;
   if (isClientObject() && gClientSceneGraph)
      block = gClientSceneGraph->getCurrentTerrain();
   else if (isServerObject() && gServerSceneGraph)
      block = gServerSceneGraph->getCurrentTerrain();

   if(block)
   {

I added this:

// JWL
If (!isGhost()) Then
{
    if(mMoveState == ModeMove)
    {
        Point2I gPos;
        block->worldToGrid(location, gPos);

        if (mGridPos != gPos)
        {
            setGridPosition(gPos);

            U8 *alphas;
            alphas = new U8[TerrainBlock::MaterialGroups];

            const MatrixF & mat = block->getTransform();
            Point3F origin;
            mat.getColumn(3, &origin);
            F32 squareSize = (F32) block->getSquareSize();
            F32 halfSquareSize = squareSize / 2;

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

            block->getMaterialAlpha(Point2I(x,y), alphas);
            for (int i=0; i < TerrainBlock::MaterialGroups; i++)
            {
                Con::printf("Before Alpha Swap: %u = %u", i, alphas[i]);
            }

            // Increase the road texture intensity
            int roadMat = 5;
            int roadWear = 50;
            U32 dAmt = (U32)(alphas[roadMat] + roadWear);
                        If (dAmt > 255) Then
            dAmt = 255;

            if(alphas[roadMat] < dAmt)
            {
                alphas[roadMat] = dAmt;
                U32 total = 0;

                for(S32 i = 0; i < TerrainBlock::MaterialGroups; i++)
                {
	                if(i != roadMat)
		                total += alphas[i];
                }
                if(total != 0)
                {
	                // gotta scale them down...

	                F32 scaleFactor = (255 - dAmt) / F32(total);
	                for(S32 i = 0; i < TerrainBlock::MaterialGroups; i++)
	                {
		                if(i != roadMat)
			                alphas[i] = (U8)(alphas[i] * scaleFactor);
	                }
                }
            }
            block->setMaterialAlpha(gPos, alphas);
            block->updateGridMaterials(gPos, gPos);

            block->getMaterialAlpha(Point2I(x,y), alphas);
            for (int i=0; i < TerrainBlock::MaterialGroups; i++)
            {
                Con::printf("After Alpha Swap: %u = %u", i, alphas[i]);
            }
        }
    }
}
// JWL
#7
10/17/2005 (1:13 am)
Well examining PaintMaterialAction::process() and the code above got me far enough for now. They simply adjust the alpha-blending of the terrain's materials, which is good enough for me at the moment. (I understand there is a limit of 6 (or thereabouts) materials per terrain, which means I can't mix arbitrary textures just yet, but that's ok.)

Ben, BitShifter (now known as Flash Bios) does in fact do something quite like I wanted. Nice game, too.

Thanks,
Nick.

P.S. I recently changed the forum name on this account from Martin Taylor to 'Monash University.' I work there, but I ain't Martin.
#8
12/29/2006 (7:07 am)
I apologize for bumping such an old thread but I'm working on the same problem. I'm not an RTSKit owner but I've adapted Jeff Leigh's above code to suit my needs. It's been placed in my ObjectiveShape class which is essentially just a subclass of StaticShape with a few game-specific additions. The terrain texture changes appropriately in a singleplayer setting but it doesn't work at all in a multiplayer (dedicated server) environment.

Does anyone have any suggestions on making the above code work in a multiplayer setting?
#9
12/31/2006 (1:53 pm)
Here's some extra tidbits of information that might be useful to someone working on this problem:

I have two methods in my ObjectiveShape class: generateTerrTexAlpha() and updateTerrainTex(). generateTerrTexAlpha() simply generates random alpha values (within a specified range) for each of the 8 TerrainBlock MaterialGroups. updateTerrainTex() uses the generated alpha values to actually modify the texture within a certain radius around a position. In effect, it's essentially like using the 'Circle Brush' with a large radius and painting a texture onto the terrain from within the editor, except done programmatically.

The problem, however, is that ObjectiveShape::updateTerrainTex() (and, consequently, generateTerrTexAlpha()) only seem to be called on the server. I verified this by adding in some simple if(isClientObject()){Con::errorf();} checks and nothing is ever printed to the console on a client machine.

Given this information, I really can't say that the above code doesn't "work" in a multiplayer setting because it's never actually being called. Therefore, I suppose my question is really: why aren't these methods called on both the client and server and how can I get them to do so?

Secondly, would it be better to add these methods to the TerrainBlock class and simply change updateTerrainTex() to accept a position? The only reason it was added into the ObjectiveClass was because this effect is designed to occur around objectives and it made grabbing the objective's position easier.
#10
01/01/2007 (4:18 am)
You'll have to pass some state indication in packUpdate/unpackUpdate or in a NetEvent if you want the client to know when to do those paints. (Or you could just add some dummy object that always paints its surroundings, and ghost that.)
#11
01/01/2007 (9:44 am)
D'oh! Thanks Ben. I remembered adding in the state change events but apparently I added them into an older copy of the code and hadn't merged the changes into the latest version. Late night stupor strikes again!

Anyway, the code works like a charm now. There's still quite a bit of cleanup to do but it's a great effect.