- Home /
Procedural Mesh random faces generated
Hello, my issue is my mesh is created, but some of the faces on the top layer are sometimes missing and there are always extra faces all the way down the chunk, there shouldnt be as the verts should have been deleted but i just cant put my finger on it.
This issue only started when i added threading.
heres images of what i mean to make it clear:
Heres the code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using SimplexNoise;
using System.Threading;
[RequireComponent (typeof(MeshRenderer))]
[RequireComponent (typeof(MeshCollider))]
[RequireComponent (typeof(MeshFilter))]
public class Chunk : MonoBehaviour {
public const float TEXTURE_TILE_SIZE = 32f;
public const float ATLAS_SIZE = 128f;
public const float TEXTURE_PADDING_SIZE = 0f;
public static List<Chunk> chunksWaiting = new List<Chunk>();
public static List<Chunk> chunks = new List<Chunk>();
public static int width {
get { return World.currentWorld.chunkWidth; }
}
public static int height {
get { return World.currentWorld.chunkHeight; }
}
public static float brickHeight {
get { return World.currentWorld.brickHeight; }
}
List<Vector3> verts = new List<Vector3>();
List<Vector2> uvs = new List<Vector2>();
List<int> tris = new List<int>();
public Thread mythread;
public byte[,,] map;
public Mesh visualMesh;
protected MeshRenderer meshRenderer;
protected MeshCollider meshCollider;
protected MeshFilter meshFilter;
protected bool initialized = false;
protected bool isPopulated = false;
Vector3 myTransform;
bool hasEnded = false;
// Use this for initialization
void Start () {
chunks.Add(this);
meshRenderer = GetComponent<MeshRenderer>();
meshCollider = GetComponent<MeshCollider>();
meshFilter = GetComponent<MeshFilter>();
chunksWaiting.Add(this);
myTransform = transform.position;
if (chunksWaiting[0] == this)
{
mythread = new Thread(new ThreadStart(CalculateMapFromScratch));
mythread.Start();
}
}
void Update(){
if (initialized && !isPopulated) {
PopulateMesh ();
isPopulated = true;
}
}
public static byte GetTheoreticalByte(Vector3 pos) {
System.Random rand = new System.Random (World.currentWorld.seed);
Vector3 grain0Offset = new Vector3((float)rand.NextDouble() * 10000, (float)rand.NextDouble() * 10000, (float)rand.NextDouble() * 10000);
Vector3 grain1Offset = new Vector3((float)rand.NextDouble() * 10000, (float)rand.NextDouble() * 10000, (float)rand.NextDouble() * 10000);
Vector3 grain2Offset = new Vector3((float)rand.NextDouble() * 10000, (float)rand.NextDouble() * 10000, (float)rand.NextDouble() * 10000);
return GetTheoreticalByte(pos, grain0Offset, grain1Offset, grain2Offset);
}
public static byte GetTheoreticalByte(Vector3 pos, Vector3 offset0, Vector3 offset1, Vector3 offset2)
{
float clusterValue = CalculateNoiseValue(pos, offset2, 0.02f);
int biomeIndex = Mathf.FloorToInt(clusterValue * World.currentWorld.biomes.Length);
Biome biome = World.currentWorld.biomes[biomeIndex];
float heightBase = biome.minHeight;
float maxHeight = biome.maxHeight;
float heightSwing = maxHeight - heightBase;
float blobValue = CalculateNoiseValue(pos, offset1, 0.05f);
float mountainValue = CalculateNoiseValue(pos, offset0, 0.009f);
mountainValue += biome.mountainPowerBonus;
if (mountainValue < 0) mountainValue = 0;
mountainValue = Mathf.Pow(mountainValue, biome.mountainPower); //Mathf.Sqrt(mountainValue);
byte brick = biome.GetBrick(Mathf.FloorToInt(pos.y), mountainValue, blobValue);
mountainValue *= heightSwing;
mountainValue += heightBase;
mountainValue += (blobValue * 10) - 5f;
if (mountainValue >= pos.y)
return brick;
return 0;
}
public virtual void CalculateMapFromScratch() {
map = new byte[width, height, width];
System.Random rand = new System.Random (World.currentWorld.seed);
Vector3 grain0Offset = new Vector3((float)rand.NextDouble() * 10000, (float)rand.NextDouble() * 10000, (float)rand.NextDouble() * 10000);
Vector3 grain1Offset = new Vector3((float)rand.NextDouble() * 10000, (float)rand.NextDouble() * 10000, (float)rand.NextDouble() * 10000);
Vector3 grain2Offset = new Vector3((float)rand.NextDouble() * 10000, (float)rand.NextDouble() * 10000, (float)rand.NextDouble() * 10000);
for (int x = 0; x < World.currentWorld.chunkWidth; x++)
{
for (int y = 0; y < height; y++)
{
for (int z = 0; z < width; z++)
{
map[x, y, z] = GetTheoreticalByte(new Vector3(x, y, z) + myTransform, grain0Offset, grain1Offset, grain2Offset);
}
}
}
CreateVisualMesh();
initialized = true;
chunksWaiting.Remove(this);
if (!hasEnded) {
if (chunksWaiting [0] != null)
chunksWaiting [0].CalculateMapFromScratch ();
}
}
public static float CalculateNoiseValue(Vector3 pos, Vector3 offset, float scale)
{
float noiseX = Mathf.Abs((pos.x + offset.x) * scale);
float noiseY = Mathf.Abs((pos.y + offset.y) * scale);
float noiseZ = Mathf.Abs((pos.z + offset.z) * scale);
return Mathf.Max(0, Noise.Generate(noiseX, noiseY, noiseZ));
}
public virtual void CreateVisualMesh() {
verts.Clear ();
uvs.Clear ();
tris.Clear();
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
for (int z = 0; z < width; z++)
{
if (map[x,y,z] == 0) continue;
byte brick = map[x,y,z];
// Left wall
if (IsTransparent(x - 1, y, z))
BuildFace (brick, new Vector3(x, y, z), Vector3.up, Vector3.forward, false, verts, uvs, tris, BlockSide.LEFT);
// Right wall
if (IsTransparent(x + 1, y , z))
BuildFace (brick, new Vector3(x + 1, y, z), Vector3.up, Vector3.forward, true, verts, uvs, tris, BlockSide.RIGHT);
// Bottom wall
if (IsTransparent(x, y - 1 , z))
BuildFace (brick, new Vector3(x, y, z), Vector3.forward, Vector3.right, false, verts, uvs, tris, BlockSide.BOTTOM);
// Top wall
if (IsTransparent(x, y + 1, z))
BuildFace (brick, new Vector3(x, y + 1, z), Vector3.forward, Vector3.right, true, verts, uvs, tris, BlockSide.TOP);
// Back
if (IsTransparent(x, y, z - 1))
BuildFace (brick, new Vector3(x, y, z), Vector3.up, Vector3.right, true, verts, uvs, tris, BlockSide.BACK);
// Front
if (IsTransparent(x, y, z + 1))
BuildFace (brick, new Vector3(x, y, z + 1), Vector3.up, Vector3.right, false, verts, uvs, tris, BlockSide.FRONT);
}
}
}
}
public void PopulateMesh(){
visualMesh = new Mesh();
visualMesh.vertices = verts.ToArray();
visualMesh.uv = uvs.ToArray();
visualMesh.triangles = tris.ToArray();
visualMesh.RecalculateBounds();
visualMesh.RecalculateNormals();
meshFilter.mesh = visualMesh;
meshCollider.sharedMesh = null;
meshCollider.sharedMesh = visualMesh;
}
public virtual void BuildFace(byte brick, Vector3 corner, Vector3 up, Vector3 right, bool reversed, List<Vector3> verts, List<Vector2> uvs, List<int> tris, BlockSide brickSide)
{
int index = verts.Count;
verts.Add (corner);
verts.Add (corner + up);
verts.Add (corner + up + right);
verts.Add (corner + right);
Vector2 tileSize = CreateUVTexture((BlockType)brick, brickSide);
float tileSizeOriginal = TEXTURE_TILE_SIZE / ATLAS_SIZE;
float padding = TEXTURE_PADDING_SIZE / ATLAS_SIZE; //stops bleeding from mip map
uvs.Add(new Vector2 (tileSize.x + padding, tileSize.y + padding));
uvs.Add(new Vector2 (tileSize.x + padding, tileSize.y + tileSizeOriginal + padding));
uvs.Add(new Vector2 (tileSize.x + tileSizeOriginal - padding, tileSize.y + tileSizeOriginal - padding));
uvs.Add(new Vector2 (tileSize.x + tileSizeOriginal - padding, tileSize.y));
if (reversed)
{
tris.Add(index + 0);
tris.Add(index + 1);
tris.Add(index + 2);
tris.Add(index + 2);
tris.Add(index + 3);
tris.Add(index + 0);
}
else
{
tris.Add(index + 1);
tris.Add(index + 0);
tris.Add(index + 2);
tris.Add(index + 3);
tris.Add(index + 2);
tris.Add(index + 0);
}
}
public virtual bool IsTransparent (int x, int y, int z)
{
if ( y < 0) return false;
byte brick = GetByte(x,y,z);
switch (brick)
{
case 0:
return true;
default:
return false;
}
}
public virtual byte GetByte (int x, int y , int z)
{
if ((y < 0) || (y >= height))
return 0;
Vector3 worldPos = new Vector3(x, y, z) + myTransform;
if (! initialized)
return GetTheoreticalByte(worldPos);
if ( (x < 0) || (z < 0) || (x >= width) || (z >= width))
{
Chunk chunk = Chunk.FindChunk(worldPos);
if (chunk == this) return 0;
if (chunk == null)
{
return GetTheoreticalByte(worldPos);
}
return chunk.GetByte (worldPos);
}
return map[x,y,z];
}
public virtual byte GetByte(Vector3 worldPos) {
worldPos -= transform.position;
int x = Mathf.FloorToInt(worldPos.x);
int y = Mathf.FloorToInt(worldPos.y);
int z = Mathf.FloorToInt(worldPos.z);
return GetByte (x, y, z);
}
public static Chunk FindChunk(Vector3 pos) {
for (int a = 0; a < chunks.Count; a++)
{
Vector3 cpos = chunks[a].transform.position;
if ( ( pos.x < cpos.x) || (pos.z < cpos.z) || (pos.x >= cpos.x + width) || (pos.z >= cpos.z + width) ) continue;
return chunks[a];
}
return null;
}
public bool SetBrick (byte brick, Vector3 worldPos)
{
worldPos -= transform.position;
return SetBrick(brick, Mathf.FloorToInt(worldPos.x),Mathf.FloorToInt(worldPos.y),Mathf.FloorToInt(worldPos.z));
}
public bool SetBrick (byte brick, int x, int y, int z)
{
if ( ( x < 0) || (y < 0) || (z < 0) || (x >= width) || (y >= height) || (z >= width) )
{
return false;
}
if (map[x,y,z] == brick) return false;
map[x,y,z] = brick;
CreateVisualMesh();
if (x == 0)
{
Chunk chunk = FindChunk( new Vector3(x - 2, y, z) + transform.position);
if (chunk != null)
chunk.CreateVisualMesh();
}
if (x == width - 1)
{
Chunk chunk = FindChunk( new Vector3(x + 2, y, z) + transform.position);
if (chunk != null)
chunk.CreateVisualMesh();
}
if (z == 0)
{
Chunk chunk = FindChunk( new Vector3(x, y, z - 2) + transform.position);
if (chunk != null)
chunk.CreateVisualMesh();
}
if (z == width - 1)
{
Chunk chunk = FindChunk( new Vector3(x, y, z + 2) + transform.position);
if (chunk != null)
chunk.CreateVisualMesh();
}
return true;
}
public Vector2 CreateUVTexture(BlockType blocktype, BlockSide brickSide){
Vector2 tileSize = new Vector2(TEXTURE_TILE_SIZE / ATLAS_SIZE, TEXTURE_TILE_SIZE / ATLAS_SIZE) ;
Vector2 tileSizeTemp = GetCoord(blocktype, brickSide);
tileSize.x *= tileSizeTemp.x;
tileSize.y *= tileSizeTemp.y;
return tileSize;
}
Vector2 GetCoord(BlockType brickType, BlockSide brickSide){
Vector2 tileCoords = new Vector2 ();
switch (brickType) {
case BlockType.STONE:
tileCoords.x = 0;
tileCoords.y = 0;
break;
case BlockType.DIRT:
tileCoords.x = 1;
tileCoords.y = 0;
break;
case BlockType.DIRT_GRASS:
if(brickSide == BlockSide.TOP){
tileCoords.x = 2;
tileCoords.y = 0;
}else if(brickSide == BlockSide.BOTTOM){
tileCoords.x = 1;
tileCoords.y = 0;
}else{
tileCoords.x = 3;
tileCoords.y = 0;
}
break;
case BlockType.REDSTONE:
tileCoords.x = 0;
tileCoords.y = 2;
break;
case BlockType.SILVER:
tileCoords.x = 1;
tileCoords.y = 2;
break;
case BlockType.IRON:
tileCoords.x = 2;
tileCoords.y = 2;
break;
case BlockType.COAL:
tileCoords.x = 0;
tileCoords.y = 3;
break;
case BlockType.LAPIS:
tileCoords.x = 1;
tileCoords.y = 3;
break;
case BlockType.EMERALD:
tileCoords.x = 2;
tileCoords.y = 3;
break;
case BlockType.GOLD:
tileCoords.x = 3;
tileCoords.y = 3;
break;
case BlockType.BEDROCK:
tileCoords.x = 0;
tileCoords.y = 1;
break;
default:
tileCoords.x = 0;
tileCoords.y = 1;
break;
}
return tileCoords;
}
void OnDestroy() {
hasEnded = true;
}
}
Still haven't found a solution cant understand why it would do this after adding threading
Answer by Slymatt · May 04, 2015 at 01:18 PM
Fixed it was a stupid mistake the code was looking at byte 0 which should have been enum NONE turns out i forgot to add NONE to my enum
Well i tested the old code and it seems that i may have created this bug just before the threading was implemented so its not a threading issue, still an issue though
Your answer
Follow this Question
Related Questions
Manual culling of voxel faces? 0 Answers
Merging faces without effecting texture mapping 2 Answers
Does CombineMesh always remove unused vertecies or not? 0 Answers
Is there any way to tile a texture within a single triangle of a procedural mesh? 1 Answer
Dynamically move vertices of a mesh 2 Answers