I’m trying to build some voxel terrain however I’m having some issues with some of the faces of the mesh not loading properly. Here’s an image below :
I think it has to do with either how I’m getting the blocks to draw or how I’m drawing them. So that would be either the createVisualMesh function in the Mesh Manager or the isTransparent function in the mesh manager. Below is all the code I think is relevant, if you need more just ask.
Mesh Manager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MeshManager : MonoBehaviour
{
public static ChunkManager chunkManager;
public static MeshObject CreateVisualMesh(int chunkSize, BlockType[,,] blocks, MeshObject meshOb, Vector3 chunkPos, Chunk chunk)
{
MeshManager.chunkManager = World.currentWorld.chunkManager;
Mesh visualMesh = meshOb.Mesh;
MeshFilter meshFilter = meshOb.MeshFilter;
MeshCollider meshCollider = meshOb.MeshCollider;
List<Vector3> verts = new List<Vector3>();
List<Vector2> uvs = new List<Vector2>();
List<int> tris = new List<int>();
int[,,] map = Chunk.GetIntFromBlockType(blocks, chunkSize);
Vector3 worldPos = new Vector3(chunkPos.x * chunkSize, chunkPos.y * chunkSize, chunkPos.z * chunkSize);
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; y++)
{
for (int z = 0; z < chunkSize; z++)
{
if (map[x, y, z] == 0) continue;
int block = map[x, y, z];
// Left wall
if (IsTransparent(x - 1, y, z, worldPos, chunk))
{
BuildFace(block, new Vector3(x, y, z), Vector3.up, Vector3.forward, false, verts, uvs, tris);
}
// Right wall
if (IsTransparent(x + 1, y, z, worldPos, chunk))
{
BuildFace(block, new Vector3(x + 1, y, z), Vector3.up, Vector3.forward, true, verts, uvs, tris);
}
// Bottom wall
if (IsTransparent(x, y - 1, z, worldPos, chunk))
{
BuildFace(block, new Vector3(x, y, z), Vector3.forward, Vector3.right, false, verts, uvs, tris);
}
// Top wall
if (IsTransparent(x, y + 1, z, worldPos, chunk))
{
BuildFace(block, new Vector3(x, y + 1, z), Vector3.forward, Vector3.right, true, verts, uvs, tris);
}
// Back
if (IsTransparent(x, y, z - 1, worldPos, chunk))
{
BuildFace(block, new Vector3(x, y, z), Vector3.up, Vector3.right, true, verts, uvs, tris);
}
// Front
if (IsTransparent(x, y, z + 1, worldPos, chunk))
{
BuildFace(block, new Vector3(x, y, z + 1), Vector3.up, Vector3.right, false, verts, uvs, tris);
}
}
}
}
visualMesh.vertices = verts.ToArray();
visualMesh.uv = uvs.ToArray();
visualMesh.triangles = tris.ToArray();
visualMesh.RecalculateBounds();
visualMesh.RecalculateNormals();
meshFilter.mesh = visualMesh;
meshCollider.sharedMesh = visualMesh;
return new MeshObject(visualMesh, meshCollider, meshFilter);
}
public static void BuildFace(int block, Vector3 corner, Vector3 up, Vector3 right, bool reversed, List<Vector3> verts, List<Vector2> uvs, List<int> tris)
{
int index = verts.Count;
Vector2 uvCorner = GetTexturePos(block);
verts.Add(corner);
verts.Add(corner + up);
verts.Add(corner + up + right);
verts.Add(corner + right);
Vector2 uvWidth = new Vector2(0.22f, 0.22f);
uvs.Add(uvCorner);
uvs.Add(new Vector2(uvCorner.x, uvCorner.y + uvWidth.y));
uvs.Add(new Vector2(uvCorner.x + uvWidth.x, uvCorner.y + uvWidth.y));
uvs.Add(new Vector2(uvCorner.x + uvWidth.x, uvCorner.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 static int[,,] GetMap(int chunkSize, List<Block> blocks)
{
int[,,] map = new int[chunkSize, chunkSize, chunkSize];
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; y++)
{
for (int z = 0; z < chunkSize; z++)
{
try
{
map[x, y, z] = (int)blocks[x + y + z].Id;
}
catch
{
Debug.Log(x + y + z);
}
}
}
}
return map;
}
public static bool IsTransparent(int x, int y, int z, Vector3 worldPos, Chunk chunk)
{
BlockType block = GetByte(x, y, z, worldPos, chunk);
switch (block)
{
case BlockType.air:
return true;
default:
return false;
}
}
public static BlockType GetByte(int x, int y, int z, Vector3 worldPos, Chunk chunk)
{
if ((x < 0) || (y < 0) || (z < 0) || (y >= chunk.chunkSize) || x >= chunk.chunkSize || z >= chunk.chunkSize)
{
Vector3 vec = new Vector3(x + worldPos.x, y + worldPos.y, z + worldPos.z);
if(chunkManager.GetChunkByWorldPos(vec) == null)
{
return Generate.GetTheoreticalByte(vec, chunk.biome);
}
return chunkManager.GetBlockByWorldPos(vec);
}
return chunk.GetBlockFromPosition(x, y, z);
}
public static Vector2 GetTexturePos(int block)
{
int b1 = block;
float lenght = 0.00f;
float height = 0.75f;
while (b1 >= 1)
{
if (b1 < 17)
{
lenght += 0.25f;
b1 -= 1;
}
else
{
height -= 0.25f;
b1 -= 16;
}
}
return new Vector2(lenght + 0.02f, height + 0.02f);
}
private static List<Block> GetBlockListFromMap(int[,,] map, int chunkSize)
{
List<Block> blockList = new List<Block>();
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; y++)
{
for (int z = 0; z < chunkSize; z++)
{
blockList.Add(Block.getBlock((BlockType)map[x, y, z]));
}
}
}
return blockList;
}
}
Chunk :
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshCollider))]
[RequireComponent(typeof(MeshFilter))]
public class Chunk : MonoBehaviour
{
public BlockType[,,] blockList;
public int chunkSize;
public Vector3 chunkPos;
public Biomes biome;
public Vector3 worldPos;
private Mesh mesh;
private MeshFilter meshFilter;
private MeshCollider meshCollider;
private MeshRenderer meshRenderer;
public bool isVisble = false;
private bool previousVisible = false;
public bool created = false;
public void Start()
{
}
public void Update()
{
if (isVisble == true && previousVisible == false)
{
gameObject.GetComponent<MeshRenderer>().enabled = true;
meshCollider.enabled = true;
previousVisible = true;
}
if (isVisble == false && previousVisible == true)
{
gameObject.GetComponent<MeshRenderer>().enabled = false;
meshCollider.enabled = false;
previousVisible = false;
}
}
public BlockType GetBlockFromPosition(Vector3 blockPos)
{
return GetBlockFromPosition(Mathf.FloorToInt(blockPos.x), Mathf.FloorToInt(blockPos.y), Mathf.FloorToInt(blockPos.z));
}
public MeshCollider MeshCollider
{
get
{
return meshCollider;
}
set
{
meshCollider = value;
}
}
public MeshFilter MeshFilter
{
get
{
return meshFilter;
}
set
{
meshFilter = value;
}
}
public Mesh Mesh
{
get
{
return mesh;
}
set
{
mesh = value;
}
}
public Vector3 ChunkPos
{
get
{
return chunkPos;
}
set
{
chunkPos = value;
}
}
public void SetChunk(Chunk chunk)
{
this.blockList = chunk.blockList;
this.chunkSize = chunk.chunkSize;
this.MeshCollider = chunk.MeshCollider;
this.MeshFilter = chunk.MeshFilter;
this.Mesh = chunk.Mesh;
this.ChunkPos = chunk.ChunkPos;
}
public void CreateChunk(Biomes biome)
{
this.worldPos = transform.position;
blockList = new BlockType[chunkSize, chunkSize, chunkSize];
this.biome = biome;
this.blockList = Generate.GenerateChunk(chunkSize, chunkPos, 20, biome);
BuildFullMesh();
created = true;
}
private void BuildFullMesh()
{
BlockType[,,] list = blockList;
MeshObject meshObject = new MeshObject(new Mesh(),
gameObject.GetComponent<MeshCollider>(), gameObject.GetComponent<MeshFilter>());
meshObject = MeshManager.CreateVisualMesh(chunkSize, list, meshObject, chunkPos, this);
mesh = meshObject.Mesh;
meshFilter = meshObject.MeshFilter;
meshCollider = meshObject.MeshCollider;
}
public void SetBlock(BlockType block, Vector3 worldPos)
{
worldPos = World.currentWorld.chunkManager.getBlockFromPos(worldPos);
SetBlock(block, (int)worldPos.x, (int)worldPos.y, (int)worldPos.z);
}
public void SetBlock(BlockType block, int x, int y, int z)
{
Debug.Log(x + ":" + y + ":" + z);
blockList[x, y, z] = block;
BuildFullMesh();
}
public string BlockListToString()
{
string list = "";
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; y++)
{
for (int z = 0; z < chunkSize; z++)
{
list += ", " + x + " : " + y + " : " + z + " = " + this.blockList[x, y, z];
}
}
}
return list;
}
public BlockType GetBlockFromPosition(int x, int y, int z)
{
//Debug.Log(x + ":" + y + ":" + z);
return blockList[x, y, z];
}
public static int[,,] GetIntFromBlockType(BlockType[,,] blocks, int chunkSize)
{
int[,,] map = new int[chunkSize, chunkSize, chunkSize];
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; y++)
{
for (int z = 0; z < chunkSize; z++)
{
map[x, y, z] = (int)blocks[x, y, z];
}
}
}
return map;
}
}
ChunkManager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChunkManager {
private Dictionary<Vector3, Chunk> chunkList;
public ChunkManager()
{
chunkList = new Dictionary<Vector3, Chunk>();
}
public BlockType GetBlockByWorldPos(Vector3 p)
{
Chunk chunk = GetChunkByWorldPos(p);
Vector3 blockPos = getBlockFromPos(p);
return chunk.GetBlockFromPosition(blockPos);
}
public BlockType GetMeshBuildBlock(Vector3 pos)
{
if(GetChunkByWorldPos(pos) == null)
{
return Generate.GetTheoreticalByte(pos, Biomes.plains);
}
else
{
Vector3 pos1 = getBlockFromPos(pos);
return GetChunkByWorldPos(pos).GetBlockFromPosition(pos1);
}
}
public void SetBlockByWorldPos(Vector3 p, BlockType block)
{
//Debug.Log(p.x + ":" + p.y + ":" + p.z + ":");
Chunk chunk = GetChunkByWorldPos(p);
Vector3 blockPos = getBlockFromPos(p);
chunk.SetBlock(block, blockPos);
}
public Vector3 getBlockFromPos(Vector3 pos)
{
Vector3 vector3 = new Vector3();
vector3.x = Mathf.Abs(pos.x) % World.staticChunkSize;
vector3.y = Mathf.Abs(pos.y) % World.staticChunkSize;
vector3.z = Mathf.Abs(pos.z) % World.staticChunkSize;
return vector3;
}
public void AddChunkToList(Chunk chunk)
{
chunkList.Add(chunk.chunkPos, chunk);
}
public Chunk GetChunkByPos(Vector3 pos)
{
if (chunkList.ContainsKey(pos))
{
return chunkList[pos];
}
return null;
}
public Chunk GetChunkByWorldPos(Vector3 pos)
{
Vector3 chunkPos = new Vector3(Mathf.Floor((pos.x / World.staticChunkSize)),
Mathf.Floor((pos.y / World.staticChunkSize)),
Mathf.Floor((pos.z / World.staticChunkSize)));
return GetChunkByPos(chunkPos);
}}