- Home /
How to translate WORLD coordinates to TERRAIN coordinates?
Recently I've begun messing around with altering the terrain in real-time. One of the hiccups I've run into is that world coordinates are not the same as terrain coordinates (obviously). How can I take a position in the world and translate it to the nearest point on the terrain that I can then alter the height of? I hope my question is clear enough. Any advice is appreciated!
Answer by duck · Dec 20, 2009 at 07:15 PM
First, subtract the terrain gameobject's world position, then divide by your terrain's size (which you can access in terrainData.size). This will give you a position with values somewhere in the range of zero to one.
You can then multiply this by your heightmap resolution (using the terrainData heightmapWidth and heightmapHeight variables), and then convert to integers to find the correct heightmap array position to feed into the GetHeights and SetHeights functions.
private Vector3 ConvertWordCor2TerrCor(Vector3 wordCor) { Vector3 vecRet = new Vector3(); Terrain ter = Terrain.activeTerrain; Vector3 terPosition = ter.transform.position; vecRet.x = ((wordCor.x - terPosition.x) / ter.terrainData.size.x) ter.terrainData.alphamapWidth; vecRet.z = ((wordCor.y - terPosition.z) / ter.terrainData.size.z) ter.terrainData.alphamapHeight; return vecRet; } Is this right? or i should change it like this: vecRet.x **= ((wordCor.x - terPosition.x) / ter.terrainData.size.x) ter.terrainData.alphamapWidth;
How do you calculate the "height map resolution"? If I get the resolution like this: tData = terrain.terrainData; xRes = tData.heightmapWidth; yRes = tData.heightmapHeight;
Then what do I do with ((point.x - terrainPosition.x) / terrainSize.x) * ...
?
Answer by Baalhug · Nov 02, 2018 at 12:08 AM
The terrain is a bidimensional array of float numbers between 0 and 1, where 0 is the lowest height and 1 is the highest. So 0.5 will be the half height defined in your Terrain Height variable. If you have defined a height of 300, 0.5 will be 150 height in world coordinates, 0.6 will be 180, and so on.
This way an array like this:
0, 0.5, 0
0.5, 1, 0.5
0, 0.5, 0
would make a romboid pyramid out of your terrain independent of your terrain object dimensions in world coordinates. Unity developers called heightmapWidth and heightmapHeight variables in heightmap resolution but heightmapHeight should be heightmapLength as they refer to the numbers of points in bidimensional array, and height is actually the value of each float on that array. Anyway in our example both variables have a value of 3, because our terrain has 3 x 3 values for heights. That is a very bad resolution but is enough to explain and understand. The higher resolution the most height points you will have in your terrain, the most precise will be your terrain contour and the larger will be the file.
Now imagine you raycast on the top of this pyramid knowing the gameobject terrain is 500 x 500 in world coordinates, you will have a point of collision in world coordinates x=250 z=250, assuming the terrain world position is Vector3(0, 0, 0). Then you can calculate the relation in world coordinates and pass it to terrain coordinates or viceversa. To know wich point of the terrain you have to edit:
//% of terrain x = (x point of raycast collision - x position of your terrain object) / x total width of terrain
//same with z
// raycast object is called ray
Terrain ter = GetComponent<Terrain>(); // assuming this script is attached to the terrain
float relativeHitTerX = (ray.point.x - transform.position.x) / ter.terrainData.size.x;
float relativeHitTerZ = (ray.point.z - transform.position.z) / ter.terrainData.size.z;
// you have a number between 0 and 1 where your terrain has been hit (multiplied by 100 it would be the percentage of width and length)
relativeTerCoordX = ter.terrainData.heightmapWidth * relativeHitTerX;
relativeTerCoordZ = ter.terrainData.heightmapHeight * relativeHitTerZ;
// now you have the relative point of your terrain, but you need to floor this because the number of points on terrain are integer
int hitPointTerX = Mathf.floorToInt(relativeTerCoordX);
int hitPointTerZ = Mathf.floorToInt(relativeTerCoordZ);
//Now you can use this point to edit it using SetHeights
float[,] heights = ter.terrainData.GetHeights();
heights[hitPointTerx, hitPointTerZ] = heights[hitPointTerx, hitPointTerZ] - (10/ter.terrainData.size.y)
ter.terrainData.SetHeights(0, 0, heights);
// this will lower that terrain point 10% of max height of that terrain object, if you want a "crater" effect you can get and edit the points surrounding your impact point and substract a little less, like (7/ter.terrainData.size.y) for example
Check out this syntax, they can have errors as I didn't write them in editor
Here is an updated and cleaned up version of the sample code
public Terrain terrain;
TerrainData td;
private void Start( )
{
td = terrain.terrainData;
}
Ray ray = new Ray(transform.position, -transform.up);
RaycastHit hit;
if ( Physics.Raycast( ray, out hit, 50f ) )
{
float relativeHitTerX = (hit.point.x - terrain.transform.position.x) / td.size.x;
float relativeHitTerZ = (hit.point.z - terrain.transform.position.z) / td.size.z;
float relativeTerCoordX = td.heightmapResolution * relativeHitTerX;
float relativeTerCoordZ = td.heightmapResolution * relativeHitTerZ;
int hitPointTerX = $$anonymous$$athf.FloorToInt(relativeTerCoordX);
int hitPointTerZ = $$anonymous$$athf.FloorToInt(relativeTerCoordZ);
float[,] heights = td.GetHeights(0, 0, td.heightmapResolution ,td.heightmapResolution);
heights[ hitPointTerZ, hitPointTerX ] = heights[ hitPointTerZ, hitPointTerX ] * 1.05f;
td.SetHeights( 0, 0, heights );
// Debug.DrawLine( transform.position, hit.point, Color.red, 5f ); // Debug.Log( "Hit " + hit.point.x + " " + hit.point.z + " Relative: " + relativeHitTerX + " " + relativeHitTerZ );
}
Answer by darkmath · Jan 12, 2010 at 03:03 PM
You could also do a terrain.collider.Raycast:
RaycastHit intersection = new RaycastHit();
Ray originUp = new Ray(new Vector3(0,0,0), Vector3.up);
terrain.collider.Raycast(originUp, out intersection, 100000.0f);
if (intersection.collider != null)
{ ball.transform.position = intersection.point;
ball.rigidbody.position = intersection.point;
}
That just gives a world position again. Terrain position is the little blue squares when you paint it.
Answer by Yanger_xy · Mar 12, 2011 at 12:30 PM
private Vector3 ConvertWordCor2TerrCor(Vector3 wordCor)
{
Vector3 vecRet = new Vector3();
Terrain ter = Terrain.activeTerrain;
Vector3 terPosition = ter.transform.position;
vecRet.x = ((wordCor.x - terPosition.x) / ter.terrainData.size.x) * ter.terrainData.alphamapWidth;
vecRet.z = ((wordCor.y - terPosition.z) / ter.terrainData.size.z) * ter.terrainData.alphamapHeight;
return vecRet;
}
Is this right? or i should change it like this: vecRet.x *= ((wordCor.x - terPosition.x) / ter.terrainData.size.x) ter.terrainData.alphamapWidth; vecRet.z = ((wordCor.y - terPosition.z) / ter.terrainData.size.z) * ter.terrainData.alphamapHeight;
Answer by fenderrex · Apr 22 at 02:03 AM
draws Gizmos using TerrainData on Terrain
void OnDrawGizmos()
{
Terrain terrain = GetComponent<Terrain>();
Vector3 terrainSize = terrain.terrainData.size;
for (int i = 0; i < terrain.terrainData.heightmapResolution; i += 1) // use steps of more than 1 for smoother interaction in editor
{
for (int k = 0; k < terrain.terrainData.heightmapResolution; k += 1)
{
Vector3 pivot = new Vector3(k, terrain.terrainData.GetHeight(k , i ), i);
float x = pivot.x / (terrainSize.x);
float z = pivot.z / (terrainSize.z );
Vector3 interpolatedNormal = terrain.terrainData.GetInterpolatedNormal(x, z);
GUI.color = Color.blue;
pivot = terrain.transform.position+ new Vector3(pivot.x / terrain.terrainData.heightmapResolution * terrainSize.x, pivot.y, pivot.z / terrain.terrainData.heightmapResolution * terrainSize.y);
Gizmos.DrawSphere(pivot, 0.2f);
Gizmos.color = Color.cyan;
Gizmos.DrawRay(pivot, interpolatedNormal * rayScale);
}
}
}
Your answer
Follow this Question
Related Questions
The name 'Joystick' does not denote a valid type ('not found') 2 Answers
How to generate random terrain like platforms for a 2.5D game. 0 Answers
Correctly Rebuilding Terrain Basemap 2 Answers
Adding trees dynamically 1 Answer
Randomizing tile textures 2 Answers