Game Development Community

dev|Pro Game Development Curriculum

Control Terrain Repeating through World Editor

by John Vanderbeck · 04/17/2004 (1:18 pm) · 35 comments

Download Code File

This MPG Video shows how this works in the world editor. Essentially it adds a new property that you can toggle to control whether or not the terrain is repeated endlessely when rendered. The change takes place immediately after hitting APPLY so you don't have to reload the mission. When the terrain is set to NOT repeat endlessley, then there will be water covering the rest of the world endlessly. I guess I could have fixed that as well, but I didin't look into it. Seemed to me that you need "something" else out there if you don't repeat the terrain.

Applying this is pretty simple, and there are two ways you can do it. Via a patch or by hand.

Applying the patch
Note: The patch was built from a HEAD checkout 4/17/04

Applying the patch should be realtivly easy if your version of Torque is close to or the same as the one I used to build the patch. You can find instructions on applying patches [url=http://www.garagegames.com/docs/torque.sdk/gstarted/patches.html]here[/url. That page deals with both making and applying patches, so just ignore the parts that cover making them.

Applying the code by hand
Applying the code by hand is pretty easy, as there are only 3 files you have to touch. All code added for this is commented cleaerly. The code below will contain some of the original code before and after the changed/added code for context. This was applied to a HEAD version from about the end of March so there may be slight differences.

1) torque/engine/terrain/terrData.h
We need a variable to control our repeating option, so we add a new member variable to the TerrainBlock class.
U32   mTextureCallbackKey;
   void  processTextureEvent(const U32 eventCode);

   S32 mMPMIndex[10];

   S32 mVertexBuffer;
// JWV BEGIN added for control over terrain repeating
   bool mRepeatTerrain;
// JWV END added for control over terrain repeating
  private:
   Resource<TerrainFile> mFile;
   GridSquare *gridMap[BlockShift+1];
   GridChunk *mChunkMap;
   U32   mCRC;
   
  public:

2) torque/engine/terrain/terrData.cc
Now that we have the new variable, we need to add the code to set that variable, as well as the code to properly transmit it over the network. There are 4 places where we add code in this file.

First, we just need to initialize a default value (We default to repeating the terrain) in the constructor.
mCRC = 0;
   flagMap = 0;
	mVertexBuffer = -1;
// JWV BEGIN added for control over epeating terrain
   mRepeatTerrain = true;
// JWV BEGIN added for control over epeating terrain
}

Next, we need to add our new variable to the initPersistantFields() method. This is what allows it to be controled via the editor.
void TerrainBlock::initPersistFields()
{
   Parent::initPersistFields();

   addGroup("Media");	// MM: Added Group Header.
   addField("detailTexture",     TypeFilename,  Offset(mDetailTextureName, TerrainBlock));
   addField("terrainFile",       TypeFilename,  Offset(mTerrFileName, TerrainBlock));
//CW - bump mapping stuff
	addField("bumpTexture",			TypeFilename,	Offset(mBumpTextureName, TerrainBlock));
//CW - end bump mapping stuff
   endGroup("Media");	// MM: Added Group Footer.

   addGroup("Misc");	// MM: Added Group Header.
   addField("squareSize",        TypeS32,       Offset(squareSize, TerrainBlock));
   addField("emptySquares",      TypeS32Vector, Offset(mEmptySquareRuns, TerrainBlock));
//CW - bump mapping stuff
	addField("bumpScale",			TypeF32,			Offset(mBumpScale, TerrainBlock));
	addField("bumpOffset",			TypeF32,			Offset(mBumpOffset, TerrainBlock));
	addField("zeroBumpScale",		TypeS32,			Offset(mZeroBumpScale, TerrainBlock));
//CW - end bump mapping stuff
// JWV BEGIN added for control of repeating terrain
   addField("repeatTerrain",     TypeBool,      Offset(mRepeatTerrain, TerrainBlock));
// JWV END added for control of repeating terrain
   endGroup("Misc");	// MM: Added Group Footer.

   removeField("position");
}

The TerrainBlock now knows about the new variable and it can be set, however we also need to add in the networking for this new variable. IF we don't then it will never get properly "ghosted". To do this we add in code to write the variable over the network and to read it from the network. This is done in packUpdate() and unpackUpdate()

First we'll take care of packUpdate(). This is where we write the variable to the network.
U32 TerrainBlock::packUpdate(NetConnection *, U32 mask, BitStream *stream)
{
   if(stream->writeFlag(mask & InitMask))
   {
      stream->write(mCRC);
      stream->writeString(mTerrFileName);
      stream->writeString(mDetailTextureName);
//CW - bump mapping stuff
		stream->writeString(mBumpTextureName);
		//don't have negative bump scale and don't have one too small
		if (mBumpScale <= 0)
		{
			Con::errorf("Bump scale cannot be less than or equal to 0.  Setting to 0.0001.");
			mBumpScale = 0.0001;
		}
		stream->write(mBumpScale);
		stream->write(mBumpOffset);
		stream->write(mZeroBumpScale);
//CW - end bump mapping stuff
      stream->write(squareSize);
// JWV BEGIN added for control over terrain repeating
      stream->write(mRepeatTerrain);
// JWV END added for control over terrain repeating

      // write out the empty rle vector
      stream->write(mEmptySquareRuns.size());
      for(U32 i = 0; i < mEmptySquareRuns.size(); i++)   
         stream->write(mEmptySquareRuns[i]);
   }
   else // normal update

And lastly, we add code to read our variable from the network in unpackUpdate()
void TerrainBlock::unpackUpdate(NetConnection *, BitStream *stream)
{
   if(stream->readFlag())  // init
   {
      stream->read(&mCRC);
      mTerrFileName = stream->readSTString();
      mDetailTextureName = stream->readSTString();
//CW - bump mapping stuff
		mBumpTextureName = stream->readSTString();
		stream->read(&mBumpScale);
		stream->read(&mBumpOffset);
		stream->read(&mZeroBumpScale);
//CW - end bump mapping stuff
      stream->read(&squareSize);
// JWV BEGIN added for control over terrain repeating
      stream->read(&mRepeatTerrain);
// JWV END added for control over terrain repeating
      
      // read in the empty rle
      U32 size;   
      stream->read(&size);
      mEmptySquareRuns.setSize(size);
      for(U32 i = 0; i < size; i++)
         stream->read(&mEmptySquareRuns[i]);
   }
   else // normal update

Now what we've got is a new variable in the TerrainBlock that can be altered through the world editor and is properly "ghosted" over the network interface. The last step is to actually use this new variable to control the rendering of our terrain.

3) torque/engine/terrain/terrRender.cc
What we want to do here is alter the renderBlock() method to control if we should render the whole thing, or just the main block.

mDynamicTextureCount = 0;
   mTextureSpaceUsed = 0;
   mUnusedTextureCount = 0;
   mStaticTextureCount = 0;
   mLevelZeroCount = 0;
   mFullMipCount = 0;
   mStaticTSU = 0;
   
   F32 worldToScreenScale   = dglProjectRadius(1,1);
   F32 zeroDetailDistance   = (mSquareSize * worldToScreenScale) / (1 << 6) - (mSquareSize >> 1);
	F32 zeroBumpDistance		 = (mSquareSize * worldToScreenScale) / (1 << mCurrentBlock->mZeroBumpScale) - (mSquareSize >> 1);
   
   F32 blockSize = mSquareSize * TerrainBlock::BlockSquareWidth;
// JWV BEGIN change to terrain rendering so as to not endlessley repeat the terrain
/* OLD CODE
   S32 xStart = (S32)mFloor( (mCamPos.x - mFarDistance) / blockSize );
   S32 xEnd   = (S32)mCeil ( (mCamPos.x + mFarDistance) / blockSize );
   S32 yStart = (S32)mFloor( (mCamPos.y - mFarDistance) / blockSize );
   S32 yEnd   = (S32)mCeil ( (mCamPos.y + mFarDistance) / blockSize );
   S32 xExt   = (S32)(xEnd - xStart);
   S32 yExt   = (S32)(yEnd - yStart);*/
   S32 xStart;
   S32 xEnd;
   S32 yStart;
   S32 yEnd;
   S32 xExt;
   S32 yExt;
   if (block->mRepeatTerrain)
   {
      xStart = (S32)mFloor( (mCamPos.x - mFarDistance) / blockSize );
      xEnd   = (S32)mCeil ( (mCamPos.x + mFarDistance) / blockSize );
      yStart = (S32)mFloor( (mCamPos.y - mFarDistance) / blockSize );
      yEnd   = (S32)mCeil ( (mCamPos.y + mFarDistance) / blockSize );
      xExt   = (S32)(xEnd - xStart);
      yExt   = (S32)(yEnd - yStart);
   }
   else
   {
      xStart = 0;
      xEnd = 1;
      yStart = 0;
      yEnd   = 1;
      xExt   = (S32)(xEnd - xStart);
      yExt   = (S32)(yEnd - yStart);
   }
// JWV END change to terrain rendering so as to not endlessley repeat the terrain
   
#if 0
   {
      SquareStackNode2 *st = (SquareStackNode2 *) 
         FrameAllocator::alloc(sizeof(SquareStackNode2) * 
                               (xExt * yExt + TerrainBlock::BlockShift*4) );
      for(S32 y = 0; y < yExt; y++)

That's all the changes. Re-compile Torque and you should be all set to go.

How to Control Terrain Repeating
The video linked at the top of this resource shows exactly how to control this in the world editor, but for those who don't want to download the video, or can't, here are the baiscs.
1) Open the World Editor.
2) Go to the Mission Inspector.
3) Select the terrain block.
4) Expand the "Misc" group.
5) The field "repeatTerrain" controls the repeating. Select (repat) or unselect (dont' repeat) the field and then click APPLY.
6) Save the mission.
Page«First 1 2 Next»
#21
03/01/2005 (2:58 pm)
Ummm what water are we talking about here?
I've applied the patch and now like that old country song... The world drops off sharp at the edge of town :(

I was under the impression that this somehow turned terrain objects into islands floating in an ocean of water or some such nonsense :(

Could this be modified to do that somehow?

Interesting note though, I placed a building just outside the stock terrain, then turned the repeating terrain off.

The building being ontop of a very large hill, just floated there. I moved my camera to player and was walking on thin air all the way up to the building.
#22
03/01/2005 (4:34 pm)
Terrain collision remains intact - your player will not fall off the edge.
If players falling to their doom is a feature you want, it would have to be turned off as well.
I prefer the repeating collision (less headaches), so I didn't really look into the option.
#23
06/28/2005 (4:17 pm)
This should be a standard feature in Torque period. I wonder why they didnt put this in. You helped me a lot man! Thanks!
#24
08/15/2005 (7:34 am)
If anyone's monitoring this...

I am trying to merge with the lighting pack, and Ive been having a hell of a time. It keeps crashing.

I have tracked it down ot this mod. Has anyone gottten this to work WITH the lighting pack? Did it require anything special ?
#25
08/23/2005 (12:14 pm)
Just a quick question. Wouldn't your changes in terrRender.cc be sufficient? Granted, you have made it it scriptable but aren't you then continually passing a variable over the network that never changes? Perhaps, I'm not properly understanding.

[edit]Nevermind. That was kind of a stupid question. Of course it would be sufficient but then you can't change it in the World Editor. However, I'm still wrapping my head around the network packets that get sent. Was that a stupid question as well?....argh.[/edit]
#26
08/29/2005 (11:33 am)
Hi Chris,

I recommend you incorporate the reapeating water changes AFTER you have succesfully merged the lighting pack. I just made the changes to the 1.3.5 Beta version of the lighting pack (which incorporates 1.3.0 version of TGE) and it works fine with the demos provided.

[edit] Clarification, I have only confirmed that the repeating water changes work with the lighting pack, I have yet to test the repeating terrain changes. Could be the patch file above will not work with the latest version of TGE, in which case it might be necessary to make the mods manually. Will let you know how they go shortly.

Foz, when it comes to network packets, it's always good to question the vailidty of any data you send. In a final product of the game where you most likely do not want your users to have access to the editor, this variable would not be necessary. The clients should all be using the same default setting locally. If your game needed to change this setting for some reason, then it becomes necessary to propogate the change.

cheers,
Shannon.
#27
08/30/2005 (3:37 am)
Shannon - I got this working a while ago. And I don't use patch files, I always do everything manually. ;)

Thank you for your help.
#28
09/14/2005 (2:46 pm)
Do these patchs work on the current HEAD?

This is indeed what I need. My map is a very large(wide) but deffinitly finite continent, that should be surrounded by infinite water. Unfortunetly the height map is about 246px leaving me 10px of "infinite ocean" before repeating. I'm hopeing this will help, as TSE is NOT a viable option for me at the moment.

thanks,
Raven

ps. This should deffinitly be in the HEAD!
#29
09/19/2005 (7:40 am)
This should work with the latest head as long as you do the patches manually. The thing to be aware of is the collision. Visually the terrain can be turned off and the water can be left to repeat, providing you with the infinite ocean you seek, however, the collision will behave as if the terrain were repeating. Your players would be walking over invisible hills above the ocean.

Disallowing your players to enter the ocean is the simplest solution, otherwise you're looking at some additional coding to extend the terrain patch. If you remove the collision completely, however, your players can fall through the ocean, which is not ideal.

I've been toying with the notion of having a fixed "bottom" to the non-repeating section to represent the bottom of the ocean. In theory you could also provide a sloping effect much more akin to a real life ocean where the bottom slopes away from the shore. Unfortunately, I'm too busy at this time to implement either of thse options. Hopefully, someone else can pick up the ball?
#30
10/02/2005 (8:40 am)
I've applied this by hand to v1.3 with the SG lighting pack. I don't know if it's due to the lighting pack or not but when I disable terrain repeating the terrain disappears but leaves big holes in the water where it used to be, which don't go away on level reloads or anything. Does anyone know how to fix that?
#31
10/19/2005 (4:46 pm)
I have the same problem with the terrain changes and the 1.3.5-beta lightingpack (which is really stable..). It just leaves a gapeing hole in the terrain, in fact I can see the sun right through it at night!

[Edit] Of COURSE this doesn't work! The whole premise is - it's telling it "only render the itteration of the terrain block that falls between x = y = 0 and x = y = 1". Outside of that range NO RENDERING TAKES PLACE!

Haveing read the code I don't understand how this COULD work? I can't figure out if we should go higher or lower-level but I think we're in-part, attacking the problem at the wrong point.[/Edit]


Anyone know how to solve this? I'll keep looking but i've come up empty so far.


Thnaks,
Raven
#32
10/20/2005 (7:32 am)
The reason for the holes is that the water is being repeated after it has been optimized for the terrain. Basically, it breaks up the water plane into smaller and smaller cells based on where the terrain meets the water plane, removing any sub-cells that fall completely underneath the terrain. No need rendering what you're not supposed to be able to see.

There are two approaches to solve the problem. The first is to get it to repeat the water BEFORE it gets modified to fit the terrain, while keeping the modified water the primary area. The second would be to have two controls for the water (one for toggling the repeat, and another for toggling the meshing with the terrain) and create two sections of water; one non-repeating section in the primary area that meshes with the terrain and a second that repeats, does not mesh with the terrain, and does not appear in the primary area.

Either way you do it, a means of disabling the terrain meshing is the key.

edit: There's a 3rd solution, which is to disable the trimming completely and take a performance hit rendering water cells that are completely hidden.
#33
10/21/2005 (1:27 pm)
IT JUST ATE MY POST!!! grrr...

Shannon, I don't fully understand those methods though it seems to be what I would need. I initally had a very different approach to this problem though:


@ALL
My goal here would be to render the normal terrainblock. Everywhere else, render a terrain with elevation '0' and repeat the water block over it, for solid, seemless ocean into the great beyond.

It seems to me we should be able to set a flag in terrRender.cc in the for loops following JWV's changes around line 1841. (this would involve removing the line 1841 - 1874 changes.

1) If we are rendering the main block (0 < x <1) && (0 < y < 1) set the flag otherwise false.

2) Find the spot where the code extracts the elevation for each individual terrain tile, and inputs it for rendering. Check the flag, If false, override the elevation with '0'.

3) If done separately, repeat step 2 for where collision map is built for terrain.


This should force it to, outside of the main block, render a flat terrain at elevation 0. The water block should repeat over it smoothly with no problems, and no (further) changes to the fluid rendering.

I'm going to go back and try to accomplish this again. I'm admittedly an unseasoned programmer though, and new to TGE however. My big problem is I can't FIND the code where the render engine gets the elevation height for an individual tile.

Does this make sense? appreciate comments


~Raven



[edit]
ps. Actually don't give me the answer; I think i just figured it out for myself!!! Just need time to finish it and you can be sure i'll post. :-D

In fact the big-break was figuring out how to use the windows debugger :-P lol
[/edit]



[edit]
Spoke to soon.... lol

Anyways I AM 1/2 way there. Basically everywhere in terrRender.cc that getHeight() is used
to change 'z' or 'foo->z' i'm replacing it with '0' when we're out of the main block.

It is rendering repeated terrain at elevation 0. But it's still using the original texture map. Also water isn't repeating over it, ie-water/terrain collision still works. And normal collision is still working. I'm going to look at the collision code desmond links to above.

If I fix/break the collision should that in turn let the water repeat over the repeated terrain block? I didn't quite follow what Shannon was saying before..

Wierd Problem: if you look out stright, it DOES reneder the repeated terrain block at elevation 0. If you look below the horizon it starts turning off rendering alltogther for terrain tiles in the distance. By the time your looking at your feet, none of the repeated terrain in front of you is being rendered.

Time to put this down till Monday :-(
See you all next week.
[/edit]
#34
10/27/2005 (3:13 pm)
where does the last block of code go?? just anywhere under the header or what?
#35
03/06/2006 (1:17 pm)
It seems this was added to torque 1.4. Just click Tile (found in the code as mTile instead of mRepeatTerrain)
Page«First 1 2 Next»