- Home /
Terrain editor brush (Solved)
Hello. I'm working on an in-game basic terrain editor similar to Unity's. I want to be able to raise/lower the terrain with a circle brush, including properties (size, opacity), but didn't find relevant code. I got the mouse position and tried to create a radius around it for changing terrain, but only got individual spikes in a circle shaped arrangement. Any ideas?
private void raiseTerrain(Vector3 point){
Debug.Log(point.x);
for (int i=0;i<=myTerrain.terrainData.size.x;i++){
for (int j=0;j<=myTerrain.terrainData.size.z;j++){
float xx=point.x-i;
float zz=point.z-j;
if(xx*xx+zz*zz<rad*rad){
int terX =(int)((i / myTerrain.terrainData.size.x) * xResolution);
int terZ =(int)((j / myTerrain.terrainData.size.z) * zResolution);
float y = heights[terX,terZ];
y += 0.001f;
float[,] height = new float[1,1];
height[0,0] = y;
heights[terX,terZ] = y;
myTerrain.terrainData.SetHeights(terX, terZ, height);
}
}
}
}
Edit: Thanks for the help alucardj, it works really nice, now I just need to modify it for my goals.
Ins$$anonymous$$d of iterating through loops of the terrainData.size, use the heightmapData
heightmapData = terrainData.GetHeights( 0, 0, heightmapWidth, heightmapHeight );
now you have a 2D array of the heightmapData, modify that, then reapply to the terrainData when you have finished modifying the whole circle area
terrainData.SetHeights( 0, 0, heightmapData );
You'll need to convert your point from terrain coordinate to heightmapData coordinate. If this is what you're already doing, it's really hard to tell with that highly compacted un-commented code.
Yeah, it's as you say. But still can't change the terrain in a circle shape, it's more like a line. I think I don't get the heightmapData indexes right and their meaning.
Answer by AlucardJay · May 25, 2014 at 04:08 PM
Here is an example script using the methods outlined in my comment.
For a feathered circle brush, you could calculate a modifier based on the calc.magnitude and the circleRadius, multiply paintWeight based on that modifier. I added the time between paint application to give Unity time to apply the heights, you can play with this time value or remove it.
I code easier in uJS, but if you have trouble converting it to C#, I can do that.
Warning : I use lots of comments and spaces ;)
//-----------------------------------//
// RealTimeTerrainHeightPainter.js //
// Written by Alucard Jay //
// 2014/5/26 //
//-----------------------------------//
#pragma strict
public var circleRadius : int = 12;
public var paintWeight : float = 0.001;
public var rayTimeInterval : float = 0.1;
private var rayTimer : float = 0;
private var rayHitPoint : Vector3;
private var heightmapPos : Vector3;
// Persistant Functions
// ----------------------------------------------------------------------------
function Start()
{
GetTerrainData();
ResetHeights(); // FOR TESTING, reset to flat terrain
}
function Update()
{
rayTimer += Time.deltaTime;
if ( rayTimer < rayTimeInterval )
return;
rayTimer = 0;
RaycastToTerrain();
GetHeightmapPosition();
if ( Input.GetMouseButton(0) && rayHitPoint != Vector3.zero )
PaintCircle( heightmapPos );
}
// Terrain Data
// ----------------------------------------------------------------------------
public var terrain : Terrain;
private var terrainData : TerrainData;
private var terrainSize : Vector3;
private var heightmapWidth : int;
private var heightmapHeight : int;
private var heightmapData : float[,];
function GetTerrainData()
{
if ( !terrain )
terrain = Terrain.activeTerrain;
terrainData = terrain.terrainData;
terrainSize = terrain.terrainData.size;
heightmapWidth = terrain.terrainData.heightmapWidth;
heightmapHeight = terrain.terrainData.heightmapHeight;
heightmapData = terrainData.GetHeights( 0, 0, heightmapWidth, heightmapHeight );
}
// Other Functions
// ----------------------------------------------------------------------------
function RaycastToTerrain()
{
rayHitPoint = Vector3.zero;
var hit : RaycastHit;
var rayPos : Ray = Camera.main.ScreenPointToRay( Input.mousePosition );
if ( Physics.Raycast( rayPos, hit, Mathf.Infinity ) ) // also consider a layermask to just the terrain layer
{
rayHitPoint = hit.point;
Debug.DrawLine( Camera.main.transform.position, hit.point, Color.red, rayTimeInterval );
}
}
function GetHeightmapPosition()
{
// find the heightmap position of that hit
heightmapPos.x = ( rayHitPoint.x / terrainSize.x ) * parseFloat( heightmapWidth );
heightmapPos.z = ( rayHitPoint.z / terrainSize.z ) * parseFloat( heightmapHeight );
// convert to integer
heightmapPos.x = Mathf.RoundToInt( heightmapPos.x );
heightmapPos.z = Mathf.RoundToInt( heightmapPos.z );
// clamp to heightmap dimensions to avoid errors
heightmapPos.x = Mathf.Clamp( heightmapPos.x, 0, heightmapWidth - 1 );
heightmapPos.z = Mathf.Clamp( heightmapPos.z, 0, heightmapHeight - 1 );
}
function PaintCircle( point : Vector3 )
{
var x : int;
var z : int;
var heightX : int;
var heightZ : int;
var heightY : float;
var calc : Vector2;
for ( z = -circleRadius; z <= circleRadius; z ++ )
{
for ( x = -circleRadius; x <= circleRadius; x ++ )
{
// for a circle, calcualate a relative Vector2
calc = new Vector2( x, z );
// check if the magnitude is within the circle radius
if ( calc.magnitude <= circleRadius )
{
// is within circle, paint height
heightX = point.x + x;
heightZ = point.z + z;
// check if heightX and Z is within the heightmapData array size
if ( heightX >= 0 && heightX < heightmapWidth && heightZ >= 0 && heightZ < heightmapHeight )
{
// read current height
heightY = heightmapData[ heightZ, heightX ]; // note that in heightmapData, X and Z are reversed
// add paintWeight to the current height
heightY += paintWeight;
// update heightmapData array
heightmapData[ heightZ, heightX ] = heightY;
}
}
}
}
// apply new heights to terrainData
terrainData.SetHeights( 0, 0, heightmapData );
}
function ResetHeights() // FOR TESTING, reset to flat terrain
{
var x : int;
var z : int;
for ( z = 0; z < heightmapHeight; z ++ )
{
for ( x = 0; x < heightmapWidth; x ++ )
{
heightmapData[ z, x ] = 0;
}
}
terrainData.SetHeights( 0, 0, heightmapData );
}
Your answer
Follow this Question
Related Questions
Problem detail resolution per patch. 2 Answers
Terrain generation [2D,C#]? 0 Answers
SetHeights to a Negative 1 Answer
terraindata.setheight as a brush for runtime terrain editing 1 Answer