Game Development Community

dev|Pro Game Development Curriculum

Terrain Edge Matching

by Jonajoint · 01/30/2012 (9:21 am) · 23 comments

Matches an edge on a TerrainBlock to the opposite edge on the same or different TerrainBlock.

Handy for Quickly matching up terrains for tiling.

In terrData.h
At the end after void inspectPostApply();
Add..

// << Jonajoint Added
private:
	struct SmoothBufferInfo{
		U32 x;
		U32 y;
		F32 height;
	};
public:
	// terrain = the terrain block to match to, edge is the edge number:- 0 being north, 2 south, 1 west, 3 east
	// set slope to enable linear slope smoothing.
	// smoothBorder is the amount in from the edge to smooth
   void matchEdge(TerrainBlock* terrain, U32 edge,bool slope, U32 smoothBorder);
//>> Jonajoint End


In terrData.cpp
At the Very end after everything else
Add..

//<< Jonajoint Added
	// terrain = the terrain block to match to
	// edge is the edge number:- 0 being north, 2 south, 1 west, 3 east
	// set slope to enable linear slope smoothing.
	// smoothBorder is the amount in from the edge to smooth

void TerrainBlock::matchEdge(TerrainBlock* terrain, U32 edge,bool slope, U32 smoothBorder)
{
	U32 edge2 = edge+2;
	if(edge2 > 3)
		edge2 -= 4;

	TerrainFile* file2=terrain->getFile();
	const U32 blockSize = terrain->getBlockSize();
	U32 edgeYMin,edgeYMax,edge2Y;
	U32 edgeXMin,edgeXMax,edge2X;
	U16 height;
	height = 0;
	if(edge==0)
	{
		edgeYMin=0;
		edgeYMax=1;
		edgeXMin=0;
		edgeXMax=blockSize;
		edge2Y=blockSize-1;
		edge2X=0;
	}else if(edge==1)
	{
		edgeYMin=0;
		edgeYMax=blockSize;
		edgeXMin=blockSize-1;
		edgeXMax=blockSize;
		edge2Y=0;
		edge2X=0;
	}else if(edge==2)
	{
		edgeYMin=blockSize-1;
		edgeYMax=blockSize;
		edgeXMin=0;
		edgeXMax=blockSize;
		edge2Y=0;
		edge2X=0;
	}else{
		edgeYMin=0;
		edgeYMax=blockSize;
		edgeXMin=0;
		edgeXMax=1;
		edge2Y=0;
		edge2X=blockSize-1;
	}
	if(edge==0 || edge==2){
		for ( U32 y = edgeYMin; y < edgeYMax; y++ )
		{
			for ( U32 x = edgeXMin; x < edgeXMax; x++ )
			{
				height = file2->getHeight(x,edge2Y);
				mFile->setHeight( x, y, height );
				if(slope && smoothBorder > 0 ){
					F32 temp1;
					F32 height2;
					height2=fixedToFloat(height);
					F32 inc;
					F32 dif;
					if(edge==0){
//						Con::printf("Doing North Smooth");
						temp1 = fixedToFloat(mFile->getHeight(x,smoothBorder+1));
						dif = temp1-height2;
						inc = dif/smoothBorder;
						for(int i=1;i<edgeYMax+smoothBorder;i++){
							//height=mFile->getHeight(x,i);
							mFile->setHeight(x,i,floatToFixed(height2+(inc*i)));
						}
					}else{
//						Con::printf("Doing South Smooth");
						temp1 = fixedToFloat(mFile->getHeight(x,blockSize-(smoothBorder+1)));
						dif = temp1-height2;
						inc = dif/smoothBorder;
						for(int i=smoothBorder+1;i>1;i--){
							//height=mFile->getHeight(x,i);
							mFile->setHeight(x,blockSize-i,floatToFixed(height2+(inc*i)));
						}
					}
				}
			}
		}
	}else{
		for ( U32 y = edgeYMin; y < edgeYMax; y++ )
		{
			for ( U32 x = edgeXMin; x < edgeXMax; x++ )
			{
				height = file2->getHeight(edge2X,y);
				mFile->setHeight( x, y, height );
			}
			if(slope && smoothBorder > 0 ){
				F32 temp1;
				F32 height2;
				height2=fixedToFloat(height);
				F32 inc;
				F32 dif;
				if(edge==1){
//					Con::printf("Doing West Smooth");
					temp1 = fixedToFloat(mFile->getHeight(blockSize-(smoothBorder+1),y));
					dif = temp1-height2;
					inc = dif/smoothBorder;
					for(int i=smoothBorder+1;i>1;i--){//blockSize-(smoothBorder-1);i<blockSize-1;i++){
						mFile->setHeight(blockSize-i,y,floatToFixed(height2+(inc*i)));
					}
				}else{
//					Con::printf("Doing East Smooth");
					temp1 = fixedToFloat(mFile->getHeight(smoothBorder+1,y));
					dif = temp1-height2;
					inc = dif/smoothBorder;
					for(int i=1;i<smoothBorder+1;i++){
						//height=mFile->getHeight(x,i);
						mFile->setHeight(i,y,floatToFixed(height2+(inc*i)));
					}
				}
			}
		}
	}
	// TODO add Average height radius smoothing 
	// TODO Linear Regression smoothing (Dan Kellers Resource would be a good place to start)
	// http://www.garagegames.com/community/resources/view/21479
	

	// Note:- TODO The update range passed to upDateGrid is set to the full terrain size
	// This works well enough for use in the editor, but needs sorting for game use
	// and should be easy enough, Just cant be bothered :p
	if(this->isServerObject()){
		TerrainBlock* clientTerrain = dynamic_cast<TerrainBlock*>( this->getClientObject() );
        	updateGrid( Point2I::Zero, Point2I( blockSize, blockSize ) );
        	clientTerrain->updateGrid(Point2I::Zero, Point2I( blockSize, blockSize ));
	}
	
    
//    terrain->updateGridMaterials( Point2I::Zero, Point2I( blockSize, blockSize ) );
}

DefineEngineMethod( TerrainBlock, matchEdge, void, ( TerrainBlock* terrBlock, U32 edge, bool slope, U32 smoothBorder),,
				   "@brief Matches an edge on this terrain block with the opposite edge on the one supplied.nn"
				   "@param terrBlock = the TerrainBlock to match to.nn"
				   "@param edge = The edge on this terr.nn"
				   "@param slope = do linear slope smoothing.nn"
				   "@param smoothBorder = amount of edge to do smoothing operations on.nn")
{
	object->matchEdge(terrBlock,edge,slope,smoothBorder);
}
//>> Jonajoint End


Usage:-

You need at least 1 terrain block in the scene.
If you are using more than 1 then they need to be the same grid size (not squaresize)
where terr1 is the name of a TerrainBlock in the editor and terr2 is the name of another.
use following command in console:-

terr1.matchEdge(terr2,0,true,50);

Command Break down :-
<Terrain Name>.matchEdge(<Terrain Name>, <Edge to Match>, <Do Linear Slope>, <Amount in from edge to slope>);



and... Some obligatory before and after pictures..

i1052.photobucket.com/albums/s454/Jonajoint/terrMatchBefore.jpgi1052.photobucket.com/albums/s454/Jonajoint/terrainMatch.jpgi1052.photobucket.com/albums/s454/Jonajoint/terrMatchAfter2.jpg

Just need to sort out those pesky black lines now :/

About the author

Recent Blogs

Page «Previous 1 2
#1
01/30/2012 (9:24 am)
Oops Forgot to add:-
0 = north edge
1 = west
2 = south
3 = east
#2
01/30/2012 (9:48 am)
hmm somehow this ended up as a blog..
can it be moved to resources?
#3
01/30/2012 (11:33 am)
your pic links are messed up, can u report them plz
#4
01/30/2012 (1:28 pm)
I went ahead and moved your post to the resource section and fixed your broken images. Thanks for the new resource.
#5
01/30/2012 (11:38 pm)
Can you use this for more than just 4 terrains? not that this isn't awesome!!! I have been trying to figure this out in my spare time but have not found many examples online, so this is great!! just would like to be able to stitch 8 at a time if possible, but thanks a million for this!

Edit: Guess I should have looked at the code a bit first, it looks like it may do that or am I reading this wrong? Also do they edit well at the terrain lines?
#6
01/31/2012 (5:13 am)
Thanks Christopher.

@Bobby you can 'stitch' as many as you like, just 1 edge at a time.

The editing at the terrain lines is as stock T3D, once lined up T3D will move the edge on the other corresponding terrain along with the one your editing. It's just very slow as it sends updates for all terrains in the scene when it does this (I think, there is a comment in the edit actions code somewhere about this).
I recomend you move the TerrainBlocks slightly apart and edit the edges to an aproximation of each other then run the matchEdge function for best results.

I have a simple edge 'lock' function which locks the edges (beleive it not :p) so they cant be moved during brush editing. its part of my terrain tools which is visible in one of the screen shots, I'll be posting that at some point when a bit more complete.
#7
01/31/2012 (1:46 pm)
Awesome! This will be perfect, see what i do is create a VERY large terrain set in L3DT and then using their mosaic tiling and then export all those maps, and I have wanted to import them one at a time into Torque(perhaps I can automate this somehow in the future?...I don't know), anyway I cant wait to get my compiler back up in running:O)
#8
01/31/2012 (2:49 pm)
I'm not sure this is what you think it is actually, L3DT mosiacs are already matched/aligned at the edges. This is more for making tiled terrains with the T3D terrain editor a little less painfull/quicker.

If you move 2 L3DT created TerrainBlocks together so the edges are matched up stock T3D will stitch them.

BTW.. I'm currently working on a L3DT auto tiler, so i'll keep you posted.

Also not to be a bearer of bad news but t3d won't handle the distances required to place a massive l3dt in the scene (due to floating point precision issues, this is inherant to any engine not just t3d), you'll have to create a paging sytem, which in itself isnt a complicated thing but brings with it other issues like having to page the whole scene.. even non networked objects like TSStatics. Forests will probably be totally unusable with a paging system as there is only one for the whole scene. As ground cover places things randomly using a seed, when a terrain is paged back a square it will be regenerated at different positions, probably not an issue for things like short dense grass,but will make it unusable for bushes etc (I think.. alot of this is conceptual atm).
Then theres the issue of AI, you will have to have an entirly seperate ai world for ai to navigate when not on a visible page, and perhaps algorithms for eta's rather than for actual movement off page to reduce load.
All in all a ton of work, probably too much for one person, but hey I love trying to work all this stuff out :p
#9
01/31/2012 (5:20 pm)
I use L3DT for the MMO that WinterLeaf Entertainment is working on. Although L3DT does do tiling, I will still use this resource to stitch together the different terrain files that I create.

FYI - chk the resource done by Vince Gee to handle the ghosted AIs. while we dont have a paging system in place, the AI, TSStatics and what not are all things we are working on for our MMO Platform so that we can handle more than the stock T3D does.
#10
01/31/2012 (6:00 pm)
jona, Im tempted to hump your leg like a happy lil terrier.

it will save alot of headaches on larger maps, aswell as time saved for skirt terrains.
#11
01/31/2012 (10:41 pm)
This is beautiful! Thanks!
#12
02/01/2012 (6:20 pm)
This is totally awesome! Thanks for posting!
#13
02/03/2012 (3:23 am)
That is a cool resource. ty !
#14
02/06/2012 (5:34 pm)
Jonajoint@ Going to give this code a shot.

I have a very large area which I broke up into (9) smaller chunks - because the over-all height is ~3200m and t3d only supports terrains up to 2048m in height total.

So - I first imported each "chunk" into L3DT, doctored it up and them exported to T3D mission / terrain files. Then I copied all (9) terrain blocks into a new mission -- and manually spaced them 6144 pixels apart.
(1024 ht map size * 6 m per square - just a guess)

The mission file loads up, but the terrains all seem to be rotated 180 degrees ... and there is a small gap between each terrain.

But question is ... how do I tell which "side" of each tile is the N,S,E or W "side" ?

[edit]
through trial and error, determined which direction was what ... but now that its working, I see those same black lines you mention. Could that be a result of tryin to merge the "edge" of the terrain itself ... ie. perhaps skipping the outer 2 pixels or such might make the black line go away -- assuming thats the cause.
#15
02/08/2012 (7:23 am)
I have not tried in a while but I used to get collision issues from mashing two terrain pieces together like, so i guess I was hoping that some sort of terrain stitching would take care of the that, but i don't know just moved to Tenn from Maine and I haven't had time with work to get this into my build, but the limits of Torque as you mentioned above are defiantly on my list of this I need to get working( no I am not making an MMO, but the world I am making is big enough to house one!) I had thought that some of these things were working more like I wanted the in TSE with the old Atlas terrain so I was going to look in there, but I am unsure if there answer lies there or not, but I am sure I can find it as I have time...any resources somewhere on the net that you can recommend?
#16
02/10/2012 (11:19 am)
@Jeff This won't currently work for terrains with a different height base, although have thought of this issue myself, i'll see if i get time to alter it this weekend.

Edit:- the black lines appear even when altering the edges of a single terrain manually with a brush. I think the issues may come from the SmoothNormal generation code, where its using the skirt or something to work out the smoothed edges.

@Bobby The only recources I have looked at really are the L3DT docs for its file formats, and those here on GG.
I have a semi-working paging script prototype, i'll see if i get time to work out the issues with it this weekend also, they arn't massive issues just things like its placing tiles slightly wrong when a long distance from starting grid square.. so just inconsistances in the algorithms, I think I know what they are.. but we'll see!
#17
02/17/2012 (11:57 am)
Nice job! Now if we can just figure out those black lines around the edges... =)
#18
02/22/2012 (8:09 am)
Lemme know if I can help, I don't have too much time...and ill look into the rendering code for the terrain and the shaders for the ground to see if I can find something that doesn't line up...not sure what causes those black lines...but im sure going to investigate:O)
#19
02/22/2012 (8:33 am)
@Bobby: Right on. I haven't had time to implement this resource yet or I'd be looking into it myself. Definitely let us know what you find. =)



#20
02/25/2012 (7:29 am)
Added 2 new methods which will help with TerrainBlocks having a different base height, so through tiering the terrains the range can be greater than 0 - 4096.
The first is blanket height change.. quite self explanitary, just work out the increase/decrease in height needed and apply using :-
terrain.blanketChangeHeight(<amount>); <<-in script

the source changes needed
void TerrainBlock::blanketChangeHeight(F32 amount)
{
	U32 blockSize = getBlockSize();
	F32 temp;
	for(int y=0;y<blockSize;y++)
	{
		for(int x=0;x<blockSize;x++)
		{
			temp = fixedToFloat( mFile->getHeight(x,y) );
			temp += amount;
			if(temp < 0) temp = 0;
			mFile->setHeight(x,y,floatToFixed(temp));
		}
	}
	if(this->isServerObject()){
		TerrainBlock* clientTerrain = dynamic_cast<TerrainBlock*>( this->getClientObject() );
        updateGrid( Point2I::Zero, Point2I( blockSize, blockSize ) );
        clientTerrain->updateGrid(Point2I::Zero, Point2I( blockSize, blockSize ));
	}
}

DefineEngineMethod( TerrainBlock, blanketChangeHeight, void, (F32 amount),,
				   "@brief Applies a height adjustment to the whole TerrainBlock.nn"
				   "@param amount = the amount to adjust by.nn")
{
	object->blanketChangeHeight(amount);
}

Page «Previous 1 2