I really liked this question, and was surprised there was no answer, so I decided to have a go myself.
This only answers the second half of the question how can I potentially draw the grid only around the cursor. I had a laugh at I don’t want to do very complex stuff as I am just a beginner then reading your modification of the alphamap script, I wouldn’t have a clue on modifying the splat/texture information!
So, this is for drawing a grid only around the mouse position. What I have done is created a mesh, then from a raycast from the camera to the terrain, reading the heightmap information, then modifying the mesh vertices based on the heights around the ray hit position. I have included some clampsing to make sure there are no array out of range problems.
Note : When I wrote this, the terrain and the object I attached the script to were positioned at (0,0,0)
indicatorSize changes the size of the grid. The grid is just a texture so it doesn’t add more squares (it’s possible with some modification to the uv calculations). indicatorOffsetY is to raise the mesh from the terrain, for some reason converting the height from terrain size to heightmap size and back again messes the actual height value. And further away from the terrain, there is some z-axis fighting. This could probably be fixed by adding more vertices to the mesh.
Unfortunately this is in uJS, but at least shows a proof-of-concept. If this is what you were after, I could try a conversion. If not, I’ll just delete this answer =]
This was a lot of fun, I really like a challenge, and playing around with meshes and terrain data.
Here’s the script :
// ===========================================================================
// Plane Mesh that Forms to the Terrain Profile script
// written by Alucard J ( Jay Kay )
// April 2013
// ===========================================================================
#pragma strict
@script RequireComponent( MeshFilter )
@script RequireComponent( MeshRenderer )
// --------------------------------------------------------------------------- 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 );
}
// --------------------------------------------------------------------------- Raycast To Terrain
private var rayHitPoint : Vector3;
private var heightmapPos : Vector3;
function Start()
{
GetTerrainData();
ConstructMesh();
}
function Update()
{
// raycast to the terrain
RaycastToTerrain();
// find the heightmap position of that hit
GetHeightmapPosition();
// Calculate Grid
CalculateGrid();
// Update Mesh
UpdateMesh();
}
function RaycastToTerrain()
{
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
{
Debug.DrawLine( Camera.main.transform.position, hit.point, Color.red );
rayHitPoint = hit.point;
}
}
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.Round( heightmapPos.x );
heightmapPos.z = Mathf.Round( 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 );
}
// --------------------------------------------------------------------------- Calculate Grid
private var mapGrid : Vector3[,] = new Vector3[ 9, 9 ];
public var indicatorSize : float = 1.0;
public var indicatorOffsetY : float = 5.0;
function CalculateGrid()
{
for ( var z : int = -4; z < 5; z ++ )
{
for ( var x : int = -4; x < 5; x ++ )
{
var calcVector : Vector3;
calcVector.x = heightmapPos.x + ( x * indicatorSize );
calcVector.x /= parseFloat( heightmapWidth );
calcVector.x *= terrainSize.x;
var calcPosX : float = heightmapPos.x + ( x * indicatorSize );
calcPosX = Mathf.Clamp( calcPosX, 0, heightmapWidth - 1 );
var calcPosZ : float = heightmapPos.z + ( z * indicatorSize );
calcPosZ = Mathf.Clamp( calcPosZ, 0, heightmapHeight - 1 );
calcVector.y = heightmapData[ calcPosZ, calcPosX ] * terrainSize.y; // heightmapData is Y,X ; not X,Y (reversed)
calcVector.y += indicatorOffsetY; // raise slightly above terrain
calcVector.z = heightmapPos.z + ( z * indicatorSize );
calcVector.z /= parseFloat( heightmapHeight );
calcVector.z *= terrainSize.z;
mapGrid[ x + 4, z + 4 ] = calcVector;
}
}
}
// --------------------------------------------------------------------------- INDICATOR MESH
private var mesh : Mesh;
private var verts : Vector3[];
private var uvs : Vector2[];
private var tris : int[];
function ConstructMesh()
{
if ( !mesh )
{
mesh = new Mesh();
GetComponent(MeshFilter).mesh = mesh;
mesh.name = gameObject.name + "Mesh";
}
mesh.Clear();
verts = new Vector3[9 * 9];
uvs = new Vector2[9 * 9];
tris = new int[ 8 * 2 * 8 * 3];
var uvStep : float = 1.0 / 8.0;
var index : int = 0;
var triIndex : int = 0;
for ( var z : int = 0; z < 9; z ++ )
{
for ( var x : int = 0; x < 9; x ++ )
{
verts[ index ] = new Vector3( x, 0, z );
uvs[ index ] = new Vector2( parseFloat(x) * uvStep, parseFloat(z) * uvStep );
if ( x < 8 && z < 8 )
{
tris[ triIndex + 0 ] = index + 0;
tris[ triIndex + 1 ] = index + 9;
tris[ triIndex + 2 ] = index + 1;
tris[ triIndex + 3 ] = index + 1;
tris[ triIndex + 4 ] = index + 9;
tris[ triIndex + 5 ] = index + 10;
triIndex += 6;
}
index ++;
}
}
// - Build Mesh -
mesh.vertices = verts;
mesh.uv = uvs;
mesh.triangles = tris;
mesh.RecalculateBounds();
mesh.RecalculateNormals();
}
function UpdateMesh()
{
var index : int = 0;
for ( var z : int = 0; z < 9; z ++ )
{
for ( var x : int = 0; x < 9; x ++ )
{
verts[ index ] = mapGrid[ x, z ];
index ++;
}
}
// assign to mesh
mesh.vertices = verts;
mesh.RecalculateBounds();
mesh.RecalculateNormals();
}
// ---------------------------------------------------------------------------
Since I'm currently using the texture method, I had the following idea: Perhaps one could paint the special grid texture only on parts of the terrain? However, if I want to have different textures than the grass texture, I would have to build also the corresponding grid texture what is very annoying.
– paberrMy suggestion would be to create a new material, and add it like you would paint on a new texture, but set the target strength to something around .3 the only issue is that the inside of the tiles may be slightly shaded. Hope this helps! :D
– AerilanAlso I got the projector solution working. But it looks very much the same as the directional light and I cannot limit the radius or something. But I think it should be somehow possible to only project 1 tile on the terrain so that it is only a matter of placing my projector.
– paberrI finally got a working solution, even if it is not the best: I use a projector to place the grid on the terrain. I use another projector (similar to the blob shadow) to highlight one grid cell. However, for a non-flat terrain the projectors don't show the grid all over the terrain what looks a bit weird.
– paberrHi AlucardJ and everyone -- This script is almost perfect for my application. But, I haven't been able to get it to work. I apply the script to an empty game object and I get console confirmation that the terrain I've created is being use by the script. No errors or anything. But, I can't see the grid. What am I doing wrong here? TIA! D
– GaasPump