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..
In terrData.cpp
At the Very end after everything else
Add..
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..



Just need to sort out those pesky black lines now :/
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 EndIn 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 EndUsage:-
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..



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

Torque Owner Jonajoint
Default Studio Name
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 :)