I've been making procedural terrain height maps with the diamond square algorithm and the mesh with the triangulation method below:
public Map GenerateMap()
{
Mesh mapMesh = new();
vertices = new Vector3[(Resolution + 1) * (Resolution + 1)];
Vector2[] uv1 = new Vector2[vertices.Length];
Vector2[] uv2 = new Vector2[vertices.Length];
Vector2[] uv3 = new Vector2[vertices.Length];
DiamondSquare diamondSquare = new(Resolution, Roughness, Seed, HeightLevels);
float[,] heightFloatMap = diamondSquare.DoDiamondSquare();
tex = new Texture2D(Resolution, Resolution);
for (int y = 0, i = 0; y <= Resolution; y++)
{
for (int x = 0; x <= Resolution; x++, i++)
{
//float height = heightMap.GetPixel(x,y).r;
float height = heightFloatMap[x, y];
vertices[i] = new Vector3(x * CellSize.x, height * CellSize.y, y * CellSize.z);
tex.SetPixel(x, y, new Color(height, height, height, 1));
if (height == 0)
uv1[i] = new Vector2(vertices[i].x, vertices[i].z);
else if (height < 0.4)
uv2[i] = new Vector2(vertices[i].x, vertices[i].z);
else if (height < 0.4)
uv3[i] = new Vector2(vertices[i].x, vertices[i].z);
}
}
mapMesh.vertices = vertices;
mapMesh.uv = uv1;
mapMesh.uv2 = uv2;
int[] triangles = new int[Resolution * Resolution * 6];
Cell[,] cellMap = new Cell[Resolution / 4, Resolution / 4];
for (int ti = 0, vi = 0, y = 0; y < Resolution; y++, vi++)
{
for (int x = 0; x < Resolution; x++, ti += 6, vi++)
{
triangles[ti] = vi;
triangles[ti + 3] = triangles[ti + 2] = vi + 1;
triangles[ti + 4] = triangles[ti + 1] = vi + Resolution + 1;
triangles[ti + 5] = vi + Resolution + 2;
Vector3[] cellVerts = new Vector3[]
{
vertices[vi], vertices[vi + 1], vertices[vi + Resolution + 1], vertices[vi + Resolution + 2]
};
Cell cell = new(new Vector2Int(x, y), cellVerts, CalculateCellGeometry(cellVerts));
cellMap[x / 4, y / 4] = cell;
}
}
mapMesh.triangles = triangles;
mapMesh.RecalculateNormals();
mapMesh.RecalculateTangents();
mapMesh.RecalculateBounds();
Map map = new(mapMesh, cellMap, heightFloatMap, vertices);
return map;
}
}
This works fine with grid sizes 16x16, 32x32... 256x256 but breaks when I try it on 512x512 or above
256x256
Mesh is perfect
512x512
It successfully triangulates up until the rows starting y=128
On the underside of the terrain there are these bars
I've mapped out the vertices generated from 512x512 and above resolutions and they are all good so I'm 99% sure its down to the triangulation.
I'm new to procedural meshes and am stumped by this issue, any help would be greatly appreciated.
Turns out it wasn't triangulation, the vertex limit was being reached as my mesh was set to use a 16-bit index buffer.
I added this line
mapMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
and the issue is fixed. An annoying oversight on my part but that's part of the learning process!
I'm working on 3d engine. But I have this problem with my translation matrix: it always returns 0, so on screen appears only one dot in the middle. I thought, that something was multiplying by 0. That wasn't the case. I also discovered, that some of the coords are extremely large. I need help.
Here is loop, where I project the points onto screen:
public override void OnUpdate(float elapsed)
{
Matice4x4 m = new Matice4x4();
Matice4x4 matRotZ = new Matice4x4(), matRotX = new Matice4x4();
//rotace z
matRotZ.m[0, 0] = (float)Math.Cos(fTheta);
matRotZ.m[0, 1] = (float)Math.Sin(fTheta);
matRotZ.m[1, 0] = -(float)Math.Sin(fTheta);
matRotZ.m[1, 1] = (float)Math.Cos(fTheta);
matRotZ.m[2, 2] = 1;
matRotZ.m[3, 3] = 1;
// rotace x
matRotX.m[0, 0] = 1;
matRotX.m[0, 1] = (float)Math.Cos(fTheta * 0.5f);
matRotX.m[1, 0] = (float)Math.Sin(fTheta * 0.5f);
matRotX.m[1, 1] = -(float)Math.Sin(fTheta * 0.5f);
matRotX.m[2, 2] = (float)Math.Cos(fTheta * 0.5f);
matRotX.m[3, 3] = 1;
fTheta += 1.0f * elapsed;
Vector3 projekce = new Vector3(), translace = new Vector3(), triRotZ = new Vector3(), triRotXZ = new Vector3();
foreach (Mesh mesh in meshes)
{
for (int i = 0; i < mesh.Vrcholy.Length; i++)
{
//rotace
m.MaticeVysledek(mesh.Vrcholy[i], triRotZ, matRotZ);
m.MaticeVysledek(triRotZ, triRotXZ, matRotX);
//translace
translace = triRotXZ;
translace.Z += 10.0f;
m.MaticeVysledek(translace, projekce, m);
projekce.X += 1.0f;
projekce.Y += 1.0f;
projekce.X *= 0.5f * (float)sirka;
projekce.Y *= 0.5f * (float)vyska;
Draw((int)projekce.X, (int)projekce.Y, Pixel.Presets.Red);
projekce.X = 0;
projekce.Y = 0;
projekce.Z = 0;
}
}
}
And here is the translation matrix:
struct Matice4x4
{
public float[,] m = new float[4, 4];
public Matice4x4()
{
var HE = new HerniEngine();
m[0, 0] = HE.fPomerStran * HE.fFovRad;
m[1, 1] = HE.fFovRad;
m[2, 2] = HE.fFar / (HE.fFar - HE.fNear);
m[3, 2] = (-HE.fFar * HE.fNear) / (HE.fFar - HE.fNear);
m[2, 3] = 1.0f;
m[3, 3] = 0.0f;
}
public void MaticeVysledek(Vector3 i, Vector3 o, Matice4x4 m)
{
o.X = i.X * m.m[0, 0] + i.Y * m.m[1, 0] + i.Z * m.m[2, 0] + m.m[3, 0];
o.Y = i.X * m.m[0, 1] + i.Y * m.m[1, 1] + i.Z * m.m[2, 1] + m.m[3, 1];
o.Z = i.X * m.m[0, 2] + i.Y * m.m[1, 2] + i.Z * m.m[2, 2] + m.m[3, 2];
float w = i.X * m.m[0, 3] + i.Y * m.m[1, 3] + i.Z * m.m[2, 3] + m.m[3, 3];
if (w != 0.0f)
{
o.X /= w;
o.Y /= w;
o.Z /= w;
}
}
}
I'm getting a problem with my terrain manager where I get these weirdly spiked columns instead of normal terrain (if there is a normal with this kind of generation) any ideas?
Image : http://bit.ly/1IVVsET
Code :
public class TerrainManager : MonoBehaviour, ICameraObserver {
public Terrain mainTerrain;
public float terrainChangeRate = 0.0001f;
public int brushArea = 20;
public static int viewDistance = 9;
public static Vector2 VECTOR_WILDCARD = new Vector2(-10000, -10000);
int resolutionX;
int resolutionY;
float[,] heights;
int heightEdit = 1;
//Chunks
List<Vector2> loadedChunks = new List<Vector2>();
Vector2[] visibleChunks = null;
Terrain[] chunkGraphics = new Terrain[viewDistance];
Vector2 curChunkIndex = new Vector2();
int chunkSizeX = 256;
int chunkSizeY = 256;
// Use this for initialization
void Start () {
resolutionX = mainTerrain.terrainData.heightmapWidth;
resolutionY = mainTerrain.terrainData.heightmapHeight;
heights = mainTerrain.terrainData.GetHeights(0, 0, resolutionX, resolutionY);
Camera.main.GetComponent<RTSCamera>().Subscribe(this);
GameObject world = new GameObject();
world.name = "World";
for (int i = 0; i < viewDistance; i++)
{
GameObject go = new GameObject();
go.name = "Chunk_" + i;
go.transform.SetParent(world.transform);
chunkGraphics[i] = go.AddComponent<Terrain>();
chunkGraphics[i].terrainData = new TerrainData();
go.AddComponent<TerrainCollider>().terrainData = chunkGraphics[i].terrainData;
chunkGraphics[i].terrainData.size = new Vector3((int)(chunkSizeX / 4), 600, (int)(chunkSizeY / 4));
chunkGraphics[i].terrainData.heightmapResolution = (int)(chunkSizeX / 2);
}
onCameraMove(Camera.main.transform.position);
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButton(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
editTerrainHeight(hit.point, terrainChangeRate, brushArea);
}
}
}
void editTerrainHeight(Vector3 position, float amount, int diameter)
{
int terrainPosX = (int)((position.x / mainTerrain.terrainData.size.x) * resolutionX);
int terrainPosY = (int)((position.z / mainTerrain.terrainData.size.z) * resolutionY);
float[,] heightChange = new float[diameter, diameter];
int radius = diameter / 2;
if (Input.GetKey(KeyCode.LeftShift))
{
heightEdit = -1;
}
else
{
heightEdit = 1;
}
amount = amount * heightEdit;
for (int x = 0; x < diameter; x++)
{
for (int y = 0; y < diameter; y++)
{
int x2 = x - radius;
int y2 = y - radius;
heightChange[y, x] = heights[terrainPosY + y2, terrainPosX + x2] + amount;
heights[terrainPosY + y2, terrainPosX + x2] = heightChange[y, x];
}
}
mainTerrain.terrainData.SetHeights(terrainPosX - radius, terrainPosY - radius, heightChange);
}
public void onCameraMove(Vector3 newCameraPosition)
{
int chunkIndexX = Mathf.FloorToInt(newCameraPosition.x / chunkSizeX);
int chunkIndexY = Mathf.FloorToInt(newCameraPosition.z / chunkSizeY);
if (curChunkIndex.x == chunkIndexX && curChunkIndex.y == chunkIndexY)
{
return;
}
curChunkIndex.x = chunkIndexX;
curChunkIndex.y = chunkIndexY;
Vector2[] newVisibleChunks = new Vector2[viewDistance];
newVisibleChunks[0] = new Vector2(chunkIndexX - 1, chunkIndexY +1);
newVisibleChunks[1] = new Vector2(chunkIndexX, chunkIndexY +1);
newVisibleChunks[2] = new Vector2(chunkIndexX + 1, chunkIndexY +1);
newVisibleChunks[3] = new Vector2(chunkIndexX -1, chunkIndexY);
newVisibleChunks[4] = new Vector2(chunkIndexX, chunkIndexY);
newVisibleChunks[5] = new Vector2(chunkIndexX + 1, chunkIndexY);
newVisibleChunks[6] = new Vector2(chunkIndexX - 1, chunkIndexY -1);
newVisibleChunks[7] = new Vector2(chunkIndexX, chunkIndexY -1);
newVisibleChunks[8] = new Vector2(chunkIndexX + 1, chunkIndexY -1);
Terrain[] newChunkGraphics = new Terrain[viewDistance];
List<int> freeTerrains = new List<int>();
List<int> loadingIndexes = new List<int>();
for (int i = 0; i < viewDistance; i++)
{
bool found = false;
for (int j = 0; j < viewDistance; j++)
{
if (visibleChunks == null)
{
break;
}
if (newVisibleChunks[i].Equals(visibleChunks[j]))
{
visibleChunks[j] = VECTOR_WILDCARD;
newChunkGraphics[i] = chunkGraphics[j];
found = true;
break;
}
}
if (!found)
{
loadingIndexes.Add(i);
}
}
if (visibleChunks != null)
{
for (int i = 0; i < viewDistance; i++)
{
if (visibleChunks[i] != VECTOR_WILDCARD)
{
freeTerrains.Add(i);
saveChunkToMemory(chunkGraphics[i], visibleChunks[i]);
}
}
}
else
{
for (int i = 0; i < viewDistance; i++)
{
freeTerrains.Add(i);
}
}
for (int i = 0; i < loadingIndexes.Count; i++)
{
loadChunkFromMemory(newVisibleChunks[loadingIndexes[i]], freeTerrains[i]);
newChunkGraphics[loadingIndexes[i]] = chunkGraphics[freeTerrains[i]];
}
visibleChunks = newVisibleChunks;
chunkGraphics = newChunkGraphics;
}
void loadChunkFromMemory(Vector2 cordIndex, int graphicIndex)
{
bool found = false;
foreach (Vector2 v in loadedChunks)
{
if (v == cordIndex)
{
found = true;
break;
}
}
GameObject terrainGO;
if (!found)
{
terrainGO = generateChunk(cordIndex, graphicIndex);
}
else
{
//Load Chunk from Memory
Debug.Log("Loading Chunk(" + cordIndex.x + "," + cordIndex.y + ")");
terrainGO = chunkGraphics[graphicIndex].gameObject;
}
terrainGO.transform.position = new Vector3(chunkSizeX * cordIndex.x, 0, chunkSizeY * cordIndex.y);
}
GameObject generateChunk(Vector2 cordIndex, int graphicIndex)
{
GameObject terrainGO = chunkGraphics[graphicIndex].gameObject;
loadedChunks.Add(cordIndex);
setTerrainHeightMap(terrainGO.GetComponent<Terrain>(), cordIndex);
return terrainGO;
}
void setTerrainHeightMap(Terrain terrain, Vector2 cordIndex)
{
float[,] heights = new float[terrain.terrainData.heightmapHeight, terrain.terrainData.heightmapWidth];
heights[0, 0] = 0.5f;
heights[terrain.terrainData.heightmapWidth - 1, 0] = 0.5f;
heights[0, terrain.terrainData.heightmapHeight - 1] = 0.5f;
heights[terrain.terrainData.heightmapWidth - 1, terrain.terrainData.heightmapHeight - 1] = 0.5f;
heights = diamondSquare(heights, 0, 0, terrain.terrainData.heightmapWidth - 1, 0);
terrain.terrainData.SetHeights(0, 0, heights);
}
float[,] getTerrainHeightMap(Vector2 cordIndex)
{
return null;
}
float[,] diamondSquare(float[,] heights, int offSetX, int offSetY, int squareSize, int depth)
{
if (squareSize == 1)
{
return heights;
}
float topLeft = heights[offSetY, offSetX];
float topRight = heights[offSetY, offSetX + squareSize];
float bottomLeft = heights[offSetY + squareSize, offSetX];
float bottomRight = heights[offSetY + squareSize, offSetX + squareSize];
int size = squareSize / 2;
if (topLeft == 0 || topRight == 0 || bottomLeft == 0 || bottomRight == 0)
{
Debug.LogError("One or more Corner Seeds have not been set..");
}
if (heights[offSetY + size, offSetX + size] == 0)
{
heights[offSetY + size, offSetX + size] = getRandomHeight(depth + (int)averagePoints(topLeft, topRight, bottomLeft, bottomRight));
}
float centrePoint = heights[offSetY + size, offSetX + size];
//left Diamond
float runningAverage = averagePoints(topLeft, centrePoint, bottomLeft);
if (offSetX - size > 0 && heights[offSetY + size, offSetX - size] != 0)
{
runningAverage = averagePoints(topLeft, centrePoint, bottomLeft, heights[offSetY + size, offSetX - size]);
}
if (heights[offSetY + size, offSetX] == 0)
{
heights[offSetY + size, offSetX] = runningAverage + getRandomHeight(depth);
}
//right Diamond
runningAverage = averagePoints(topRight, centrePoint, bottomRight);
if (offSetX + (squareSize * 1.5f) < heights.GetLength(1) && heights[offSetY + size, offSetX + (int)(squareSize * 1.5f)] != 0)
{
runningAverage = averagePoints(topRight, centrePoint, bottomRight, heights[offSetY + size, offSetX + (int)(squareSize * 1.5f)]);
}
if (heights[offSetY + size, offSetX + squareSize] == 0)
{
heights[offSetY + size, offSetX + squareSize] = runningAverage + getRandomHeight(depth);
}
//top Diamond
runningAverage = averagePoints(topLeft, centrePoint, topRight);
if (offSetY - size > 0 && heights[offSetY - size, offSetX + size] != 0)
{
runningAverage = averagePoints(topLeft, centrePoint, topRight, heights[offSetY - size, offSetX + size]);
}
if (heights[offSetY, offSetX + size] == 0)
{
heights[offSetY, offSetX + size] = runningAverage + getRandomHeight(depth);
}
//bottom Diamond
runningAverage = averagePoints(bottomRight, centrePoint, bottomLeft);
if (offSetY + (squareSize * 1.5f) < heights.GetLength(0) && heights[offSetY + (int)(squareSize * 1.5f), offSetX + size] != 0)
{
runningAverage = averagePoints(bottomRight, centrePoint, topRight, heights[offSetY + (int)(squareSize * 1.5f), offSetX + size]);
}
if (heights[offSetY + squareSize, offSetX + size] == 0)
{
heights[offSetY + squareSize, offSetX + size] = runningAverage + getRandomHeight(depth);
}
heights = diamondSquare(heights, offSetX, offSetY, size, depth + 1);
heights = diamondSquare(heights, offSetX + size, offSetY, size, depth + 1);
heights = diamondSquare(heights, offSetX, offSetY + size, size, depth + 1);
heights = diamondSquare(heights, offSetX + size, offSetY + size, size, depth + 1);
return heights;
}
float averagePoints(float p1, float p2, float p3, float p4)
{
return (p1 + p2 + p3 + p4) * 0.25f;
}
float averagePoints(float p1, float p2, float p3)
{
return (p1 + p2 + p3) * 0.3333f;
}
float getRandomHeight(int depth)
{
return Random.Range(-0.1f, 0.0f) / Mathf.Pow(2, depth);
}
void saveChunkToMemory(Terrain chunk, Vector2 index)
{
Debug.Log("Unloading Chunk(" + index.x + "," + index.y + ")");
}
}
So after a few more hours here is a fix :D
public class TerrainManager : MonoBehaviour, ICameraObserver {
public Terrain mainTerrain;
public float terrainChangeRate = 0.0001f;
public int brushArea = 20;
public static int viewDistance = 9;
public static Vector2 VECTOR_WILDCARD = new Vector2(-10000, -10000);
int resolutionX;
int resolutionY;
float[,] heights;
int heightEdit = 1;
//Chunks
List<Vector2> loadedChunks = new List<Vector2>();
Vector2[] visibleChunks = null;
Terrain[] chunkGraphics = new Terrain[viewDistance];
Vector2 curChunkIndex = new Vector2();
int chunkSizeX = 256;
int chunkSizeY = 256;
// Use this for initialization
void Start () {
resolutionX = mainTerrain.terrainData.heightmapWidth;
resolutionY = mainTerrain.terrainData.heightmapHeight;
heights = mainTerrain.terrainData.GetHeights(0, 0, resolutionX, resolutionY);
Camera.main.GetComponent<RTSCamera>().Subscribe(this);
GameObject world = new GameObject();
world.name = "World";
for (int i = 0; i < viewDistance; i++)
{
GameObject go = new GameObject();
go.name = "Chunk_" + i;
go.transform.SetParent(world.transform);
chunkGraphics[i] = go.AddComponent<Terrain>();
chunkGraphics[i].terrainData = new TerrainData();
go.AddComponent<TerrainCollider>().terrainData = chunkGraphics[i].terrainData;
chunkGraphics[i].terrainData.size = new Vector3((int)(chunkSizeX / 4), 600, (int)(chunkSizeY / 4));
chunkGraphics[i].terrainData.heightmapResolution = (int)(chunkSizeX / 2);
}
onCameraMove(Camera.main.transform.position);
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButton(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
editTerrainHeight(hit.point, terrainChangeRate, brushArea);
}
}
}
void editTerrainHeight(Vector3 position, float amount, int diameter)
{
int terrainPosX = (int)((position.x / mainTerrain.terrainData.size.x) * resolutionX);
int terrainPosY = (int)((position.z / mainTerrain.terrainData.size.z) * resolutionY);
float[,] heightChange = new float[diameter, diameter];
int radius = diameter / 2;
if (Input.GetKey(KeyCode.LeftShift))
{
heightEdit = -1;
}
else
{
heightEdit = 1;
}
amount = amount * heightEdit;
for (int x = 0; x < diameter; x++)
{
for (int y = 0; y < diameter; y++)
{
int x2 = x - radius;
int y2 = y - radius;
heightChange[y, x] = heights[terrainPosY + y2, terrainPosX + x2] + amount;
heights[terrainPosY + y2, terrainPosX + x2] = heightChange[y, x];
}
}
mainTerrain.terrainData.SetHeights(terrainPosX - radius, terrainPosY - radius, heightChange);
}
public void onCameraMove(Vector3 newCameraPosition)
{
//Debug.Log ("Camera Moved");
int chunkIndexX = Mathf.FloorToInt (newCameraPosition.x / chunkSizeX);
int chunkIndexY = Mathf.FloorToInt (newCameraPosition.z / chunkSizeY);
if (curChunkIndex.x == chunkIndexX && curChunkIndex.y == chunkIndexY) {
return;
}
curChunkIndex.x = chunkIndexX;
curChunkIndex.y = chunkIndexY;
//Debug.Log ("Chunk Index ( " + chunkIndexX + ", " + chunkIndexY + " )");
Vector2[] newVisibleChunks = new Vector2[9];
newVisibleChunks [0] = new Vector2 (chunkIndexX -1, chunkIndexY +1);
newVisibleChunks [1] = new Vector2 (chunkIndexX, chunkIndexY +1);
newVisibleChunks [2] = new Vector2 (chunkIndexX +1, chunkIndexY +1);
newVisibleChunks [3] = new Vector2 (chunkIndexX -1, chunkIndexY);
newVisibleChunks [4] = new Vector2 (chunkIndexX, chunkIndexY);
newVisibleChunks [5] = new Vector2 (chunkIndexX +1, chunkIndexY);
newVisibleChunks [6] = new Vector2 (chunkIndexX -1, chunkIndexY -1);
newVisibleChunks [7] = new Vector2 (chunkIndexX, chunkIndexY -1);
newVisibleChunks [8] = new Vector2 (chunkIndexX +1, chunkIndexY -1);
Terrain[] newChunkGraphics = new Terrain[chunkGraphics.Length];
List<int> freeTerrains = new List<int>();
List<int> loadingIndexes = new List<int>();
//List<int> newIndex = new List<int>();
for (int i =0; i <9; i++) {
bool found = false;
for (int j =0; j <9; j++) {
if (visibleChunks == null)
break;
if (newVisibleChunks[i].Equals (visibleChunks[j])){
visibleChunks[j] = VECTOR_WILDCARD;
newChunkGraphics[i] = chunkGraphics[j];
found = true;
break;
}
}
if (!found){
loadingIndexes.Add (i);
}
}
if (visibleChunks != null) {
for (int i = 0; i< 9; i++) {
if (visibleChunks [i] != VECTOR_WILDCARD) {
freeTerrains.Add (i);
saveChunkToMemory (chunkGraphics [i], visibleChunks [i]);
}
}
} else {
for (int i = 0; i< 9; i++) {
freeTerrains.Add (i);
}
}
for (int i = 0; i < loadingIndexes.Count; i++) {
newChunkGraphics[loadingIndexes[i]] = chunkGraphics[freeTerrains[i]];
}
visibleChunks = newVisibleChunks;
chunkGraphics = newChunkGraphics;
for (int i = 0; i < loadingIndexes.Count; i++) {
loadChunkFromMemory(visibleChunks[loadingIndexes[i]], loadingIndexes[i]);
}
}
void loadChunkFromMemory(Vector2 cordIndex, int graphicIndex)
{
bool found = false;
foreach (Vector2 v in loadedChunks)
{
if (v == cordIndex)
{
found = true;
break;
}
}
GameObject terrainGO;
if (!found)
{
terrainGO = generateChunk(cordIndex, graphicIndex);
}
else
{
//Load Chunk from Memory
Debug.Log("Loading Chunk(" + cordIndex.x + "," + cordIndex.y + ")");
terrainGO = chunkGraphics[graphicIndex].gameObject;
}
terrainGO.transform.position = new Vector3(chunkSizeX * cordIndex.x, 0, chunkSizeY * cordIndex.y);
}
GameObject generateChunk(Vector2 cordIndex, int graphicIndex)
{
GameObject terrainGO = chunkGraphics[graphicIndex].gameObject;
loadedChunks.Add(cordIndex);
setTerrainHeightMap(terrainGO.GetComponent<Terrain>(), cordIndex);
return terrainGO;
}
void setTerrainHeightMap(Terrain terrain, Vector2 cordIndex)
{
float[,] heights = new float[terrain.terrainData.heightmapHeight, terrain.terrainData.heightmapWidth];
bool left = false;
bool right = false;
bool top = false;
bool bottom = false;
//left
float[,] hm = getTerrainHeightMap(new Vector2(cordIndex.x - 1, cordIndex.y));
if(hm != null)
{
left = true;
for (int i = 0; i < hm.GetLength(0); i++)
{
heights[i, 0] = hm[i, hm.GetLength(1) - 1];
}
}
//Right
hm = getTerrainHeightMap(new Vector2(cordIndex.x + 1, cordIndex.y));
if (hm != null)
{
right = true;
for (int i = 0; i < hm.GetLength(0); i++)
{
heights[i, heights.GetLength(1) - 1] = hm[i, 0];
}
}
hm = getTerrainHeightMap(new Vector2(cordIndex.x, cordIndex.y - 1));
if (hm != null)
{
top = true;
for (int i = 0; i < hm.GetLength(1); i++)
{
heights[i, 0] = hm[hm.GetLength(0) - 1, i];
}
}
hm = getTerrainHeightMap(new Vector2(cordIndex.x, cordIndex.y + 1));
if (hm != null)
{
bottom = true;
for (int i = 0; i < hm.GetLength(0); i++)
{
heights[heights.GetLength(1) - 1, i] = hm [0, i];
}
}
if (!top && !left)
{
heights[0, 0] = 0.2f;
}
if (!bottom && !left)
{
heights[terrain.terrainData.heightmapHeight - 1, 0] = 0.2f;
}
if (!top && !right)
{
heights[0, terrain.terrainData.heightmapWidth - 1] = 0.2f;
}
if (!bottom && !right)
{
heights[terrain.terrainData.heightmapHeight - 1, terrain.terrainData.heightmapWidth - 1] = 0.2f;
}
heights[0, 0] = 0.2f;
heights[terrain.terrainData.heightmapHeight - 1, 0] = 0.2f;
heights[0, terrain.terrainData.heightmapWidth - 1] = 0.2f;
heights[terrain.terrainData.heightmapHeight - 1, terrain.terrainData.heightmapWidth - 1] = 0.2f;
heights = diamondSquare(heights, 0, 0, terrain.terrainData.heightmapWidth - 1, 0);
terrain.terrainData.SetHeights(0, 0, heights);
}
float[,] getTerrainHeightMap(Vector2 cordIndex)
{
if (loadedChunks.Contains(cordIndex))
{
for (int i = 0; i < visibleChunks.Length; i++)
{
if (visibleChunks[i].x == cordIndex.x && visibleChunks[i].y == cordIndex.y)
{
return chunkGraphics[i].terrainData.GetHeights(0, 0, chunkGraphics[i].terrainData.heightmapWidth, chunkGraphics[i].terrainData.heightmapWidth);
}
}
return loadHeightMapFromMemory(cordIndex);
}
else
{
return null;
}
}
float[,] loadHeightMapFromMemory(Vector2 cordIndex)
{
return null;
}
float[,] diamondSquare(float[,] heights, int offSetX, int offSetY, int squareSize, int depth)
{
if (squareSize == 1)
return heights;
float topLeft = heights[offSetY, offSetX];
float topRight = heights[offSetY, offSetX + squareSize];
float bottomLeft = heights[offSetY + squareSize, offSetX];
float bottomRight = heights[offSetY + squareSize, offSetX + squareSize];
if (topLeft == 0 || topRight == 0 || bottomLeft == 0 || bottomRight == 0)
{
Debug.LogError("One or more Corner Seed Values is not set");
}
if (heights[offSetY + (squareSize / 2), offSetX + (squareSize / 2)] == 0)
{
heights[offSetY + (squareSize / 2), offSetX + (squareSize / 2)] = getRandomHeight(depth) + averagePoints(topLeft, topRight, bottomLeft, bottomRight);
}
float centrePoint = heights[offSetY + (squareSize / 2), offSetX + (squareSize / 2)];
//left diamond
float runningAverage = averagePoints(topLeft, centrePoint, bottomLeft);
if (offSetX - (squareSize / 2) > 0 && heights[offSetY + (squareSize / 2), offSetX - (squareSize / 2)] != 0)
{
runningAverage = averagePoints(topLeft, centrePoint, bottomLeft, heights[offSetY + (squareSize / 2), offSetX - (squareSize / 2)]);
}
if (heights[offSetY + (squareSize / 2), offSetX] == 0)
{
heights[offSetY + (squareSize / 2), offSetX] = runningAverage + getRandomHeight(depth);
}
//right diamond
runningAverage = averagePoints(topRight, centrePoint, bottomRight);
if (offSetX + (squareSize * 1.5f) < heights.GetLength(1) && heights[offSetY + (squareSize / 2), offSetX + (int)(squareSize * 1.5f)] != 0)
{
runningAverage = averagePoints(topRight, centrePoint, bottomRight, heights[offSetY + (squareSize / 2), offSetX + (int)(squareSize * 1.5f)]);
}
if (heights[offSetY + (squareSize / 2), offSetX + squareSize] == 0)
{
heights[offSetY + (squareSize / 2), offSetX + squareSize] = runningAverage + getRandomHeight(depth);
}
//top diamond
runningAverage = averagePoints(topLeft, centrePoint, topRight);
if (offSetY - (squareSize / 2) > 0 && heights[offSetY - (squareSize / 2), offSetX + (squareSize / 2)] != 0)
{
runningAverage = averagePoints(topLeft, centrePoint, topRight, heights[offSetY - (squareSize / 2), offSetX + (squareSize / 2)]);
}
if (heights[offSetY, offSetX + (squareSize / 2)] == 0)
{
heights[offSetY, offSetX + (squareSize / 2)] = runningAverage + getRandomHeight(depth);
}
//bottom diamond
runningAverage = averagePoints(bottomRight, centrePoint, bottomLeft);
if (offSetY + (int)(squareSize * 1.5f) < heights.GetLength(0) && heights[offSetY + (int)(squareSize * 1.5f), offSetX + (squareSize / 2)] != 0)
{
runningAverage = averagePoints(bottomRight, centrePoint, topRight, heights[offSetY + (int)(squareSize * 1.5f), offSetX + (squareSize / 2)]);
}
if (heights[offSetY + squareSize, offSetX + (squareSize / 2)] == 0)
{
heights[offSetY + squareSize, offSetX + (squareSize / 2)] = runningAverage + getRandomHeight(depth);
}
heights = diamondSquare(heights, offSetX, offSetY, squareSize / 2, depth + 1);//top left
heights = diamondSquare(heights, offSetX + (squareSize / 2), offSetY, squareSize / 2, depth + 1);//top right
heights = diamondSquare(heights, offSetX, offSetY + (squareSize / 2), squareSize / 2, depth + 1);//bottom left
heights = diamondSquare(heights, offSetX + (squareSize / 2), offSetY + (squareSize / 2), squareSize / 2, depth + 1);//bottom right'
return heights;
}
float averagePoints(float p1, float p2, float p3, float p4)
{
return (p1 + p2 + p3 + p4) / 4;
}
float averagePoints(float p1, float p2, float p3)
{
return (p1 + p2 + p3) / 3;
}
float getRandomHeight(int depth)
{
return Random.Range(-0.1f, 0.1f) / Mathf.Pow(2, depth);
}
void saveChunkToMemory(Terrain chunk, Vector2 index)
{
Debug.Log("Unloading Chunk(" + index.x + "," + index.y + ")");
}