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«First 1 2 Next»
#21
02/25/2012 (7:30 am)
the 2nd is an tiering method that works out and applies the required height to match 2 terrains with a different height base.

use :-
<TerrainToAlter>.tierMatch(<TargetTerrain>,<edge>,<Use Average Method>); <- in script

This method is limited in use for TerrainBlock's that need to be matched to more than one other TerrainBlock.. but its a start, and thats next on the list!
And it could be reworked so there is less repetative code. Once i've done both those things i'll update the resource.

void TerrainBlock::tierMatch(TerrainBlock* terrain, U32 edge, bool average)
{
	TerrainFile* file2=terrain->getFile();
	const U32 blockSize = terrain->getBlockSize();
	F32 height1=0;
	F32 height2=0;
	F32 dif;

	if(edge==0 || edge==2){
		if(edge==0){
//			Con::printf("Doing North Tier");
			if(!average){
				F32 min1 = 4096;
				F32 min2 = 4096;
				F32 max1 = -4096;
				F32 max2 = -4096;
				F32 temp1 =0;
				F32 temp2 =0;
				for(int x=0;x<blockSize;x++){
					temp1 = fixedToFloat(mFile->getHeight(x,0));
					temp2 = fixedToFloat(file2->getHeight(x,blockSize-1));
					if(temp1>max1)
						max1 = temp1;
					else if(temp1<min1)
						min1 = temp1;
					if(temp2>max2)
						max2 = temp2;
					else if(temp2<min2)
						min2 = temp2;
				}

				height1 = (max1-min1)/2;
				height2 = (max2-min2)/2;
			}else{
				for(int x=0;x<blockSize;x++){
					height1 += fixedToFloat(mFile->getHeight(x,0));
					height2 += fixedToFloat(file2->getHeight(x,blockSize-1));
				}
			}
		}else{
//			Con::printf("Doing South Tier");
			if(!average){
				F32 min1 = 4096;
				F32 min2 = 4096;
				F32 max1 = -4096;
				F32 max2 = -4096;
				F32 temp1 =0;
				F32 temp2 =0;
				for(int x=0;x<blockSize;x++){
					temp1 = fixedToFloat(mFile->getHeight(x,blockSize-1));
					temp2 = fixedToFloat(file2->getHeight(x,0));
					if(temp1>max1)
						max1 = temp1;
					else if(temp1<min1)
						min1 = temp1;
					if(temp2>max2)
						max2 = temp2;
					else if(temp2<min2)
						min2 = temp2;
				}

				height1 = (max1-min1)/2;
				height2 = (max2-min2)/2;
			}else{
				for(int x=0;x<blockSize;x++){
					height1 += fixedToFloat(mFile->getHeight(x,blockSize-1));
					height2 += fixedToFloat(file2->getHeight(x,0));
				}
			}
		}
	}else{
		if(edge==1){
//			Con::printf("Doing West Tier");
			if(!average){
				F32 min1 = 4096;
				F32 min2 = 4096;
				F32 max1 = -4096;
				F32 max2 = -4096;
				F32 temp1 =0;
				F32 temp2 =0;
				for(int y=0;y<blockSize;y++){
					temp1 = fixedToFloat(mFile->getHeight(blockSize-1,y));
					temp2 = fixedToFloat(file2->getHeight(0,y));
					if(temp1>max1)
						max1 = temp1;
					else if(temp1<min1)
						min1 = temp1;
					if(temp2>max2)
						max2 = temp2;
					else if(temp2<min2)
						min2 = temp2;
				}

				height1 = (max1-min1)/2;
				height2 = (max2-min2)/2;
			}else{
				for(int y=0;y<blockSize;y++){
					height1 += fixedToFloat(mFile->getHeight(blockSize-1,y));
					height2 += fixedToFloat(file2->getHeight(0,y));
				}
			}
		}else{
//			Con::printf("Doing East Tier");
			if(!average){
				F32 min1 = 4096;
				F32 min2 = 4096;
				F32 max1 = -4096;
				F32 max2 = -4096;
				F32 temp1 =0;
				F32 temp2 =0;
				for(int y=0;y<blockSize;y++){
					temp1 = fixedToFloat(mFile->getHeight(0,y));
					temp2 = fixedToFloat(file2->getHeight(blockSize-1,y));
					if(temp1>max1)
						max1 = temp1;
					else if(temp1<min1)
						min1 = temp1;
					if(temp2>max2)
						max2 = temp2;
					else if(temp2<min2)
						min2 = temp2;
				}

				height1 = (max1-min1)/2;
				height2 = (max2-min2)/2;
			}else{
				for(int y=0;y<blockSize;y++){
					height1 += fixedToFloat(mFile->getHeight(0,y));
					height2 += fixedToFloat(file2->getHeight(blockSize-1,y));
				}
			}
		}
	}
	if(average){
		height1 /= blockSize;
		height2 /= blockSize;
	}
	dif = height2-height1;
	Point3F pos1 = getPosition();
	Point3F pos2 = terrain->getPosition();
	pos1.z = pos2.z+dif;
	setPosition(pos1);
}

DefineEngineMethod( TerrainBlock, tierMatch, void, ( TerrainBlock* terrBlock, U32 edge, bool average),,
					"@brief Raises or lowers the height of the TerrainBlock postion so edges match"
					"@param terrBlock = the TerrainBlock to match to.nn"
					"@param edge = The edge on this terr.nn"
					"@param average = Calculate by average, else use half min/max.")
{
	object->tierMatch(terrBlock, edge, average);
}

Sorry its taken alot longer than I said, busy busy busy :)
#22
12/05/2012 (9:30 pm)
thanks for you resource~~
#23
12/06/2012 (7:56 pm)
this is very useful~~
Page«First 1 2 Next»