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()
};
}
}
Related
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.
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, I'm making a terraria-like game in unity 2019 on windws 10 using c# which has procedurally generated tilemaps and i have this script attached to a grid:
using UnityEngine;
using AccidentalNoise;
using System.Collections.Generic;
using UnityEngine.Tilemaps;
using System;
public class CompileTerrain : MonoBehaviour
{
public TileBase dirtTile;
public TileBase grassTile;
public TileBase stoneTile;
public List<GameObject> fractalLayers = new List<GameObject>();
public Tilemap grid;
public int width;
public int height;
public float seed;
public int caveSmoothness = 2;
void Start()
{
grid.ClearAllTiles();
int touchCount = 0;
Vector3Int newPos;
double nx, ny;
ModuleBase combinedTerrain = CavesAndMountains((uint)seed);
List<Vector3Int> terrainCoords = new List<Vector3Int>();
SMappingRanges ranges = new SMappingRanges();
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
nx = (ranges.mapx0 + ((double)x / (double)width) * (ranges.mapx1 - ranges.mapx0)) * 3;
ny = (ranges.mapy0 + ((double)y / (double)height) * (ranges.mapy1 - ranges.mapy0)) * 3;
if (combinedTerrain.Get(nx, ny) > 0f)
{
terrainCoords.Add(new Vector3Int(x, height - y, 0));
}
}
}
List<Tuple<int, int>> neighbors = new List<Tuple<int, int>>() {Tuple.Create(1, 1), Tuple.Create(-1, -1),
Tuple.Create(0, 1), Tuple.Create(1, 0),
Tuple.Create(0, -1), Tuple.Create(-1, 0),
Tuple.Create(-1, 1), Tuple.Create(1, -1)};
for (int index = 0; index < terrainCoords.Count; index++)
{
if (index == terrainCoords.Count)
{
break;
}
touchCount = 0;
for (int posAdd = 0; posAdd < neighbors.Count; posAdd++)
{
newPos = new Vector3Int(terrainCoords[index].x + neighbors[posAdd].Item1, terrainCoords[index].y + neighbors[posAdd].Item2, 0);
touchCount += terrainCoords.Contains(newPos) ? 1 : 0;
}
if (touchCount < 2)
{
terrainCoords.Remove(terrainCoords[index]);
}
}
for (int j = 0; j < caveSmoothness; j++)
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (!terrainCoords.Contains(new Vector3Int(x, y, 0)))
{
touchCount = 0;
for (int posAdd = 0; posAdd < neighbors.Count; posAdd++)
{
newPos = new Vector3Int(x + neighbors[posAdd].Item1, y + neighbors[posAdd].Item2, 0);
touchCount += terrainCoords.Contains(newPos) ? 1 : -1;
}
if (touchCount > 1)
{
terrainCoords.Add(new Vector3Int(x, y, 0));
}
}
}
}
}
foreach (Vector3Int blck in terrainCoords)
{
grid.SetTile(blck, stoneTile);
}
terrainCoords.Sort((x, y) => x.x == y.x ? x.y.CompareTo(y.y) : x.x.CompareTo(y.x));
terrainCoords.Reverse();
TileBase selectedTile;
int depth = 0;
int lastx = 0;
int lasty = terrainCoords[0].y + 1;
foreach (Vector3Int blck in terrainCoords)
{
depth = blck.x != lastx ? 0 : depth;
lasty = blck.x != lastx ? blck.y + 1 : lasty;
selectedTile = depth < 4 ? grassTile : stoneTile;
selectedTile = 3 < depth && depth < 30 ? dirtTile : selectedTile;
grid.SetTile(blck, selectedTile);
lastx = blck.x;
depth += lasty - blck.y;
lasty = blck.y;
}
int layerNum = 1;
List<Vector3Int> posList = new List<Vector3Int>();
foreach (GameObject layer in fractalLayers)
{
GetPerlinLayer component = layer.GetComponent<GetPerlinLayer>();
for (int k = 0; k < component.populateCount; k++)
{
layerNum++;
foreach (Vector3Int pos in component.GetFractalCoords(width, height, (uint)(seed * layerNum)))
if (grid.GetTile(pos) != null && grid.GetTile(pos) != grassTile)
{
grid.SetTile(pos, component.defaultTile);
}
}
}
}
public static ModuleBase CavesAndMountains(uint seed)
{
AccidentalNoise.Gradient ground_gradient = new AccidentalNoise.Gradient(0, 0, 0, 1);
// lowlands
Fractal lowland_shape_fractal = new Fractal(FractalType.BILLOW, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 2, 0.25, seed);
AutoCorrect lowland_autocorrect = new AutoCorrect(lowland_shape_fractal, 0, 1);
ScaleOffset lowland_scale = new ScaleOffset(0.125, -0.45, lowland_autocorrect);
ScaleDomain lowland_y_scale = new ScaleDomain(lowland_scale, null, 0);
TranslatedDomain lowland_terrain = new TranslatedDomain(ground_gradient, null, lowland_y_scale);
// highlands
Fractal highland_shape_fractal = new Fractal(FractalType.FBM, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 4, 2, seed);
AutoCorrect highland_autocorrect = new AutoCorrect(highland_shape_fractal, -1, 1);
ScaleOffset highland_scale = new ScaleOffset(0.25, 0, highland_autocorrect);
ScaleDomain highland_y_scale = new ScaleDomain(highland_scale, null, 0);
TranslatedDomain highland_terrain = new TranslatedDomain(ground_gradient, null, highland_y_scale);
// mountains
Fractal mountain_shape_fractal = new Fractal(FractalType.RIDGEDMULTI, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 8, 1, seed);
AutoCorrect mountain_autocorrect = new AutoCorrect(mountain_shape_fractal, -1, 1);
ScaleOffset mountain_scale = new ScaleOffset(0.3, 0.15, mountain_autocorrect);
ScaleDomain mountain_y_scale = new ScaleDomain(mountain_scale, null, 0.15);
TranslatedDomain mountain_terrain = new TranslatedDomain(ground_gradient, null, mountain_y_scale);
// terrain
Fractal terrain_type_fractal = new Fractal(FractalType.FBM, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 3, 0.125, seed);
AutoCorrect terrain_autocorrect = new AutoCorrect(terrain_type_fractal, 0, 1);
ScaleDomain terrain_type_y_scale = new ScaleDomain(terrain_autocorrect, null, 0);
AccidentalNoise.Cache terrain_type_cache = new AccidentalNoise.Cache(terrain_type_y_scale);
Select highland_mountain_select = new Select(terrain_type_cache, highland_terrain, mountain_terrain, 0.55, 0.2);
Select highland_lowland_select = new Select(terrain_type_cache, lowland_terrain, highland_mountain_select, 0.25, 0.15);
AccidentalNoise.Cache highland_lowland_select_cache = new AccidentalNoise.Cache(highland_lowland_select);
Select ground_select = new Select(highland_lowland_select_cache, 0, 1, 0.5, null);
// caves
Fractal cave_shape = new Fractal(FractalType.RIDGEDMULTI, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 1, 4, seed);
Bias cave_attenuate_bias = new Bias(highland_lowland_select_cache, 0.65);
Combiner cave_shape_attenuate = new Combiner(CombinerTypes.MULT, cave_shape, cave_attenuate_bias);
Fractal cave_perturb_fractal = new Fractal(FractalType.FBM, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 6, 3, seed);
ScaleOffset cave_perturb_scale = new ScaleOffset(0.5, 0, cave_perturb_fractal);
TranslatedDomain cave_perturb = new TranslatedDomain(cave_shape_attenuate, cave_perturb_scale, null);
Select cave_select = new Select(cave_perturb, 1, 0, 0.75, 0);
return new Combiner(CombinerTypes.MULT, cave_select, ground_select) as ModuleBase;
}
}
which i have so graciously borrowed and modified from the fine folks at accidental noise, and i made an empty gameobject which i attached this script to:
using UnityEngine;
using AccidentalNoise;
using System.Collections.Generic;
using UnityEngine.Tilemaps;
public class GetPerlinLayer : MonoBehaviour
{
public TileBase defaultTile;
public float threshold = 0.5f;
public int populateCount = 5;
public List<Vector3Int> GetFractalCoords(int width, int height, uint seed)
{
double nx, ny;
ModuleBase combinedTerrain = new Fractal(FractalType.FBM, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 6, 2, seed);
List<Vector3Int> fractalCoords = new List<Vector3Int>();
SMappingRanges ranges = new SMappingRanges();
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
nx = (ranges.mapx0 + ((double)x / (double)width) * (ranges.mapx1 - ranges.mapx0)) * 3;
ny = (ranges.mapy0 + ((double)y / (double)height) * (ranges.mapy1 - ranges.mapy0)) * 3;
if (combinedTerrain.Get(nx, ny) > threshold)
{
fractalCoords.Add(new Vector3Int(x, height - y, 0));
}
}
}
return fractalCoords;
}
}
and i attached different colored square sprites for each of those gameobjects, and saved them as a prefab. Once i had that prefab, i attached that to the fractalLayers list in my previous script to generate ores. And although it runs fine on a lower scale, I cant run it on a larger scale. And since there's no cure-all for making code run faster (aside from refactoring, which i don't know how to do), and i probably could've made parts of my code more efficient since I'm a novice, i would really like some insight from the eyes of a professional on how to make my code run better. I know i didn't explain everything about my project but its really just a bare-bones project those are the only scripts and unique parts about it, you can just infer what i did and fill in the blanks. Any help is appreciated. And if you could give me the extra push along with some information on the subject, I would love to have some videos recommended along with your insight to guide me along this process, since i am more of a visual learner. Thank you! =)
(For reference, it took me about 4 minutes to build this, with the settings shown here.)
So apparently my word generation method was fine, just the loop I was using to smoothen the terrain took too long.
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 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.