So I am trying to create my own version of Sebastian Lauge's marching cubes coding adventures (https://www.youtube.com/watch?v=M3iI2l0ltbE) and I have a script that generates perlin noise into terrain on the Y-axis ant Im trying to make it so it creates perlin noise for the x, y, and z axis and compile vertices from that perlin noise into a mesh. Rightnow I'm in the process of adding the code to decides which points should be put into the mesh The problem is that I have to change the variable y in lines 49 - 56 to the variable ypn (declared on line 45) and leave the y variables out of that area alone the problem is that if i change one of those y variables (don't know which one) it will make the mesh flat and that's not what I want then it gets worse. If you try to revert the variable you changed back to y it will not change a thing. It will always make the mesh flat unless you make a new script. My code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MapGen : MonoBehaviour
{
Mesh mesh;
Material material;
Vector3[] vertices;
int[] triangles;
Vector2[] uvs;
Vector3[] interest;
public int xSize = 20;
public int zSize = 20;
public int ySize = 20;
[Range(-1, 1)]
public float Surface = 0;
public float Weight = 1;
float minHeight = 0;
float maxHeight = 0;
// Start is called before the first frame update
void Start()
{
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
CreateShape();
UpdateMesh();
}
void CreateShape()
{
vertices = new Vector3[(xSize + 1) * (zSize + 1)];
Texture2D texture = new Texture2D(xSize, zSize, TextureFormat.ARGB32, false);
for (int y = 0; y <= ySize; y++)
{
for (int i = 0, z = 0; z <= zSize; z++)
{
for (int x = 0; x <= xSize; x++)
{
float ypn = (Mathf.PerlinNoise(x * .3f, z * .3f) * 2f) * Weight;
vertices[i] = new Vector3(x, ypn, z);
if (y <= minHeight)
minHeight = y;
else
{
if (y >= maxHeight)
maxHeight = y;
}
var result = Mathf.Lerp(minHeight, maxHeight, Mathf.InverseLerp(0, 1, y));
texture.SetPixel(x, z, new Color(y, x, 1));
i++;
}
}
}
texture.Apply();
GetComponent<Renderer>().material.mainTexture = texture;
triangles = new int[xSize * zSize * 6];
int vert = 0;
int tris = 0;
for (int z = 0; z < zSize; z++)
{
for (int x = 0; x < xSize; x++)
{
triangles[tris + 0] = vert + 0;
triangles[tris + 1] = vert + xSize + 1;
triangles[tris + 2] = vert + 1;
triangles[tris + 3] = vert + 1;
triangles[tris + 4] = vert + xSize + 1;
triangles[tris + 5] = vert + xSize + 2;
vert++;
tris += 6;
}
vert++;
}
uvs = new Vector2[vertices.Length];
for (int i = 0, z = 0; z <= zSize; z++)
{
for (int x = 0; x <= xSize; x++)
{
uvs[i] = new Vector2((float)x / xSize, (float)z / zSize);
i++;
}
}
}
void UpdateMesh()
{
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
mesh.RecalculateNormals();
MeshCollider meshc = gameObject.AddComponent(typeof(MeshCollider)) as MeshCollider;
meshc.sharedMesh = mesh;
}
}
Note: using unity 2019.2.15
Script is attached to empty Game object with a mesh renderer and a mesh filter on default unity values
Related
Codes below:
using System;
using UnityEngine;
using UnityEngine.Serialization;
public class Chessboard : MonoBehaviour
{
[Header("Art")]
[SerializeField] private Material tileMaterial;
//Logic
private const int TILE_COUNT_X = 8;
private const int TILE_COUNT_Y = 8;
private GameObject[,] tiles;
private Camera currentCamera;
private Vector2Int currentHover = -Vector2Int.one;
private void Awake()
{
GenerateAllTiles(1, TILE_COUNT_X, TILE_COUNT_Y);
}
private void Update()
{
if (!currentCamera)
{
currentCamera = Camera.current;
return;
}
RaycastHit info;
Ray ray = currentCamera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out info, 100, LayerMask.GetMask("Tile")))
{
// get the indexes of Tile i've hit
Vector2Int hitPosition = LookupTileIndex(info.transform.gameObject);
//If we are hovering a tile after not hovering any
if (currentHover == -Vector2Int.one)
{
tiles[hitPosition.x, hitPosition.y].layer = LayerMask.NameToLayer("Hover");
currentHover = hitPosition;
}
//If we were already hovering a tile, change the previous one
else if (currentHover != hitPosition)
{
tiles[currentHover.x, currentHover.y].layer = LayerMask.NameToLayer("Tile");
currentHover = hitPosition;
tiles[hitPosition.x, hitPosition.y].layer = LayerMask.NameToLayer("Hover");
}
}
else
{
if (currentHover != -Vector2Int.one)
{
tiles[currentHover.x, currentHover.y].layer = LayerMask.NameToLayer("Tile");
currentHover = -Vector2Int.one;
}
}
}
//Generate the board (useful)
private void GenerateAllTiles(float tileSize, int tileCountX, int tileCountY)
{
tiles = new GameObject[tileCountX, tileCountY];
for (int x = 0; x < tileCountX; x++)
for (int y = 0; y < tileCountY; y++)
tiles[x, y] = generate1tile(tileSize, x, y);
}
private GameObject generate1tile(float tileSize, int x, int y)
{
GameObject tileObject = new GameObject(string.Format("Tile X:{0}, Y:{1}", x, y));
tileObject.transform.parent = transform;
Mesh mesh = new Mesh();
tileObject.AddComponent<MeshFilter>().mesh = mesh;
tileObject.AddComponent<MeshRenderer>().material = tileMaterial;
Vector3[] vertices = new Vector3[4];
vertices[0] = new Vector3(x * tileSize, 0, y * tileSize);
vertices[1] = new Vector3(x * tileSize, 0, (y + 1) * tileSize);
vertices[2] = new Vector3((x + 1) * tileSize, 0, y * tileSize);
vertices[3] = new Vector3((x + 1) * tileSize, 0, (y + 1) * tileSize);
int[] tris = new int[]{0, 2, 1, 1, 2, 3};
mesh.vertices = vertices;
mesh.triangles = tris;
mesh.RecalculateNormals();
tileObject.layer = LayerMask.NameToLayer("Tile");
tileObject.AddComponent<BoxCollider>();
return tileObject;
}
private Vector2Int LookupTileIndex(GameObject hitInfo)
{
for(int x = 0; x < TILE_COUNT_X; x++)
for(int y = 0; y < TILE_COUNT_Y; y++)
if(tiles[x, y] == hitInfo)
return new Vector2Int(x, y);
return new Vector2Int(-1, -1); //Invalid
}
}
Error Message:
A game object can only be in one layer. The layer needs to be in the range [0...31]
UnityEngine.StackTraceUtility:ExtractStackTrace ()
Chessboard:generate1tile (single,int,int) (at Assets/scripts/Chessboard.cs:93)
Chessboard:GenerateAllTiles (single,int,int) (at Assets/scripts/Chessboard.cs:68)
Chessboard:Awake () (at Assets/scripts/Chessboard.cs:20)
I just get into Unity, so I might be missed some parts of it.
I tired using AI to config my problem, but it doesn't work. I am expecting i put my mouse on a tile, the tile change its color.
Here is the link to the tutorial I watched: https://www.youtube.com/watch?v=FtGy7J8XD90&list=PLmcbjnHce7SeAUFouc3X9zqXxiPbCz8Zp&index=2&ab_channel=Epitome
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've made a little mesh generator in Unity by following brackeys tutorial series, but I think one was deleted or taken down because there are pieces missing. I'm trying to learn how to use octaves with perlin noise to create more interesting terrain and I cant figure it out. Here's my code if you can help me:
[RequireComponent(typeof(MeshFilter))]
public class MeshGenerator : MonoBehaviour
{
Mesh mesh;
Vector3[] vertices;
int[] triangles;
Color[] colors;
public Gradient gradient;
float minTerrainHeight;
float maxTerrainHeight;
public int xSize = 20;
public int zSize = 20;
void Start()
{
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
CreateShape();
UpdateMesh();
}
void CreateShape()
{
vertices = new Vector3[(xSize + 1) * (zSize + 1)];
colors = new Color[vertices.Length];
for (int i = 0, z = 0; z <= zSize; z++)
{
for (int x = 0; x <= xSize; x++)
{
float y = Mathf.PerlinNoise(x * 0.3f, z * 0.3f) * 3f;
if (y > maxTerrainHeight)
{
maxTerrainHeight = y;
}
if (y < minTerrainHeight)
{
minTerrainHeight = y;
}
float height = Mathf.InverseLerp(minTerrainHeight, maxTerrainHeight, vertices[i].y);
colors[i] = gradient.Evaluate(height);
vertices[i] = new Vector3(x, y, z);
i++;
}
}
triangles = new int[xSize * zSize * 6];
int vert = 0;
int tris = 0;
for (int z = 0; z < zSize; z++)
{
for (int x = 0; x < xSize; x++)
{
triangles[tris + 0] = vert + 0;
triangles[tris + 1] = vert + xSize + 1;
triangles[tris + 2] = vert + 1;
triangles[tris + 3] = vert + 1;
triangles[tris + 4] = vert + xSize + 1;
triangles[tris + 5] = vert + xSize + 2;
vert++;
tris += 6;
}
vert++;
}
}
void UpdateMesh()
{
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.colors = colors;
mesh.RecalculateNormals();
}
}
I have problem with normalized values when creating procedural grid in Unity. I have been following great tutorial from catlikecoding and I dumped in to weird behaving when I tried to use normalized values for my vertices. In some cases of xSize and ySize grid combinations all works, but in other combinations mesh get deformed. Let me give you couple of examples
xSize = 35; ySize = 25; // OK
xSize = 350; ySize = 250; // NOT OK
xSize = 150; ySize = 250; // OK
xSize = 350; ySize = 200; // NOT OK
xSize = 1000; ySize = 750; // NOT OK
First 2 cases I illustrated with sphere representing each 10th vertices.
35x25 case
350x250 case
I am using Unity3d 2018.3
private void Generate()
{
GetComponent<MeshFilter>().mesh = mesh = new Mesh();
mesh.name = "Procedural Grid";
vertices = new Vector3[(xSize + 1) * (ySize + 1)];
Vector2[] uv = new Vector2[vertices.Length];
float multX = 1 / (float)xSize;
float multY = 1 / (float)ySize;
for (int i = 0, y = 0; y <= ySize; y++)
{
for (int x = 0; x <= xSize; x++, i++)
{
//vertices[i] = new Vector3(x, y);
var xNormalized = x * multX;
var yNormalized = y * multY;
vertices[i] = new Vector3(xNormalized, yNormalized);
uv[i] = new Vector2(xNormalized, yNormalized);
}
}
mesh.vertices = vertices;
mesh.uv = uv;
var triangles = new int[xSize * ySize * 6];
for (int ti = 0, vi = 0, y = 0; y < ySize; y++, vi++)
{
for (int x = 0; x < xSize; x++, ti += 6, vi++)
{
triangles[ti] = vi;
triangles[ti + 3] = triangles[ti + 2] = vi + 1;
triangles[ti + 4] = triangles[ti + 1] = vi + xSize + 1;
triangles[ti + 5] = vi + xSize + 2;
}
}
mesh.triangles = triangles;
mesh.RecalculateNormals();
}
I expect the mesh be 1x1 in every case, no matter which xSize or ySize of the grid I use. Anybody can advise how to achieve that?
So my friend explained me, that by default meshes have a 65535 vertices limit in Unity. And I have to nicely ask if I want more.
I had to add
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
after
mesh.name = "Procedural Grid";
here is more..
Suddenly all works as expected. Thank you all for support.
I am creating a game after working through a XNA 4.0 book. It will be 3D, but I am already stuck in creating the terrain...
UPDATE: Everything starting from here is an update...
Terrain Update:
public void Update(Matrix view, Matrix projection)
{
View = view;
Projection = projection;
World = Matrix.CreateTranslation(-Width / 2f, 0, Height / 2f);
}
Terrain Draw:
public void Draw(GraphicsDevice g)
{
effect.CurrentTechnique = effect.Techniques["ColoredNoShading"];
effect.Parameters["xView"].SetValue(View);
effect.Parameters["xProjection"].SetValue(Projection);
effect.Parameters["xWorld"].SetValue(World);
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
//g.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, indices.Length / 3, VertexPositionColorNormal.VertexDeclaration);
g.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Length, 0, indices.Length / 3);
}
}
The commented line is working, in the both cases I am able to see the terrain...
The following code is to initialize Vertex and Index Buffer:
private void SetUpVertices(GraphicsDevice g)
{
float currentH;
int currentI;
vertices = new VertexPositionColorNormal[Width * Height];
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
currentH = heightData[x,y];
currentI = x + y * Width;
vertices[currentI].Position = new Vector3(x, currentH , -y);
if (currentH < minH + (maxH - minH) / 3)
vertices[currentI].Color = Color.ForestGreen;
else if (currentH < maxH - (maxH - minH) / 3)
vertices[currentI].Color = Color.LawnGreen;
else
vertices[currentI].Color = Color.White;
}
}
SetUpIndices(g);
}
private void SetUpIndices(GraphicsDevice g)
{
indices = new int[(Width - 1) * (Height - 1) * 6];
int counter = 0;
for (int y = 0; y < Height - 1; y++)
{
for (int x = 0; x < Width - 1; x++)
{
int lowerLeft = x + y * Width;
int lowerRight = (x + 1) + y * Width;
int topLeft = x + (y + 1) * Width;
int topRight = (x + 1) + (y + 1) * Width;
indices[counter++] = topLeft;
indices[counter++] = lowerRight;
indices[counter++] = lowerLeft;
indices[counter++] = topLeft;
indices[counter++] = topRight;
indices[counter++] = lowerRight;
}
}
SetUpNormals(g);
}
private void SetUpNormals(GraphicsDevice g)
{
for (int i = 0; i < vertices.Length; i++)
{
vertices[i].Normal = Vector3.Zero;
}
int[] index = new int[3];
Vector3 s1, s2, n;
for (int i = 0; i < vertices.Length / 3; i++)
{
for (int y = 0; y < 3; y++)
index[y] = indices[i * 3 + y];
s1 = vertices[index[0]].Position - vertices[index[2]].Position;
s2 = vertices[index[0]].Position - vertices[index[1]].Position;
n = Vector3.Cross(s1, s2);
for (int y = 0; y < 3; y++)
{
vertices[index[y]].Normal += n;
vertices[index[y]].Normal.Normalize();
}
}
FillBuffers(g);
}
private void FillBuffers(GraphicsDevice g)
{
VertexBuffer = new VertexBuffer(g, VertexPositionColorNormal.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
VertexBuffer.SetData(vertices);
IndexBuffer = new IndexBuffer(g, typeof(int), indices.Length, BufferUsage.WriteOnly);
IndexBuffer.SetData(indices);
g.Indices = IndexBuffer;
g.SetVertexBuffer(VertexBuffer);
}
I don't think, that there is a mistake, because it is working with the other line. Might there be an error with the .fx file I am using. If you think so, I am going to switch to BasicEffects...
(You might notice, that the code is from http://www.riemers.net/eng/Tutorials/XNA/Csharp/series1.php )
Thanks for your help...
Yours,
Florian
(Answer to original revision of the question.)
You're not setting your vertex buffer and index buffer onto the graphics device. These two lines of code (untested) should do what you need:
g.GraphicsDevice.Indices = indexBuffer;
g.GraphicsDevice.SetVertexBuffer(vertexBuffer);
Place them just after you set the parameters on your effect (ef), before the loop.
The vertex buffer provides the vertex declaration that the exception message is asking for.
Edit after question update: In your new version you're setting the vertex and index buffers - but it's in the wrong place. You need to set them onto the graphics device each frame. Your code would only work if nothing changes them after you set them in FillBuffers. But I'm guessing that stuff is being drawn outside your class's Draw method?
If that something else is a SpriteBatch, even it works using vertex buffers and index buffers. So it will reset your settings. (It's worth adding that it also sets render states - in which case you might need to see this article.)