So, I am in the process of creating a infinite terrain system similar to Minecraft's. I am making it myself out of personal interest to learn. I have most everything working, and I have made some functions to generate the faces of cubes and then I'm adding it all together into a chunk, but for some reason I am getting a really weird thing where each row of the blocks are randomly much higher than the others, ruining the mesh entire. Anyone able to understand why, because I am completely lost as to the issue.
Note: The highlighted part is supposed to be a singular chunk, and it extends upwards over the entire chunk. Each set of them are a slight bit behind the previous, its supposed to be a completely flat face on the top.
// Face Generator
public enum Face { top, bottom, north, south, west, east };
public Mesh GetMeshDataFromFace(Face face, Vector3 pos)
{
Mesh _face = new Mesh();
Vector3[] _normals = new Vector3[4];
if (face == Face.top)
{
//Create the mesh for a upwards facing surface.
_face.vertices = new Vector3[] {
pos + new Vector3(0, 1, 0),
pos + new Vector3(1, 1, 0),
pos + new Vector3(1, 1, 1),
pos + new Vector3(0, 1, 1)
};
_face.triangles = new int[]{
0, 3, 2, 2, 1, 0
};
_normals = new Vector3[]{
Vector3.up, Vector3.up, Vector3.up, Vector3.up
};
}
if (face == Face.bottom)
{
//Create the mesh for a upwards facing surface.
_face.vertices = new Vector3[] {
pos + new Vector3(0, 0, 0),
pos + new Vector3(1, 0, 0),
pos + new Vector3(1, 0, 1),
pos + new Vector3(0, 0, 1)
};
_normals = new Vector3[]{
Vector3.down, Vector3.down, Vector3.down, Vector3.down
};
_face.triangles = new int[]{
0, 1, 2, 2, 3, 0
};
}
if (face == Face.north)
{
//Create the mesh for a upwards facing surface.
_face.vertices = new Vector3[] {
pos + new Vector3(0, 0, 1),
pos + new Vector3(1, 0, 1),
pos + new Vector3(1, 1, 1),
pos + new Vector3(0, 1, 1)
};
_normals = new Vector3[]{
Vector3.right, Vector3.right, Vector3.right, Vector3.right
};
_face.triangles = new int[]{
0, 1, 2, 2, 3, 0
};
}
if (face == Face.south)
{
//Create the mesh for a upwards facing surface.
_face.vertices = new Vector3[] {
pos + new Vector3(0, 0, 0),
pos + new Vector3(1, 0, 0),
pos + new Vector3(1, 1, 0),
pos + new Vector3(0, 1, 0)
};
_normals = new Vector3[]{
Vector3.left, Vector3.left, Vector3.left, Vector3.left
};
_face.triangles = new int[]{
0, 3, 1, 3, 2, 1
};
}
if (face == Face.west)
{
//Create the mesh for a upwards facing surface.
_face.vertices = new Vector3[] {
pos + new Vector3(0, 0, 0), // 0
pos + new Vector3(0, 1, 0), // 3
pos + new Vector3(0, 1, 1), // 7
pos + new Vector3(0, 0, 1) // 4
};
_normals = new Vector3[]{
Vector3.back, Vector3.back, Vector3.back, Vector3.back
};
_face.triangles = new int[]{
2, 1, 0, 0, 3, 2
};
}
if (face == Face.east)
{
//Create the mesh for a upwards facing surface.
_face.vertices = new Vector3[] {
pos + new Vector3(1, 0, 0),
pos + new Vector3(1, 1, 0),
pos + new Vector3(1, 1, 1),
pos + new Vector3(1, 0, 1)
};
_normals = new Vector3[]{
Vector3.forward, Vector3.forward, Vector3.forward, Vector3.forward
};
_face.triangles = new int[]{
0, 1, 2, 2, 3, 0
};
}
_face.SetNormals(_normals);
return _face;
}
Chunks store block data as a singular byte[] array of 4096 indexs.
Here is the code for generating the mesh
public Mesh CreateChunkMesh(ChunkObject chunkObj)
{
Chunk chunkData = chunkObj.chunkData;
Mesh mesh = new Mesh();
List<Mesh> meshesToCombine = new List<Mesh>();
for (int i = 0; i < chunkData.blocks.Length; i++)
{
if (chunkData.blocks[i] != 0)
{
Vector3 blockPos = new Vector3((i % 16), Mathf.Floor(i % 256), Mathf.Floor((i % 256) / 16));
Debug.Log(i + " | " + blockPos);
if (i + 256 >= chunkData.blocks.Length)
{
//This is the top layer of blocks.
if (!_loadedChunkObjs.ContainsKey(chunkObj.chunkPosition + new Vector3Int(0, 1, 0)))
{
//If the block ABOVE this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.top, blockPos));
}
}
if (i < 256)
{
if (!_loadedChunkObjs.ContainsKey(chunkObj.chunkPosition + new Vector3Int(0, -1, 0)))
{
//If the block ABOVE this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.bottom, blockPos));
}
}
if ((i % 256) < 16)
{
//This is the EAST layer of the chunk.
if (!_loadedChunkObjs.ContainsKey(chunkObj.chunkPosition + new Vector3Int(0, 0, 1)))
{
//If the block ABOVE this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.east, blockPos));
}
}
if ((i % 256) >= 240)
{
//This is the WEST layer of the chunk.
if (!_loadedChunkObjs.ContainsKey(chunkObj.chunkPosition + new Vector3Int(0, 0, -1)))
{
//If the block ABOVE this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.west, blockPos));
}
}
if (i % 16 == 0)
{
//This is the NORTH layer of the chunk
if (!_loadedChunkObjs.ContainsKey(chunkObj.chunkPosition + new Vector3Int(1, 0, 0)))
{
//If the block ABOVE this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.north, blockPos));
}
}
if (i % 16 == 1)
{
//This is the SOUTH layer of the chunk.
if (!_loadedChunkObjs.ContainsKey(chunkObj.chunkPosition + new Vector3Int(-1, 0, 0)))
{
//If the block ABOVE this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.south, blockPos));
}
}
//This block is not within any of the faces.
//Check each direction of the block
if (i + 256 <= chunkData.blocks.Length)
{
if (chunkData.blocks[i + 255] == 0) //TOP direction
{
//If the block ABOVE this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.top, blockPos));
}
}
if (i - 256 >= 0)
{
if (chunkData.blocks[i - 256] == 0) //BOTTOM Direction
{
//If the block BELOW this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.bottom, blockPos));
}
}
if (i + 1 <= chunkData.blocks.Length - 1)
{
if (chunkData.blocks[i + 1] == 0) //NORTH Direction
{
//If the block NORTH of this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.north, blockPos));
}
}
if (i - 1 >= 0)
{
if (chunkData.blocks[i - 1] == 0) //SOUTH Direction
{
//If the block SOUTH of this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.south, blockPos));
}
}
if (i + 16 <= chunkData.blocks.Length)
{
if (chunkData.blocks[i + 15] == 0) //WEST Direction
{
//If the block WEST of this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.west, blockPos));
}
}
if (i - 16 >= 0)
{
if (chunkData.blocks[i - 16] == 0) //EAST Direction
{
//If the block EAST of this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.east, blockPos));
}
}
}
}
CombineInstance[] combine = new CombineInstance[meshesToCombine.Count];
for (int i = 0; i < combine.Length; i++)
{
combine[i].mesh = meshesToCombine[i];
combine[i].transform = chunkObj.transform.localToWorldMatrix;
}
mesh.CombineMeshes(combine);
return mesh;
}
If there are any necessary details I didn't include let me know and ill add them.
Related
Trying to generate a sphere made of cubes like so:
This was easy, but any sphere of significant sizes has obvious performance issues if each cube is a separate gameobject, especially if the sphere is solid. After some digging, the answer appears to be to create a single mesh from only the exposed edges. Okay, makes sense. Found several videos, this one was the best I could find: link
I was able to customize the script for my needs, but I keep getting what appears to be a 2x2x2 cube regardless of the diameter input. I should be getting a hollow sphere made of a single mesh.
I suspect the issue is that I am not attaching the script to the correct type of gameobject, am missing a needed component on the gameobject, or because I skipped the UV portion.
Can anyone offer some advice?
Code:
public class PlanetGeneration : MonoBehaviour
{
private int planetDia = 30;
private int planetRadius;
//array of bools to keep track if each space is solid(true) or void(false)
private bool[,,] currentPlanet;
void Start()
{
currentPlanet = new bool[planetDia, planetDia, planetDia];
planetRadius = planetDia / 2 + 1;
GeneratePlanet();
GenerateMesh();
}
//define each block as void or solid
public void GeneratePlanet()
{
Vector3 planetCenter = new(planetRadius, planetRadius, planetRadius);
Vector3 currentPoint;
for (int x = 0; x < planetDia; x++)
{
for (int y = 0; y < planetDia; y++)
{
for (int z = 0; z < planetDia; z++)
{
//set the current position
currentPoint = new Vector3(x, y, z);
//check if this point is within the desired planet size
if (Vector3.Distance(planetCenter, currentPoint) <= planetRadius)
{
//make it a solid block
currentPlanet[x, y, z] = true;
}
//make it void
else currentPlanet[x, y, z] = false;
}
}
}
}
//generate mesh
public void GenerateMesh()
{
List<Vector3> Vertices = new List<Vector3>();
List<int> Triangles = new List<int>();
for (int x = 0; x < planetDia; x++)
{
for (int y = 0; y < planetDia; y++)
{
for (int z = 0; z < planetDia; z++)
{
//the 8 corners of a cube
Vector3[] VertexPos = new Vector3[8]
{
new Vector3(-1, 1, -1), new Vector3(-1, 1, 1),
new Vector3(1, 1, 1), new Vector3(1, 1, -1),
new Vector3(-1, -1, -1), new Vector3(-1, -1, 1),
new Vector3(1, -1, 1), new Vector3(1, -1, -1),
};
//first 4 digits are the vertices of the face, and the last 3 are the relative coords of the next block in that direction
int[,] Faces = new int[6, 7]{
{0, 1, 2, 3, 0, 1, 0}, //top
{7, 6, 5, 4, 0, -1, 0}, //bottom
{2, 1, 5, 6, 0, 0, 1}, //right
{0, 3, 7, 4, 0, 0, -1}, //left
{3, 2, 6, 7, 1, 0, 0}, //front
{1, 0, 4, 5, -1, 0, 0} //back
};
//if this block is solid
if (currentPlanet[x, y, z] == true)
{
//for each face of the block
for (int i = 0; i < 6; i++)
{
//check if it is on the edge of the overall array
//This part is dumb and needs to be removed. Keeping for now to prevent the array out of bounds error.
if(x + Faces[i, 4] == -1 || y + Faces[i, 5] == -1 || z + Faces[i, 6] == -1 ||
x + Faces[i, 4] == planetDia || y + Faces[i, 5] == planetDia || z + Faces[i, 6] == planetDia)
{
//do nothing
}
//check if the block next to it is void
else if(currentPlanet[x + Faces[i, 4], y + Faces[i, 5], z + Faces[i, 6]] == false)
{
AddQuad(Faces[i, 0], Faces[i, 1], Faces[i, 2], Faces[i, 3], Vertices.Count);
}
}
}
void AddQuad(int ai, int bi, int ci, int di, int i)
{
Vector3 a = VertexPos[ai];
Vector3 b = VertexPos[bi];
Vector3 c = VertexPos[ci];
Vector3 d = VertexPos[di];
Vertices.AddRange(new List<Vector3>() { a, b, c, d });
Triangles.AddRange(new List<int>() { i, i + 1, i + 2, i, i + 2, i + 3 });
}
}
}
}
this.gameObject.GetComponent<MeshFilter>().mesh = new Mesh()
{
vertices = Vertices.ToArray(),
triangles = Triangles.ToArray(),
};
}
}
After some more testing, and finding the youtube video authors updated code on github, I made several corrections and got it working. Code below in case anyone wants to reference.
Also of note - make a blank game object, attach the script, add a mesh filter and mesh renderer, set the material in the renderer.
This is a good starting point for anyone looking for this basic idea.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class PlanetGeneration : MonoBehaviour
{
//must be an odd number
private int planetDia = 39;
private int planetRadius;
//array of bools to keep track if each space is solid(true) or void(false)
private bool[,,] currentPlanet;
void Start()
{
currentPlanet = new bool[planetDia, planetDia, planetDia];
planetRadius = planetDia / 2 - 1;
GeneratePlanet();
GenerateMesh();
}
//define each block as void or solid
public void GeneratePlanet()
{
Vector3 planetCenter = new(planetRadius + 1, planetRadius + 1, planetRadius + 1);
Vector3 currentPoint;
for (int x = 0; x < planetDia; x++)
{
for (int y = 0; y < planetDia; y++)
{
for (int z = 0; z < planetDia; z++)
{
//set the current position
currentPoint = new Vector3(x, y, z);
//check if this point is within the desired planet size
if (Vector3.Distance(planetCenter, currentPoint) <= planetRadius)
{
//make it a solid block
currentPlanet[x, y, z] = true;
}
//make it void
else currentPlanet[x, y, z] = false;
}
}
}
}
//generate mesh
public void GenerateMesh()
{
List<int> Triangles = new();
List<Vector3> Verticies = new();
List<Vector2> uv = new();
for (int x = 1; x < planetDia; x++)
{
for (int y = 1; y < planetDia; y++)
{
for (int z = 1; z < planetDia; z++)
{
//the 8 corners of a cube
Vector3[] VertexPos = new Vector3[8]
{
new Vector3(-1, 1, -1), new Vector3(-1, 1, 1),
new Vector3(1, 1, 1), new Vector3(1, 1, -1),
new Vector3(-1, -1, -1), new Vector3(-1, -1, 1),
new Vector3(1, -1, 1), new Vector3(1, -1, -1),
};
//first 4 digits are the vertices of the face, and the last 3 are the relative coords of the next block in that direction, the last two are for the UV
int[,] Faces = new int[6, 9]{
{0, 1, 2, 3, 0, 1, 0, 0, 0}, //top
{7, 6, 5, 4, 0, -1, 0, 1, 0}, //bottom
{2, 1, 5, 6, 0, 0, 1, 1, 1}, //right
{0, 3, 7, 4, 0, 0, -1, 1, 1}, //left
{3, 2, 6, 7, 1, 0, 0, 1, 1}, //front
{1, 0, 4, 5, -1, 0, 0, 1, 1} //back
};
//if this block is solid
if (currentPlanet[x, y, z] == true)
{
//for each face of the block
for (int i = 0; i < 6; i++)
{
//check if the block next to it is void
if(currentPlanet[x + Faces[i, 4], y + Faces[i, 5], z + Faces[i, 6]] == false)
{
AddQuad(i, Verticies.Count);
}
}
}
void AddQuad(int facenum, int v)
{
// Add Mesh
for (int i = 0; i < 4; i++) Verticies.Add(new Vector3(x, y, z) + VertexPos[Faces[facenum, i]] / 2f);
Triangles.AddRange(new List<int>() { v, v + 1, v + 2, v, v + 2, v + 3 });
// Add uvs
Vector2 bottomleft = new Vector2(Faces[facenum, 7], Faces[facenum, 8]) / 2f;
uv.AddRange(new List<Vector2>() { bottomleft + new Vector2(0, 0.5f), bottomleft + new Vector2(0.5f, 0.5f), bottomleft + new Vector2(0.5f, 0), bottomleft });
}
}
}
}
GetComponent<MeshFilter>().mesh = new Mesh()
{
vertices = Verticies.ToArray(),
triangles = Triangles.ToArray(),
uv = uv.ToArray()
};
}
}
I've been trying to create a plane mesh in Unity using code, and I've come across a very interesting problem. I created an int[], filled it up with some values, and it's length is somehow zero. I've never encountered anything this quirky, so I'd enjoy a bit of help.
mesh.triangles = new int[]
{
4, 6, 5, 5, 6, 7
};
... // Not important stuff
Debug.Log(mesh.triangles.Length);
I don't know what is happening, so I really haven't tried anything. But in the console, there is an error message stating Failed setting triangles. Some indices are referencing out of bounds vertices. IndexCount: 6, VertexCount: 4.This is probably really important, but I don't understand some parts of the message(especially the last part). And if it makes a difference, I have an array concatenation method being called to add the first triangles to these ones. I initially identified this problem when the half of my mesh still wasn't appearing. I would really appreciate help; thanks.
Edit:
To cut confusion, I'm just going to paste my whole entire method.
private void CreateQuad(ref Mesh mesh, Vector3 offset, bool first)
{
if (first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero, Vector3.right, Vector3.forward, new Vector3(1, 0, 1)
};
mesh.triangles = new int[]
{
0, 2, 1, 1, 2, 3
};
mesh.normals = new Vector3[]
{
Vector3.back, Vector3.back, Vector3.back, Vector3.back
};
mesh.tangents = new Vector4[]
{
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1)
};
mesh.uv = new Vector2[]
{
Vector2.zero, Vector2.right, Vector2.up, Vector2.one
};
}
else if (!first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero + offset,
Vector3.right + offset,
Vector3.forward + offset,
new Vector3(1, 0, 1) + offset
};
mesh.triangles = new int[]
{
4, 6, 5, 5, 6, 7
};
mesh.normals = new Vector3[]
{
Vector3.back, Vector3.back, Vector3.back, Vector3.back
};
mesh.tangents = new Vector4[]
{
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1)
};
mesh.uv = new Vector2[]
{
Vector2.zero, Vector2.right, Vector2.up, Vector2.one
};
Debug.Log(mesh.triangles.Length);
}
}
You only have FOUR vertices!
mesh.vertices = new Vector3[]
{
Vector3.zero + offset,
Vector3.right + offset,
Vector3.forward + offset,
new Vector3(1, 0, 1) + offset
};
So the indices 4, 6, 5, 5, 6, 7 are all invalid! If you have only four vertices you can maximum have the indices 0, 1, 2, 3
=> Unity simply rejects them all. You should have already taken that hint from the error you get
Failed setting triangles. Some indices are referencing out of bounds vertices. IndexCount: 6, VertexCount: 4
Now it is a bit unclear what exactly you are trying to achieve here but
either you want to REPLACE the vertices: In this case there is no reason to set new triangle instances etc at all! It is enough to connect them only once:
private void CreateQuad(ref Mesh mesh, Vector3 offset, bool first)
{
if (first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero, Vector3.right, Vector3.forward, new Vector3(1, 0, 1)
};
mesh.triangles = new int[]
{
0, 2, 1, 1, 2, 3
};
mesh.normals = new Vector3[]
{
Vector3.back, Vector3.back, Vector3.back, Vector3.back
};
mesh.tangents = new Vector4[]
{
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1)
};
mesh.uv = new Vector2[]
{
Vector2.zero, Vector2.right, Vector2.up, Vector2.one
};
}
else if (!first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero + offset,
Vector3.right + offset,
Vector3.forward + offset,
new Vector3(1, 0, 1) + offset
};
}
}
the other properties can simply be left untouched since you only want to update the vertex positions.
Or you actually wanted to ADD more faces. In that case you rather want to append to the existing arrays:
private void CreateQuad(ref Mesh mesh, Vector3 offset, bool first)
{
if (first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero, Vector3.right, Vector3.forward, new Vector3(1, 0, 1)
};
mesh.triangles = new int[]
{
0, 2, 1, 1, 2, 3
};
mesh.normals = new Vector3[]
{
Vector3.back, Vector3.back, Vector3.back, Vector3.back
};
mesh.tangents = new Vector4[]
{
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1)
};
mesh.uv = new Vector2[]
{
Vector2.zero, Vector2.right, Vector2.up, Vector2.one
};
}
else if (!first)
{
// fist get already existing verts etc
var oldVerts = mesh.vertices;
var oldTris = mesh.triangles;
// create new vertices and triangles arrays with additional space for the new quad
var newVerts = new Vector3[oldVerts.Length + 4];
var newTris = new int[oldTris.Length + 6];
// copy over the existing vertices and triangles
Array.Copy(oldVerts, newVerts, olVerts.Length);
Array.Copy(oldTris, newtris, oldtris.Length);
// then append the new vertices
newVerts[oldverts.Length + 0] = Vector3.zero + offset;
newVerts[oldverts.Length + 1] = Vector3.right + offset;
newVerts[oldverts.Length + 2] = Vector3.forward + offset;
newVerts[oldverts.Length + 3] = new Vector3(1, 0, 1) + offset;
// append the new triangles
newTris[oldTris.Length + 0] = oldverts.Length + 0;
newTris[oldTris.Length + 1] = oldverts.Length + 2;
newTris[oldTris.Length + 2] = oldverts.Length + 1;
newTris[oldTris.Length + 3] = oldverts.Length + 1;
newTris[oldTris.Length + 4] = oldverts.Length + 2;
newTris[oldTris.Length + 5] = oldverts.Length + 3;
// get the min and max points for filling the uvs (not the most efficient way probably but it is what it is ^^)
// we later want to spread out the UV values linear between 0 (min) and 1 (max) on the given vertices
var min = Vector3.zero;
var max = Vector3.zero;
foreach(var vertex in newVerts)
{
min = Vector3.Min(min, vertex);
max = Vector3.Max(max, vertex);
}
// also fill new tangents and normals and uvs (if really necessary)
var newNormals = new Vector3[newVerts.Length];
var newTangents = new Vector4[newVerts.Length];
var newUVs = new Vector2[newVerts.Length];
for(var i = 0; i < newVerts.Length; i++)
{
var vertex = newVerts[i];
newUVs[i] = new Vector2((vertex.x - min.x) / (max.x - min.x), (vertex.z - min.z) / (max.z - min.z));
newNormals[i] = Vector3.back;
newTangents[i] = new Vector4(1, 0, 0, -1);
};
// finally set them all back
mesh.vertices = newVerts;
mesh.triangles = newTris;
mesh.normals = newNormals;
mesh.tangents = newTangents;
mesh.uv = newUs;
}
}
You first need to set the vertex array before altering the triangles. As Unity writes "It is recommended to assign a triangle array after assigning the vertex array, in order to avoid out of bounds errors."
mesh.vertices = new Vector3[] { new Vector3(-1,0,1), new Vector3(-1,0,-1),
new Vector3(1,0,-1), new Vector3(1,0,1) };
mesh.triangles = new int[] {0,1,2,0,2,3};
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 a 3D application in WPF and having trouble with the camera. It should be possible to rotate the camera around its own axis (with other words, look around) using the mouse but I can not get it to work properly. I create the camera with the following code:
PerspectiveCamera perspectiveCamera = new PerspectiveCamera(new Point3D(0, 30, 0), new Vector3D(0, -1, 0), new Vector3D(0, 0, 1), 90);
perspectiveCamera.NearPlaneDistance = 0.001;
perspectiveCamera.FarPlaneDistance = 1000;
center = new TranslateTransform3D(0, 30, 0);
rot_x = new AxisAngleRotation3D(new Vector3D(1, 0, 0), 0);
rot_y = new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0);
rot_z = new AxisAngleRotation3D(new Vector3D(0, 0, 1), 0);
zoom = new ScaleTransform3D(1, 1, 1);
Transform3DGroup t = new Transform3DGroup();
t.Children.Add(zoom);
t.Children.Add(new RotateTransform3D(rot_x));
t.Children.Add(new RotateTransform3D(rot_y));
t.Children.Add(new RotateTransform3D(rot_z));
t.Children.Add(center);
perspectiveCamera.Transform = t;
myViewport3D.Camera = perspectiveCamera;
And then I try to rotate it using the following code:
private void OnViewportMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (mouseLeftIsDown)
{
Point position = e.GetPosition(this);
double rotZAngle = (rot_z.Angle % 360) + oldLeftPosition.X - position.X;
double rotXAngle = (rot_x.Angle % 360) + position.Y - oldLeftPosition.Y;
if (rotZAngle < 0) //rotZAngle is negative, make it positive
{
rotZAngle = 360 + rotZAngle;
}
if (rotXAngle < 0) //rotXAngle is negative, make it positive
{
rotXAngle = 360 + rotXAngle;
}
rot_z.Angle = rotZAngle;
rot_x.Angle = rotXAngle;
oldLeftPosition = position;
}
}
However, it seams that the rotation is not happening around the camera and instead somehere else. The model that I load at position (0,0,0) is becoming visible after I rotate 180 degrees around the z axis which should not be the case.
What am I missing?
I have a 10*10 plane of cubes. I want only visible cubes to be drawn, so I use a Bounding frustum. The problem is that when the first cube from the grid (at location 0,0) gets out of the frustum, all cubes disappear.
Here is my code in Cube.cs:
public class Cube
{
Vector3 scale;
float textureScale;
GraphicsDevice device;
Effect effect;
VertexBuffer vertexBuffer;
Texture2D texture;
public Vector3 GlobalPosition = new Vector3(0, 0, 0);
public Vector3[] vertices = new Vector3[36];
public Cube(Vector3 scale, float textureScale, Effect effect, Texture2D texture, GraphicsDevice device)
{
this.scale = scale;
this.textureScale = textureScale;
this.device = device;
this.effect = effect;
this.texture = texture;
vertices[0] = new Vector3(-1, 1, -1);
vertices[1] = new Vector3(-1, -1, -1);
vertices[2] = new Vector3(1, -1, -1);
vertices[3] = new Vector3(1, -1, -1);
vertices[4] = new Vector3(1, 1, -1);
vertices[5] = new Vector3(-1, 1, -1);
//Front
vertices[6] = new Vector3(1, -1, 1);
vertices[7] = new Vector3(-1, -1, 1);
vertices[8] = new Vector3(-1, 1, 1);
vertices[9] = new Vector3(-1, 1, 1);
vertices[10] = new Vector3(1, 1, 1);
vertices[11] = new Vector3(1, -1, 1);
//Bottom
vertices[12] = new Vector3(-1, -1, -1);
vertices[13] = new Vector3(-1, -1, 1);
vertices[14] = new Vector3(1, -1, -1);
vertices[15] = new Vector3(1, -1, 1);
vertices[16] = new Vector3(1, -1, -1);
vertices[17] = new Vector3(-1, -1, 1);
//Top
vertices[18] = new Vector3(1, 1, -1);
vertices[19] = new Vector3(-1, 1, 1);
vertices[20] = new Vector3(-1, 1, -1);
vertices[21] = new Vector3(-1, 1, 1);
vertices[22] = new Vector3(1, 1, -1);
vertices[23] = new Vector3(1, 1, 1);
//Left
vertices[24] = new Vector3(-1, -1, 1);
vertices[25] = new Vector3(-1, -1, -1);
vertices[26] = new Vector3(-1, 1, -1);
vertices[27] = new Vector3(-1, 1, -1);
vertices[28] = new Vector3(-1, 1, 1);
vertices[29] = new Vector3(-1, -1, 1);
//Right
vertices[30] = new Vector3(1, -1, -1);
vertices[31] = new Vector3(1, -1, 1);
vertices[32] = new Vector3(1, 1, -1);
vertices[33] = new Vector3(1, -1, 1);
vertices[34] = new Vector3(1, 1, 1);
vertices[35] = new Vector3(1, 1, -1);
for (int i = 0; i < 36; i++)
{
vertices[i] = vertices[i] * scale + GlobalPosition;
}
VertexPositionNormalTexture[] verticesList = GetVPNT();
vertexBuffer = new VertexBuffer(device, VertexPositionNormalTexture.VertexDeclaration, verticesList.Length, BufferUsage.WriteOnly);
vertexBuffer.SetData<VertexPositionNormalTexture>(verticesList.ToArray());
}
public void Draw(Matrix View, Matrix Projection, Vector3 pos)
{
effect.CurrentTechnique = effect.Techniques["Textured"];
effect.Parameters["xWorld"].SetValue(Matrix.Identity * Matrix.CreateTranslation(pos));
effect.Parameters["xView"].SetValue(View);
effect.Parameters["xProjection"].SetValue(Projection);
effect.Parameters["xTexture"].SetValue(texture);
effect.Parameters["xEnableLighting"].SetValue(true);
effect.Parameters["xLightDirection"].SetValue(new Vector3(30, 30, 30));
effect.Parameters["xAmbient"].SetValue(0.5f);
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
device.SetVertexBuffer(vertexBuffer);
device.DrawPrimitives(PrimitiveType.TriangleList, 0, vertexBuffer.VertexCount / 3);
}
}
public VertexPositionNormalTexture[] GetVPNT()
{
VertexPositionNormalTexture[] vpnt = new VertexPositionNormalTexture[36];
Vector2[] texCoords = CalculateTexCoords(vertices, "tile");
for (int i = 0; i < 36; i++)
{
vpnt[i] = new VertexPositionNormalTexture(vertices[i], new Vector3(0, 0, 1), texCoords[i]);
}
return vpnt;
}
public Vector2[] CalculateTexCoords(Vector3[] vec, string type)
{
List<Vector2> texCoords = new List<Vector2>();
for (int i = 0; i < 12; i++)
{
if (AllEqual<float>(vertices[i * 3 + 0].X, vertices[i * 3 + 1].X, vertices[i * 3 + 2].X))
{
Vector2[] normvec = new Vector2[3];
normvec[0] = TexNorm(new Vector2(vertices[i * 3 + 0].Z, vertices[i * 3 + 0].Y), type);
normvec[1] = TexNorm(new Vector2(vertices[i * 3 + 1].Z, vertices[i * 3 + 1].Y), type);
normvec[2] = TexNorm(new Vector2(vertices[i * 3 + 2].Z, vertices[i * 3 + 2].Y), type);
texCoords.AddRange(normvec);
}
if (AllEqual<float>(vertices[i * 3 + 0].Y, vertices[i * 3 + 1].Y, vertices[i * 3 + 2].Y))
{
Vector2[] normvec = new Vector2[3];
normvec[0] = TexNorm(new Vector2(vertices[i * 3 + 0].X, vertices[i * 3 + 0].Z), type);
normvec[1] = TexNorm(new Vector2(vertices[i * 3 + 1].X, vertices[i * 3 + 1].Z), type);
normvec[2] = TexNorm(new Vector2(vertices[i * 3 + 2].X, vertices[i * 3 + 2].Z), type);
texCoords.AddRange(normvec);
}
if (AllEqual<float>(vertices[i * 3 + 0].Z, vertices[i * 3 + 1].Z, vertices[i * 3 + 2].Z))
{
Vector2[] normvec = new Vector2[3];
normvec[0] = TexNorm(new Vector2(vertices[i * 3 + 0].X, vertices[i * 3 + 0].Y), type);
normvec[1] = TexNorm(new Vector2(vertices[i * 3 + 1].X, vertices[i * 3 + 1].Y), type);
normvec[2] = TexNorm(new Vector2(vertices[i * 3 + 2].X, vertices[i * 3 + 2].Y), type);
texCoords.AddRange(normvec);
}
}
return texCoords.ToArray();
}
public bool AllEqual<T>(params T[] values)
{
if (values == null || values.Length == 0)
return true;
return values.All(v => v.Equals(values[0]));
}
public Vector2 TexNorm(Vector2 vecIn, string type)
{
Vector2 vec = new Vector2();
//Remove negative coordinates
if (vecIn.Y < 0)
vec.Y = 0;
if (vecIn.X < 0)
vec.X = 0;
switch (type)
{
case "stretch":
{
if (vecIn.X > 0)
vec.X = 1;
if (vecIn.Y > 0)
vec.Y = 1;
break;
}
case "tile":
{
if (vecIn.X > 0)
vec.X = textureScale;
if (vecIn.Y > 0)
vec.Y = textureScale;
break;
}
}
return vec;
}
public bool IsVisible(Matrix VP)
{
BoundingFrustum bf = new BoundingFrustum(VP);
bool isVis = true;
//Check weather at least one vertices are out of the frustum
for (int i = 0; i < 36; i++)
{
if (bf.Contains(vertices[i]) != ContainmentType.Contains)
{
isVis = false;
break;
}
}
return isVis;
}
}
Draw() method in Main.cs:
for (int i = 0; i < 10; i++)
{
for (int k = 0; k < 10; k++)
{
Cube cube = new Cube(Vector3.One, 1, effect, woodTexture, device);
if (cube.IsVisible(View * Projection))
{
cube.Draw(View, Projection, new Vector3(2 * i, 0, 2 * k));
}
}
}
I would also like if you can give me link to an atlas texturing tutorial/sample, Thanks!
Your cube.IsVisible(...) method does not take into account latter transformation you're applying when you're drawing the cube, i.e you're always checking against your original cube, not the transformed ones.
Please reference this answer as it touches both your culling problem (frustum-box) as well as instancing.