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 + ")");
}
Related
I read in stackoverflow before about Bresenham algorithm use for a particular environment like low-language, but I have to compare it in my report so any ideal for my report. How can I prove Bresenham is faster than DDA. Now I created a simple paint on winform C# and the time when I draw with 2 different method is likely equal, sometime DDA is faster. Here is my code in both method
Bresenham Algorithm
List<Point> vertices = new List<Point>();
int deltaX = xN - x0; int signX = deltaX >= 0 ? 1 : -1;
int deltaY = yN - y0; int signY = deltaY >= 0 ? 1 : -1;
if (deltaX == deltaY && deltaX * deltaY == 0) return vertices;
// |dy|/|dx| < 1 => |dy| < |dx| => m < 1
if (Math.Abs(deltaX) > Math.Abs(deltaY))
{
int _2deltaX = deltaX * 2 * signX;
int _2deltaY = deltaY * 2 * signY;
int p0 = _2deltaY - deltaX * signX;
// create array to contain vertices
vertices.Add(new Point(x0, y0));
int xCurrent = x0;
int yCurrent = y0;
while (true)
{
if (count >= (Math.Abs(deltaX) + 1)) return vertices;
if (p0 < 0)
{
xCurrent += signX;
// pk + 1 = pk + 2.∆y.signX
p0 = p0 + _2deltaY;
}
else
{
xCurrent += signX;
yCurrent += signY;
// pk+1= pk + 2.∆y.signX - 2.∆x.signY
p0 = p0 + _2deltaY - _2deltaX;
}
vertices.Add(new Point(xCurrent, yCurrent));
}
}
// |dy|/|dx| > 1 => |dy| > |dx| => m > 1
else if (Math.Abs(deltaX) <= Math.Abs(deltaY))
{
int _2deltaX = deltaX * 2 * signX;
int _2deltaY = deltaY * 2 * signY;
int p0 = _2deltaX - deltaY * signY;
// create array to contain vertices
vertices.Add(new Point(x0, y0));
int xCurrent = x0;
int yCurrent = y0;
while (true)
{
if (count >= (Math.Abs(deltaY) + 1)) return vertices;
if (p0 < 0)
{
yCurrent += signY;
// pk + 1 = pk + 2.∆x.signY
p0 = p0 + _2deltaX;
}
else
{
xCurrent += signX;
yCurrent += signY;
// pk+1= pk + 2.∆x.signY - 2.∆y.signX
p0 = p0 + _2deltaX - _2deltaY;
}
vertices.Add(new Point(xCurrent, yCurrent));
}
}
return vertices;
DDA Algorithm
List<Point> vertices = new List<Point>();
int deltaX = xN - x0; int signX = deltaX >= 0 ? 1 : -1;
int deltaY = yN - y0; int signY = deltaY >= 0 ? 1 : -1;
if (deltaX == deltaY && deltaX * deltaY == 0) return vertices;
int step = Math.Abs(deltaX) > Math.Abs(deltaY) ? Math.Abs(deltaX) : Math.Abs(deltaY);
// x(k + 1) = xk + x'
double stepX = deltaX * 1.0 / step;
double stepY = deltaY * 1.0 / step;
vertices.Add(new Point(x0, y0));
double xCurrent = x0;
double yCurrent = y0;
for (int i = 0; i < step; i++)
{
xCurrent += stepX;
yCurrent += stepY;
vertices.Add(new Point((int)Math.Round(xCurrent), (int)Math.Round(yCurrent)));
}
return vertices;
So my code I am using is below (C# in Unity)
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
public class PolygonGenerator : MonoBehaviour
{
public List<Vector3> newVertices = new List<Vector3>();
public List<int> newTriangles = new List<int>();
public List<Vector2> newUV = new List<Vector2>();
private Mesh mesh;
private float tUnitY = 0.33333f; //These are just objects I've given IDs for textures
private float tUnitX = 0.166667f;
private Vector2 tDirtBL = new Vector2(0, 0);
private Vector2 tDirtB = new Vector2(1, 0);
private Vector2 tDirtBR = new Vector2(2, 0);
private Vector2 tDirtL = new Vector2(0, 1);
private Vector2 tDirt = new Vector2(1, 1);
private Vector2 tDirtR = new Vector2(2, 1);
private Vector2 tDirtUL = new Vector2(0, 2);
private Vector2 tDirtU = new Vector2(1, 2);
private Vector2 tDirtUR = new Vector2(2, 2);
private Vector2 tStone = new Vector2(4, 1);
private int squareCount;
public byte[,] blocks;
public List<Vector3> colVertices = new List<Vector3>();
public List<int> colTriangles = new List<int>();
private int colCount;
private EdgeCollider2D col;
void Start()
{
mesh = GetComponent<MeshFilter>().mesh;
col = GetComponent<EdgeCollider2D>();
float x = transform.position.x;
float y = transform.position.y;
float z = transform.position.z;
GenTerrain();
BuildMesh();
MeshUpdate();
}
void GenSquare(int x, int y, Vector2 texture) //This creates the blocks I can apply textures to
{
newVertices.Add(new Vector3(x, y, 0));
newVertices.Add(new Vector3(x + 1, y, 0));
newVertices.Add(new Vector3(x + 1, y - 1, 0));
newVertices.Add(new Vector3(x, y - 1, 0));
newTriangles.Add(squareCount * 4);
newTriangles.Add((squareCount * 4) + 1);
newTriangles.Add((squareCount * 4) + 3);
newTriangles.Add((squareCount * 4) + 1);
newTriangles.Add((squareCount * 4) + 2);
newTriangles.Add((squareCount * 4) + 3);
newUV.Add(new Vector2(tUnitX * texture.x, tUnitY * texture.y + tUnitY));
newUV.Add(new Vector2(tUnitX * texture.x + tUnitX, tUnitY * texture.y + tUnitY));
newUV.Add(new Vector2(tUnitX * texture.x + tUnitX, tUnitY * texture.y));
newUV.Add(new Vector2(tUnitX * texture.x, tUnitY * texture.y));
squareCount++;
}
void MeshUpdate() //This merely updates the mesh
{
mesh.Clear();
mesh.vertices = newVertices.ToArray();
mesh.triangles = newTriangles.ToArray();
mesh.uv = newUV.ToArray();
mesh.RecalculateNormals();
squareCount = 0;
newVertices.Clear();
newTriangles.Clear();
newUV.Clear();
Mesh newMesh = new Mesh();
newMesh.vertices = colVertices.ToArray();
newMesh.triangles = colTriangles.ToArray();
col.sharedMaterial = newMesh;
colVertices.Clear();
colTriangles.Clear();
colCount = 0;
}
void GenTerrain() //Generates terrain based on parameters I supply and I can add in
{ //PerlinNoise to create variable terrain instead of flat
blocks = new byte[512, 128];
for (int px = 0; px < blocks.GetLength(0); px++) //Also tells which blocks are which
{
int stone = Noise(px, 0, 80, 15, 1);
stone += Noise(px, 0, 50, 30, 1);
stone += Noise(px, 0, 10, 10, 1);
stone += 75;
print(stone);
int dirt = Noise(px, 0, 25f, 35, 1);
dirt += Noise(px, 100, 50, 30, 1);
dirt += 75;
for (int py = 0; py < blocks.GetLength(1); py++)
{
if (py < stone)
{
blocks[px, py] = 1;
//The next three lines make dirt spots in random places
if (Noise(px, py, 12, 16, 1) > 10)
{
blocks[px, py] = 2;
}
//The next three lines remove dirt and rock to make caves in certain places
if (Noise(px, py * 2, 16, 14, 1) > 10)
{ //Caves
blocks[px, py] = 0;
}
}
else if (py < dirt)
{
blocks[px, py] = 2;
}
}
}
}
void BuildMesh() //Mesh Creation
{
for (int px = 0; px < blocks.GetLength(0); px++)
{
for (int py = 0; py < blocks.GetLength(1); py++)
{
if (blocks[px, py] != 0)
{
GenCollider(px, py);
}
if (blocks[px, py] == 1)
{
GenSquare(px, py, tStone);
}
else if (blocks[px, py] == 2)
{
GenSquare(px, py, tDirtU);
}
}
}
}
void GenCollider(int x, int y)
{
//Top
if (Block(x, y + 1) == 0)
{
colVertices.Add(new Vector3(x, y, 1));
colVertices.Add(new Vector3(x + 1, y, 1));
colVertices.Add(new Vector3(x + 1, y, 0));
colVertices.Add(new Vector3(x, y, 0));
ColliderTriangles();
colCount++;
}
//Bottom
if (Block(x, y - 1) == 0)
{
colVertices.Add(new Vector3(x, y - 1, 0));
colVertices.Add(new Vector3(x + 1, y - 1, 0));
colVertices.Add(new Vector3(x + 1, y - 1, 1));
colVertices.Add(new Vector3(x, y - 1, 1));
ColliderTriangles();
colCount++;
}
//Left
if (Block(x - 1, y) == 0)
{
colVertices.Add(new Vector3(x, y - 1, 1));
colVertices.Add(new Vector3(x, y, 1));
colVertices.Add(new Vector3(x, y, 0));
colVertices.Add(new Vector3(x, y - 1, 0));
ColliderTriangles();
colCount++;
}
//Right
if (Block(x + 1, y) == 0)
{
colVertices.Add(new Vector3(x + 1, y, 1));
colVertices.Add(new Vector3(x + 1, y - 1, 1));
colVertices.Add(new Vector3(x + 1, y - 1, 0));
colVertices.Add(new Vector3(x + 1, y, 0));
ColliderTriangles();
colCount++;
}
}
void ColliderTriangles()
{
colTriangles.Add(colCount * 4);
colTriangles.Add((colCount * 4) + 1);
colTriangles.Add((colCount * 4) + 3);
colTriangles.Add((colCount * 4) + 1);
colTriangles.Add((colCount * 4) + 2);
colTriangles.Add((colCount * 4) + 3);
}
byte Block(int x, int y)
{
if (x == -1 || x == blocks.GetLength(0) || y == -1 || y == blocks.GetLength(1))
{
return (byte)1;
}
return blocks[x, y];
}
int Noise(int x, int y, float scale, float mag, float exp)
{
return (int)(Mathf.Pow((Mathf.PerlinNoise(x / scale, y / scale) * mag), (exp)));
}
}
The code has been modified from its original in order to accommodate BoxCollider2D but is giving me an error. This script is used alongside a Mesh Filter, Mesh Collider (Trying to replace this with a box collider for a 2D game), and a Mesh Renderer on an Empty that was created in Unity.
Is there any viable solution I can use, instead of generating a mesh collider alongside the tiles, in order to make a box collider generate with it? If any further details are needed just ask.
I am working on Hilbert curve, and I cant rotate the whole figure, just lower rectangle(look at the screenshots, on third and next steps I have a problem)
first 3 steps
I have a Figure class. I use it to store every figure and build next firuge from previous one. Here is my code
public class Fragment
{
public static int PADDING = 50;
public static float sideLength;
private readonly Pen crimsonPen = new Pen(Color.Crimson);
public List<PointF> pointsF = new List<PointF>();
public PointF[] points;
public Fragment(int step, Graphics graphics)
{
sideLength = Form1.sideLenght;
points = new PointF[(int)Math.Pow(4, step + 1)];
if (step.Equals(0))
{
points[0] = new PointF(PADDING, PADDING + sideLength);
points[1] = new PointF(PADDING, PADDING);
points[2] = new PointF(PADDING + sideLength, PADDING);
points[3] = new PointF(PADDING + sideLength, PADDING + sideLength);
graphics.DrawLines(crimsonPen, new[] { points[0], points[1], points[2], points[3] });
}
else
{
var frag = Form1.fragments[step - 1];
for (var i = 0; i < step; i++)
{
PointF tmpPoint;
// left lower #1
for (int j = frag.points.Length - 1; j >= 0; j--)
{
points[frag.points.Length - 1 - j] = frag.points[j];
points[frag.points.Length - 1 - j].Y += sideLength * 2 * (i + 1);
}
//rotate left lower #1
for (int b = 0; b < Math.Pow(4, step) - 1; b++)
{
tmpPoint = points[0];
for (int j = 0; j < frag.points.Length; j++)
{
if (j.Equals(frag.points.Length - 1))
{
points[j] = tmpPoint;
}
else
{
points[j] = points[j + 1];
}
}
}
// left upper #2
for (int j = 0; j < frag.points.Length; j++)
{
points[j + frag.points.Length] = frag.points[j];
}
// right upper #3
for (int j = 0; j < frag.points.Length; j++)
{
points[j + 2 * frag.points.Length] = points[j + frag.points.Length];
points[j + 2 * frag.points.Length].X += sideLength * 2 * (i + 1);
}
//right lower #4
for (int j = frag.points.Length - 1; j >= 0; j--)
{
points[3 * frag.points.Length + j] = points[2 * frag.points.Length + frag.points.Length - j - 1];
points[3 * frag.points.Length + j].Y += sideLength * 2 * (i + 1);
}
tmpPoint = points[3 * frag.points.Length];
//rotate right lower #4
for (int j = 0; j < frag.points.Length; j++)
{
if (j.Equals(frag.points.Length - 1))
{
points[4 * (frag.points.Length) - 1] = tmpPoint;
}
else
{
points[3 * frag.points.Length + j] = points[3 * frag.points.Length + j + 1];
}
}
}
graphics.DrawLines(crimsonPen, points);
}
}
}
Here I use my recursive method to draw figures
private void drawButton_Click(object sender, EventArgs e)
{
canvas.Refresh();
count = 0;
if (Int32.TryParse(stepsTextBox.Text, out steps))
{
sideLenght = (float)((canvas.Width - 100) / (Math.Pow(2, steps) - 1));
fragments = new Fragment[steps];
drawCurve();
}
else
{
MessageBox.Show("Wow, incorrect input", "Try again");
}
}
private void drawCurve()
{
if (count < steps)
{
fragments[count] = new Fragment(count, graphics);
++count;
drawCurve();
}
}
I've tried to rotate points around figure center and use next code but the rotation is incorrect
public PointF rotatePoint(PointF pointToRotate)
{
pointToRotate.X = (float)(Math.Cos(180 * Math.PI / 180) * (pointToRotate.X - centerPoint.X) -
Math.Sin(180 * Math.PI / 180) * (pointToRotate.Y - centerPoint.Y) +
centerPoint.X);
pointToRotate.Y = (float)(Math.Sin(0 * Math.PI / 180) * (pointToRotate.X - centerPoint.X) +
Math.Cos(0 * Math.PI / 180) * (pointToRotate.Y - centerPoint.Y) +
centerPoint.Y);
return pointToRotate;
}
The problem is that you are using the X co-ordinate that you have already rotated when you calculate the rotated Y co-ordinate. Use a temp variable to avoid this:
public PointF rotatePoint(PointF pointToRotate)
{
float rotatedX = (float)(Math.Cos(180 * Math.PI / 180) * (pointToRotate.X - centerPoint.X) -
Math.Sin(180 * Math.PI / 180) * (pointToRotate.Y - centerPoint.Y) +
centerPoint.X);
pointToRotate.Y = (float)(Math.Sin(0 * Math.PI / 180) * (pointToRotate.X - centerPoint.X) +
Math.Cos(0 * Math.PI / 180) * (pointToRotate.Y - centerPoint.Y) +
centerPoint.Y);
pointToRotate.X = rotatedX;
return pointToRotate;
}
I am pretty new to the 3D side of XNA and am converting a 2D game.
Its basically designed as a grid and I am drawing with the code below. This works but is a bit laggy, am I doing this completely wrong? Presumably there shouldn't be much lag at this point when we are only talking about a few small models.
protected override void Draw(GameTime gameTime)
{
fpsTimer += gameTime.ElapsedGameTime;
fpsCount++;
if (fpsTimer >= TimeSpan.FromSeconds(1))
{ fpsTimer = TimeSpan.FromSeconds(0); fps = fpsCount; fpsCount = 0; }
GraphicsDevice.Clear(Color.CornflowerBlue);
projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 1.0f, 1000.0f);
world = Matrix.CreateTranslation(new Vector3(0, 0, 0));
view = Matrix.CreateLookAt(new Vector3(xPos, yPos, zHeight), new Vector3(xPos, yPos + zRotation, 0), new Vector3(0, 5, 0));
// DRAW OBJECTS ON MAP
drawMap(view, world, projection);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
spriteBatch.DrawString(font, "" + fps, new Vector2(100, 100), Color.Black);
spriteBatch.End();
base.Draw(gameTime);
}
private void drawMap(Matrix view, Matrix world, Matrix projection)
{
GraphicsDevice.BlendState = BlendState.Opaque;
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
int scale = Globals.scale;
int screenWidthScale = Globals.screenwidth / scale;
int screenHeightScale = Globals.screenheight / scale;
int mapXtile = Convert.ToInt32(xPos/2);
int mapYtile = Convert.ToInt32(yPos/2);
for (int i = Convert.ToInt32(xPos/2) - 30; i < Convert.ToInt32(xPos/2) + 30; i++)
{
for (int a = Convert.ToInt32(yPos/2); a < Convert.ToInt32(yPos/2) + 50; a++)
{
if (mapXtile > 0 && mapYtile > 0 && mapXtile < Globals.mapsizex && mapYtile < Globals.mapsizey)
{
int currentTile = Globals.levelArray[mapXtile, mapYtile].TyleType;
// DRAW TREES
if (currentTile == tyleType.tree)
{
if (Globals.levelArray[mapXtile, mapYtile].typeModifier == 1)
{
Matrix worldMatrix = Matrix.CreateScale(0.8f, 0.8f, 0.8f) * Matrix.CreateRotationX(1.5f) * Matrix.CreateTranslation(new Vector3(i * 2 + 0.2f, a * 2 - 0.4f, -0.1f));
tree.Draw(worldMatrix, view, projection);
}
if (Globals.levelArray[mapXtile, mapYtile].typeModifier == 2)
{
Matrix worldMatrix = Matrix.CreateScale(0.8f, 0.8f, 0.8f) * Matrix.CreateRotationX(1.5f) * Matrix.CreateTranslation(new Vector3(i * 2+0.2f, a * 2-0.4f, -0.1f));
tree2.Draw(worldMatrix, view, projection);
}
}
}
mapYtile = mapYtile + 1;
//mapYtile++;
}
mapXtile = mapXtile + 1;
mapYtile = Convert.ToInt32(yPos / 2);
}
}
I think a little background will help before I get into my question. What I'm doing is creating my own small 2D physics library in xna, for fun nonetheless. This is also my first independent xna project, and my first time working with the 3D tools, so I may be doing things all wacky. Anyway, I'm currently making a triangle class in which the constructor takes three arbitrary points in the form of Vector2s. In the constructor I have to put these points in clockwise order (so they're not culled) and then find the texture coordinates they should correspond to (since I'm using VertexPositionTextures as my vertices). What I've got works, but it seems very long and complicated. I'm looking for any ways to shorten/simplify the code, which is this:
public PTriangle(Vector2 a, Vector2 b, Vector2 c)
: base()
{
//set up vertices
VertexPositionTexture[] vertices = new VertexPositionTexture[3];
//center vertices around origin
Vector2 center = new Vector2((a.X + b.X + c.X) / 3, (a.Y + b.Y + c.Y) / 3);
Vector2 newA = a - center;
Vector2 newB = b - center;
Vector2 newC = c - center;
//get angle of each vertex (clockwise from -x axis)
double angleA = MathHelper.Pi - Math.Atan((double)(newA.Y / newA.X));
double angleB = MathHelper.Pi - Math.Atan((double)(newB.Y / newB.X));
double angleC = MathHelper.Pi - Math.Atan((double)(newC.Y / newC.X));
if (newA.X < 0)
{
if (newA.Y < 0)
{
angleA += MathHelper.Pi;
}
else
{
angleA -= MathHelper.Pi;
}
}
if (newB.X < 0)
{
if (newB.Y < 0)
{
angleB += MathHelper.Pi;
}
else
{
angleB -= MathHelper.Pi;
}
}
if (newC.X < 0)
{
if (newC.Y < 0)
{
angleC += MathHelper.Pi;
}
else
{
angleC -= MathHelper.Pi;
}
}
//order vertices by angle
Vector2[] newVertices = new Vector2[3];
if (angleA < angleB && angleA < angleC)
{
newVertices[0] = newA;
if (angleB < angleC)
{
newVertices[1] = newB;
newVertices[2] = newC;
}
else
{
newVertices[1] = newC;
newVertices[2] = newB;
}
}
else if (angleB < angleA && angleB < angleC)
{
newVertices[0] = newB;
if (angleA < angleC)
{
newVertices[1] = newA;
newVertices[2] = newC;
}
else
{
newVertices[1] = newC;
newVertices[2] = newA;
}
}
else
{
newVertices[0] = newC;
if (angleA < angleB)
{
newVertices[1] = newA;
newVertices[2] = newB;
}
else
{
newVertices[1] = newB;
newVertices[2] = newA;
}
}
//set positions of vertices
vertices[0].Position = new Vector3(newVertices[0] + center, 0);
vertices[1].Position = new Vector3(newVertices[1] + center, 0);
vertices[2].Position = new Vector3(newVertices[2] + center, 0);
//get width and height of triangle
float minX = 0;
float minY = 0;
float maxX = 0;
float maxY = 0;
foreach (Vector2 vertex in newVertices)
{
if (vertex.X < minX)
{
minX = vertex.X;
}
else if (vertex.X > maxX)
{
maxX = vertex.X;
}
if (vertex.Y < minY)
{
minY = vertex.Y;
}
else if (vertex.Y > maxY)
{
maxY = vertex.Y;
}
}
float width = maxX - minX;
float height = maxY - minY;
//shift triangle so fits in quadrant IV, and set texture coordinates
for (int index = 0; index < newVertices.Length; ++index)
{
newVertices[index].X -= minX;
newVertices[index].Y -= minY;
vertices[index].TextureCoordinate = new Vector2(
newVertices[index].X / width,
1 - (newVertices[index].Y / height));
}
this.Vertices = vertices;
//set up indices
this.Indices = new short[] { 0, 1, 2 };
}
To put the 3 points in clockwise order, you can use counter-clockwise test (or left-turn test) to check the direction of the 3 points.
Pseudocode from Wikipedia
# Three points are a counter-clockwise turn if ccw > 0, clockwise if
# ccw < 0, and collinear if ccw = 0 because ccw is a determinant that
# gives the signed area of the triangle formed by p1, p2 and p3.
function ccw(p1, p2, p3):
return (p2.x - p1.x)*(p3.y - p1.y) - (p2.y - p1.y)*(p3.x - p1.x)
If the 3 points are counter-clockwise, you can just swap the last 2 points to make the 3 points clockwise order.