I'm trying to create a terrain editor, that works on runtime. At this point I can raise a square but have problems to raise a circle-shape. I saw this post and thoughts thats the solution for my problem, so I transfered it to my code. It really raise a circle somewhere (where I dont click) but its not efficient, because its updating the whole terrain.
The whole function:
public void RaiseTerrain(Terrain terrain, Vector3 location, float effectIncrement)
{
int offset = areaOfEffectSize / 2;
Vector3 tempCoord = (location - terrain.GetPosition());
Vector3 coord;
coord = new Vector3(
(tempCoord.x / GetTerrainSize().x),
(tempCoord.y / GetTerrainSize().y),
(tempCoord.z / GetTerrainSize().z)
);
Vector3 locationInTerrain = new Vector3(coord.x * terrainHeightMapWidth, 0, coord.z * terrainHeightMapHeight);
int terX = (int)locationInTerrain.x - offset;
int terZ = (int)locationInTerrain.z - offset;
int terXInv = (int)locationInTerrain.x + offset;
int terZInv = (int)locationInTerrain.z + offset;
//This raises a square
//float[,] heights = targetTerrainData.GetHeights(terX, terZ, areaOfEffectSize, areaOfEffectSize);
//for (int xx = 0; xx < areaOfEffectSize; xx++)
//{
// for (int yy = 0; yy < areaOfEffectSize; yy++)
// {
// heights[xx, yy] += (effectIncrement * Time.smoothDeltaTime);
// }
//}
//targetTerrainData.SetHeights(terX, terZ, heights);
//This raises a circle
float[,] heights = targetTerrainData.GetHeights(0, 0, terrainHeightMapWidth, terrainHeightMapHeight);
for (int xx = terX; xx < terXInv; xx++)
{
for (int yy = terZ; yy < terZInv; yy++)
{
float currentRadiusSqr = (new Vector2(locationInTerrain.x, locationInTerrain.z) - new Vector2(xx, yy)).sqrMagnitude;
if(currentRadiusSqr < offset*offset)
{
heights[xx, yy] += (effectIncrement * Time.smoothDeltaTime);
}
}
}
targetTerrainData.SetHeights(0, 0, heights);
}
All following lines are just excerpts from above.
I guess my problem is this line:
float[,] heights = targetTerrainData.GetHeights(0, 0, terrainHeightMapWidth, terrainHeightMapHeight);
Because raising a square works fine. I just update a specific area, so its updating the terrain without dropping framerate:
float[,] heights = targetTerrainData.GetHeights(terX, terZ, areaOfEffectSize, areaOfEffectSize);
[...]
targetTerrainData.SetHeights(terX, terZ, heights);
So my question is, how can I raise a circle and update the changes like I did with raising a square? Update only the changed heights?
I tried to use this lines from the square:
float[,] heights = targetTerrainData.GetHeights(terX, terZ, areaOfEffectSize, areaOfEffectSize);
[...]
targetTerrainData.SetHeights(terX, terZ, heights);
in comination with:
for (int xx = terX; xx < terXInv; xx++)
{
for (int yy = terZ; yy < terZInv; yy++)
{
float currentRadiusSqr = (new Vector2(locationInTerrain.x, locationInTerrain.z) - new Vector2(xx, yy)).sqrMagnitude;
if(currentRadiusSqr < offset*offset)
{
heights[xx, yy] += (effectIncrement * Time.smoothDeltaTime);
}
}
}
but it didnt work. Im getting an error:
IndexOutOfRangeException: Index was outside the bounds of the array.
instead of heights[xx, yy] += (effectIncrement * Time.smoothDeltaTime);
try heights[yy, xx] += (effectIncrement * Time.smoothDeltaTime); this seems to work for me
int offset = radious / 2;
int mouseX = (int)((point.x / terrainData.size.x) * heightmapWidth);
int mouseZ = (int)((point.z / terrainData.size.z) * heightmapHeight);
int terX = mouseX - offset;
int terZ = mouseZ - offset;
int terXInv = mouseX + offset;
int terZInv = mouseZ + offset;
float[,] heights = terrainData.GetHeights(0, 0, heightmapWidth, heightmapHeight);
for (int xx = terX; xx < terXInv; xx++)
{
for (int yy = terZ; yy < terZInv; yy++)
{
float currentRadiusSqr = (new Vector2(mouseX, mouseZ) - new Vector2(xx, yy)).sqrMagnitude;
if (currentRadiusSqr < offset * offset)
{
heights[yy, xx] += (strength * Time.smoothDeltaTime);
}
}
}
terrainData.SetHeights(0, 0, heights);
Related
i'm a newbie and i am following a tutorial on procedural landmass generation. However, my plane does not look right. It has a lot of seams/cracks. Is there someone who can point me in the right direction?
Below is my MeshGenerator scripts:
public static class MeshGenerator
{
public static MeshData GenerateTerrainMesh(float[,] heightMap, float heightMultiplier, AnimationCurve heightCurve)
{
int width = heightMap.GetLength(0);
int height = heightMap.GetLength(1);
float topLeftX = (width - 1) / -2f;
float topLeftZ = (height - 1) / 2f;
MeshData meshData = new MeshData(width, height);
int vertexIndex = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
meshData.vertices[vertexIndex] = new Vector3(topLeftX + x, heightCurve.Evaluate(heightMap[x,y]) * heightMultiplier, topLeftZ - y);
meshData.uvs[vertexIndex] = new Vector2(x / (float)width, y / (float)height);
if (x < width - 1 && y < height - 1)
{
meshData.AddTriangle(vertexIndex, vertexIndex + width + 1, vertexIndex + width);
meshData.AddTriangle(vertexIndex, + width + 1, vertexIndex + 1);
}
vertexIndex++;
}
}
return meshData;
}
}
public class MeshData
{
public Vector3[] vertices;
public int[] triangles;
public Vector2[] uvs;
int triangleIndex;
public MeshData(int meshWidth, int meshHeight)
{
vertices = new Vector3[meshWidth * meshHeight];
uvs = new Vector2[meshWidth * meshHeight];
triangles = new int[(meshWidth-1) * (meshHeight-1)*6];
}
public void AddTriangle(int a, int b, int c)
{
triangles[triangleIndex] = a;
triangles[triangleIndex+1] = b;
triangles[triangleIndex+2] = c;
triangleIndex += 3;
}
public Mesh CreateMesh()
{
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
mesh.RecalculateNormals();
return mesh;
}
}
You triangle indices are wrong, this is rather obvious since you have a bunch of triangle-shaped holes. Notably
meshData.AddTriangle(vertexIndex, + width + 1, vertexIndex + 1);
the second vertex of the second triangle is a constant value, and that is most likely incorrect
You should not need to keep a running total of vertexIndex, you should be perfectly able to compute the triangle indices from the grid indices:
var v1 = y * (width+1) + x; // You should have one more column of vertices than you have grid cells
var v2 = v2 + 1; // the vertex one column to the right
var v3 = v1 + width+1; // the vertex one row down
var v4 = v3 + 1;
meshData.AddTriangle(v1, v2, v4);
meshData.AddTriangle(v1, v4, v3);
You may need to invert the vertex order to ensure the normals are oriented correctly.
Well, my question is pretty simple to understand, I'm trying to create a grid of chunks but on each step, I want to double the distance of a chunk.
Then, this is the code I have:
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class DrawLoDTest : MonoBehaviour
{
private List<Vector3> initialChunks;
public int chunkSize = 16;
public int lodLevels = 2;
public int width = 16;
public int height = 16;
private Color[] colorList;
// Start is called before the first frame update
private void Start()
{
}
// Update is called once per frame
private void Update()
{
}
private void Init()
{
if (initialChunks != null && initialChunks.Count > 0)
return;
colorList = new[] { Color.blue, Color.green, Color.yellow, Color.cyan, Color.magenta, Color.grey };
Debug.Log("Init test!");
initialChunks = new List<Vector3>(16 * 16);
for (var i = 0; i < width * height; i++)
{
var x = i % width;
var y = i / width;
initialChunks.Add(new Vector3(x * chunkSize - width * chunkSize / 2, 0, y * chunkSize - height * chunkSize / 2));
}
//Debug.Log(string.Join(Environment.NewLine, initialChunks.Select(v => v.ToString())));
}
private void OnDrawGizmos()
{
Init();
Gizmos.color = Color.red;
foreach (var chunk in initialChunks)
{
Gizmos.DrawWireCube(chunk, Vector3.one * chunkSize);
}
var ww = 0;
var ww2 = 0;
var sum = 0;
var halfChunkSize = chunkSize / 2;
var halfWidth = width * halfChunkSize;
var halfHeight = height * halfChunkSize;
for (var i = 1; i <= lodLevels; i++)
{
if (ww > 0)
++ww2;
var pow = (int)Mathf.Pow(2, i);
var w = width / pow;
var h = height / pow;
Gizmos.color = colorList[i - 1];
var oddSum = ww * chunkSize * pow;
for (var x = -1 - ww2; x <= w + ww2; x++)
{
for (var n = 0; n <= 1; n++)
{
var sign = n == 0 ? -1 : 1;
var chunk = new Vector3(
halfWidth - x * chunkSize * pow - halfChunkSize,
0,
halfHeight * sign - halfChunkSize - chunkSize * 2 * sign + oddSum * sign + sum * sign);
chunk.x -= pow * halfChunkSize;
for (var j = i + 1; j >= i; j--)
{
chunk.z += chunkSize * (int)Mathf.Pow(2, j) / 2 * sign;
}
Gizmos.DrawWireCube(chunk, Vector3.one * chunkSize * pow);
}
}
// TODO
if ((w + ww2 * 2 + 2) / 2 % 2 != 0)
{
Debug.Log($"[{i}, {ww}, {ww2}] W: {w} + {ww2} * 2 = {w + ww2 * 2} + 2 = {w + ww2 * 2 + 2} / 2 = {(w + ww2 * 2 + 2) / 2}");
if (ww == 0)
{
--i;
++ww;
}
else
{
ww = 0;
sum += oddSum;
}
}
else
{
if (ww2 > 0)
ww2 = 0;
}
}
}
}
A little bit of explanation:
First, I load a 16x16 chunk grid (initialChunks).
Then, I start a loop foreach lod level (I'm testing it for 6 levels).
Then, foreach axis I iterate its position.
Then, in a nested loop foreach axis I give a sign (a loop for 0 to 1, that converts into -1 and 1 for the sign).
Then, and this is the more complex thing, if the following row/column has an odd number of elements then I try to match the current row/column by doubling it, in that way non of the following corner will be half of the previous one, like the image below:
I have two main problems:
For some reason, sum and oddSum variables has the wrong input.
At certain levels, (for i >= 5) the logic is broken.
Now I have an octahedron sphere and I woud like to add noise to it, but I can' t do it because I dont know to much of this,
I think I need to modify the vertex pos but i don't know how.
Here is the script that generate the vertices, If you need More, only Ask (;
Vector3[] vertices = new Vector3[(resolution + 1) * (resolution + 1) * 4 - (resolution * 2 - 1) * 3];
int v = 0, vBottom = 0, t = 0;
for (int i = 0; i < 4; i++)
{
vertices[v++] = Vector3.down;
}
for (int i = 1; i <= resolution; i++)
{
float progress = (float)i / resolution;
Vector3 from, to;
vertices[v++] = to = Vector3.Lerp(Vector3.down, Vector3.up, progress);
for (int d = 0; d < 4; d++)
{
from = to;
to = Vector3.Lerp(Vector3.down, directions[d], progress);
t = CreateLowerStrip(i, v, vBottom, t, triangles);
v = CreateVertexLine(from, to, i, v, vertices);
vBottom += i > 1 ? (i - 1) : 1;
}
vBottom = v - 1 - i * 4;
}
Depending on how exactly your noise should look like the simplest I can imagine would be to simply iterate through the vertices and slightly shift them around by random direction vectors with a random magnitude up to a certain maximum like e.g.
void Randomize(Vector3[] vertices, float maxDistance)
{
for(var i = 0; i< vertices.Length; i++)
{
// Generates a random vactor pointing in any possible direction
var randomDirection = new Vector3(Random.Range(-1f, 1f), Random.Range(-1f, 1f), Random.Range(-1f, 1f)).normalized;
// Generate a random magnitude between 0 and given maxDistance
var randomMagnitude = Random.Range(0, maxDistance);
// Move the current vert the randomMagnitude along the randomDirection
vertices[i] += randomDirection * randomMagnitude;
}
}
Or alternatively you could only move on Vectors between the current vert position and the center (assuming Vector3.zero for now)
void RandomizeFromCenter(Vector3[] vertices, float maxDistance)
{
var center = Vector3.zero;
for(var i = 0; i< vertices.Length; i++)
{
var direction = (vertices[i] - center).normalized;
// Generate a random magnitude between -maxDistance and maxDistance
var randomMagnitude = Random.Range(-maxDistance, maxDistance);
// Move the current vert the randomMagnitude along the randomDirection
vertices[i] += direction * randomMagnitude;
}
}
I am trying to implement Hough Line Transform.
Input. I am using the following image as input. This single line is expected to produce only one intersection of sine waves in the output.
Desired behavior. my source code is expected to produce the following output as it was generated by the sample application of AForge framework.
Here, we can see:
the dimension of the output is identical to the input image.
the intersection of sine waves are seen at almost at the center.
the intersection pattern of waves is very small and simple.
Present behavior. My source code is producing the following output which is different than that of the output generated by AForge.
the intersection is not at the center.
the wave patterns are also different.
Why is my code producing a different output?
.
Source Code
I have written the following code myself. The following is a Minimal, Complete, and Verifiable source code.
public class HoughMap
{
public int[,] houghMap { get; private set; }
public int[,] image { get; set; }
public void Compute()
{
if (image != null)
{
// get source image size
int inWidth = image.GetLength(0);
int inHeight = image.GetLength(1);
int inWidthHalf = inWidth / 2;
int inHeightHalf = inHeight / 2;
int outWidth = (int)Math.Sqrt(inWidth * inWidth + inHeight * inHeight);
int outHeight = 180;
int outHeightHalf = outHeight / 2;
houghMap = new int[outWidth, outHeight];
// scanning through each (x,y) pixel of the image--+
for (int y = 0; y < inHeight; y++) //|
{ //|
for (int x = 0; x < inWidth; x++)//<-----------+
{
if (image[x, y] != 0)//if a pixel is black, skip it.
{
// We are drawing some Sine waves. So, it may
// vary from -90 to +90 degrees.
for (int theta = -outHeightHalf; theta < outHeightHalf; theta++)
{
double rad = theta * Math.PI / 180;
// respective radius value is computed
//int radius = (int)Math.Round(Math.Cos(rad) * (x - inWidthHalf) - Math.Sin(rad) * (y - inHeightHalf));
//int radius = (int)Math.Round(Math.Cos(rad) * (x + inWidthHalf) - Math.Sin(rad) * (y + inHeightHalf));
int radius = (int)Math.Round(Math.Cos(rad) * (x) - Math.Sin(rad) * (outHeight - y));
// if the radious value is between 1 and
if ((radius > 0) && (radius <= outWidth))
{
houghMap[radius, theta + outHeightHalf]++;
}
}
}
}
}
}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Bitmap bitmap = (Bitmap)pictureBox1.Image as Bitmap;
int[,] intImage = ToInteger(bitmap);
HoughMap houghMap = new HoughMap();
houghMap.image = intImage;
houghMap.Compute();
int[,] normalized = Rescale(houghMap.houghMap);
Bitmap hough = ToBitmap(normalized, bitmap.PixelFormat);
pictureBox2.Image = hough;
}
public static int[,] Rescale(int[,] image)
{
int[,] imageCopy = (int[,])image.Clone();
int Width = imageCopy.GetLength(0);
int Height = imageCopy.GetLength(1);
int minVal = 0;
int maxVal = 0;
for (int j = 0; j < Height; j++)
{
for (int i = 0; i < Width; i++)
{
double conv = imageCopy[i, j];
minVal = (int)Math.Min(minVal, conv);
maxVal = (int)Math.Max(maxVal, conv);
}
}
int minRange = 0;
int maxRange = 255;
int[,] array2d = new int[Width, Height];
for (int j = 0; j < Height; j++)
{
for (int i = 0; i < Width; i++)
{
array2d[i, j] = (maxRange - minRange) * (imageCopy[i,j] - minVal) / (maxVal - minVal) + minRange;
}
}
return array2d;
}
public int[,] ToInteger(Bitmap input)
{
int Width = input.Width;
int Height = input.Height;
int[,] array2d = new int[Width, Height];
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
Color cl = input.GetPixel(x, y);
int gray = (int)Convert.ChangeType(cl.R * 0.3 + cl.G * 0.59 + cl.B * 0.11, typeof(int));
array2d[x, y] = gray;
}
}
return array2d;
}
public Bitmap ToBitmap(int[,] image, PixelFormat pixelFormat)
{
int[,] imageCopy = (int[,])image.Clone();
int Width = imageCopy.GetLength(0);
int Height = imageCopy.GetLength(1);
Bitmap bitmap = new Bitmap(Width, Height, pixelFormat);
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
int iii = imageCopy[x, y];
Color clr = Color.FromArgb(iii, iii, iii);
bitmap.SetPixel(x, y, clr);
}
}
return bitmap;
}
}
I have solved the problem from this link. The source code from this link is the best one I have ever came across.
public class HoughMap
{
public int[,] houghMap { get; private set; }
public int[,] image { get; set; }
public void Compute()
{
if (image != null)
{
// get source image size
int Width = image.GetLength(0);
int Height = image.GetLength(1);
int centerX = Width / 2;
int centerY = Height / 2;
int maxTheta = 180;
int houghHeight = (int)(Math.Sqrt(2) * Math.Max(Width, Height)) / 2;
int doubleHeight = houghHeight * 2;
int houghHeightHalf = houghHeight / 2;
int houghWidthHalf = maxTheta / 2;
houghMap = new int[doubleHeight, maxTheta];
// scanning through each (x,y) pixel of the image--+
for (int y = 0; y < Height; y++) //|
{ //|
for (int x = 0; x < Width; x++)//<-------------+
{
if (image[x, y] != 0)//if a pixel is black, skip it.
{
// We are drawing some Sine waves.
// It may vary from -90 to +90 degrees.
for (int theta = 0; theta < maxTheta; theta++)
{
double rad = theta *Math.PI / 180;
// respective radius value is computed
int rho = (int)(((x - centerX) * Math.Cos(rad)) + ((y - centerY) * Math.Sin(rad)));
// get rid of negative value
rho += houghHeight;
// if the radious value is between
// 1 and twice the houghHeight
if ((rho > 0) && (rho <= doubleHeight))
{
houghMap[rho, theta]++;
}
}
}
}
}
}
}
}
Just look at this C++ code, and this C# code. So, complicated and messy that my brain got arrested. Especially, the C++ one. I never anticipated someone to store 2D values in a 1D array.
Currently, I have the player select 2 positions in the map, it's a 3d world but my problem is only relevant in the first 2 dimensions. I want to create a floor between the 2 selected points, and rotate them to the angle between those 2 points. http://i.imgur.com/hCjtEzB.png (excuse my paint skills)
My code works properly in some cases, but in other cases the crates are spawned at the wrong angle. I think it has something to do with vec1.x > vec2.x, or something similar. But I can't figure it out.
What am I missing?
const int modelLength = 55;
const int modelWidth = 30;
void createFloor(Vector vec1, Vector vec2)
{
float height = vec1.Z;
float length = Math.Abs(vec1.X - vec2.X);
float width = Math.Abs(vec1.Y - vec2.Y);
int crateWidth = (int)Math.Ceiling(width / modelWidth);
int crateLength = (int)Math.Ceiling(length / modelLength);
int adjustedWidth = crateWidth * modelWidth;
int adjustedLength = crateLength * modelLength;
double angleRad = Math.Atan2(adjustedWidth, adjustedLength);
double angleDeg = Util.RadianToDegree(angleRad);
Vector angles = new Vector(0, (float)angleDeg, 0);
float mX = (vec1.X < vec2.X) ? adjustedLength / 2 + vec1.X : vec1.X - (adjustedLength / 2);
float mY = (vec1.Y < vec2.Y) ? adjustedWidth / 2 + vec1.Y : vec1.Y - (adjustedWidth / 2);
Vector middle = new Vector(mX, mY, vec1.Z);
for (int i = 0; i < crateLength; i++)
{
for (int j = 0; j < crateWidth; j++)
{
float x = (vec1.X < vec2.X) ? vec1.X + i * modelLength : vec1.X - i * modelLength;
float y = (vec1.Y < vec2.Y) ? vec1.Y + j * modelWidth : vec1.Y - j * modelWidth;
Vector v = new Vector(x, y, height);
v = Util.RotateAround(v, middle, angleDeg);
spawnCrate(v, angles);
}
}
}
public static Vector RotateAround(Vector vectorToRotate, Vector center, double angleDeg)
{
double angleRad = DegreeToRadian(angleDeg);
double cosTheta = Math.Cos(angleRad);
double sinTheta = Math.Sin(angleRad);
return new Vector
{
X = (int)(cosTheta * (vectorToRotate.X - center.X) - sinTheta * (vectorToRotate.Y - center.Y) + center.X),
Y = (int)(sinTheta * (vectorToRotate.X - center.X) + cosTheta * (vectorToRotate.Y - center.Y) + center.Y),
Z = vectorToRotate.Z
};
}
Thanks.