I’m writing a tool that will add a racetrack to a terrain.
I can successfully read a Texture2D, and quite happily modify terrainData.
My problem lies with a calculation that I just cannot seem to formulate. So :
I want to set an average height for the racetrack. All these height references are normalized values (between 0 and 1).
var averageHeight : float = 0.3;
Then read the height at specified point of the terrain :
var heightMap : float = heightmapData[y,x];
Then read the height from my racetrack image :
var pixelHeight : float = racetrackTexture.GetPixel( x, y ).grayscale;
From these 3 floats, I need to find out :
if the heightMap height is greater than the averageHeight, and the pixelHeight is greater than zero, then reduce the heightMap height, factoring in the pixelHeight.
if the heightMap height is less than the averageHeight, and the pixelHeight is greater than zero, then increase the heightMap height, factoring in the pixelHeight.
Example :
heightMap height is greater than the averageHeight,
so the heightMap height has to be reduced,
reduction is a factor of the pixelHeight,
if pixelHeight is high (close to 1) then dramatically reduce the heightMap
if pixelHeight is low (close to 0) then only slightly reduce the heightMap
So basically I want my racetrack to follow the profile of the terrain.
if the heightMap is greater than the averageHeight, then dig the racetrack into the terrain.
if the heightMap is less than the averageHeight, then raise the racetrack above the terrain.
If you managed to follow that description, then well done !
Here is an image for the heightMap being greater than the averageHeight :
Here is an image for the heightMap being less than the averageHeight :
I should point out while my most excellent paint jobs show the track being the same as the averageHeight, I don’t want a flat track, I want it to follow the terrain. But if the terrain is higher than the averageHeight then drop the track down a bit, and if the terrain is lower than the averageHeight then raise the track up a bit.
I totally understand if you are confused. If you want to see my code work-in-progress with all my experimentation, here it is :
#pragma strict
public var racetrackTexture : Texture2D;
private var textureSize : Vector2;
public var averageHeight : float = 0.2;
public var blendFactor : float = 0.5;
public var terrain : Terrain;
private var terrainData : TerrainData;
private var terrainSize : Vector3;
private var heightmapWidth : int;
private var heightmapHeight : int;
private var heightmapScale : Vector3;
private var heightmapData : float[,];
private var backupHeightmapData : float[,];
private var racetrackData : float[,];
enum terrainModifier
{
Method_1,
Method_2,
Method_3,
Method_4,
Method_5,
Method_6,
Method_7,
Restore_Backup
}
public var EditTerrain : terrainModifier;
function Start()
{
if ( !terrain )
{
terrain = Terrain.activeTerrain;
}
terrainData = terrain.terrainData;
heightmapWidth = terrain.terrainData.heightmapWidth;
heightmapHeight = terrain.terrainData.heightmapHeight;
heightmapScale = terrain.terrainData.heightmapScale;
backupHeightmapData = terrainData.GetHeights( 0, 0, heightmapWidth, heightmapHeight );
racetrackData = new float[ racetrackTexture.width, racetrackTexture.height ];
for ( var y : int = 0; y < racetrackTexture.height; y ++ )
{
for ( var x : int = 0; x < racetrackTexture.width; x ++ )
{
racetrackData[ x, y ] = racetrackTexture.GetPixel( x, y ).grayscale;
}
}
}
function Update()
{
if ( Input.GetMouseButtonDown(0) )
{
if ( EditTerrain == terrainModifier.Restore_Backup )
{
terrainData.SetHeights( 0, 0, backupHeightmapData );
}
else
{
TestingStuff();
}
}
else if ( Input.GetMouseButtonDown(1) )
{
terrainData.SetHeights( 0, 0, backupHeightmapData );
}
}
function TestingStuff()
{
heightmapData = terrainData.GetHeights( 0, 0, heightmapWidth, heightmapHeight );
textureSize = new Vector2( racetrackTexture.width, racetrackTexture.height );
var calc : float;
var calcNorm : float;
for ( var y : int = 0; y < textureSize.y; y ++ )
{
for ( var x : int = 0; x < textureSize.x; x ++ )
{
var pixelHeight : float = racetrackData[ x, y ];
var terrainHeight : float = heightmapData[ y, x ];
switch( EditTerrain )
{
case terrainModifier.Method_1 :
pixelHeight = racetrackTexture.GetPixel( x, y ).grayscale;
// find the difference
var difference : float = terrainHeight - averageHeight;
// adjusted height is pixel as a percentage of difference
var adjustHeight : float = difference * pixelHeight;
// apply to heightmapData[y,x]
heightmapData[y,x] = heightmapData[y,x] + adjustHeight;
break;
case terrainModifier.Method_2 :
pixelHeight = racetrackTexture.GetPixel( x, y ).grayscale;
if ( terrainHeight > averageHeight pixelHeight > 0.0 )
{
heightmapData[y,x] = terrainHeight - ( pixelHeight * terrainHeight );
}
/*
else
{
heightmapData[y,x] = terrainHeight + ( pixelHeight * terrainHeight );
}
*/
break;
case terrainModifier.Method_3 :
if ( terrainHeight > averageHeight )
{
heightmapData[y,x] = Mathf.Clamp( heightmapData[y,x] - 0.1, 0.0, 1.0 );
}
else
{
heightmapData[y,x] = Mathf.Clamp( heightmapData[y,x] + 0.1, 0.0, 1.0 );
}
break;
case terrainModifier.Method_4 :
//var diff : float = pixelHeight - terrainHeight;
var diff : float = 1.0 - ( pixelHeight - terrainHeight );
calc = Mathf.Clamp( terrainHeight + ( diff * 0.5 ), 0.0, 1.0 );
heightmapData[y,x] = calc;
break;
case terrainModifier.Method_5 :
// AVERAGE
calc = ( pixelHeight * 0.5 ) + ( terrainHeight * 0.5 );
// BLEND FACTOR
pixelHeight *= blendFactor;
terrainHeight *= 1.0 - blendFactor;
//calc = pixelHeight + terrainHeight;
calcNorm = Mathf.Clamp( calc, 0.0, 1.0 );
heightmapData[y,x] = calcNorm;
break;
case terrainModifier.Method_6 :
if ( terrainHeight < averageHeight )
{
calc = terrainHeight + ( pixelHeight * 0.1 );
}
else
{
//calc = terrainHeight;
calc = terrainHeight - ( pixelHeight * 0.1 );
//calc = terrainHeight - ( 1.0 - ( pixelHeight * 0.1 ) );
//calc = terrainHeight - ( ( 1.0 - pixelHeight ) * 0.1 );
}
calcNorm = Mathf.Clamp( calc, 0.0, 1.0 );
heightmapData[y,x] = calcNorm;
break;
case terrainModifier.Method_7 :
if (terrainHeight > averageHeight)
{
calc = terrainHeight - (terrainHeight * pixelHeight);
}
else if (terrainHeight < averageHeight)
{
calc = terrainHeight + (terrainHeight * pixelHeight);
}
heightmapData[y,x] = calc;
break;
}
}
}
terrainData.SetHeights( 0, 0, heightmapData );
}
Here is my test terrain : http://www.alucardj.net16.net/unityquestions2012/TestTerrain.asset
Here is my test RaceTrack image : http://www.alucardj.net16.net/unityquestions2012/RaceTrack0_Inverted.jpg


