- Home /
Texture mapping procedural mesh with texture atlas? (Minecraft style terrain gen)
Hey guys, I'm currently working on a small learning project where I'm trying to recreate minecraft style terrain gen. It's been going well so far, however, I've hit a brick wall with texturing different block faces with different textures.
Heres what my terrain looks like at the minute:
As you can see, I've managed to get a single texture UV mapped on all sides of the cube fine, however I just can't seem to figure out how to make each face use a different texture.
Here's my terrain generation code:
public void RenderChunk()
{
Mesh mesh = GetComponent<MeshFilter>().mesh;
mesh.Clear(false);
//Clear Lists
vertices.Clear();
uvs.Clear();
triangles.Clear();
foreach (KeyValuePair<Vector3, TYPE> position in positions)
{
if (position.Value == TYPE.BLOCK)
{
List<int> neededVerts = CheckAdjacentPositions(position.Key);
if (neededVerts.Count > 0)
{
Block block = new Block();
List<Vector3> verts_ = block.GetBlockVertices(position.Key, neededVerts);
int vcount = 0;
int face = 0; //0 = front, 1 = back, 2 = top, 3 = bottom, 4 = right, 5 = left
int tri = 0; //Current triangle
int lastTri = 0; //Previous triangle
int firstIndex = 0; //First index to check.
foreach (Vector3 v in verts_)
{
vertices.Add(v);
triangles.Add(vertices.Count-1);
//Check which face the next six verts represent by checking the first 2 vertices of face.
if ((vcount == 0 || tri != lastTri))
{
if (neededVerts[firstIndex] == 0 && neededVerts[firstIndex + 1] == 2)
face = 0;
else if (neededVerts[firstIndex] == 5 && neededVerts[firstIndex + 1] == 4)
face = 1;
else if (neededVerts[firstIndex] == 0 && neededVerts[firstIndex + 1] == 7)
face = 5;
else if (neededVerts[firstIndex] == 1 && neededVerts[firstIndex + 1] == 2)
face = 4;
else if (neededVerts[firstIndex] == 2 && neededVerts[firstIndex + 1] == 3)
face = 2;
else if (neededVerts[firstIndex] == 0 && neededVerts[firstIndex + 1] == 6)
face = 3;
else
{
Debug.Log(neededVerts[firstIndex] + "??" + neededVerts[firstIndex + 1] + " " + firstIndex);
}
lastTri = tri;
}
//Assigns UVs based of face. Each number represents a face, break down of this is given above.
if (face == 0 || face == 1)
uvs.Add(new Vector2(v.y, v.x));
else if (face == 2 || face == 3)
uvs.Add(new Vector2(v.x, v.z));
else if (face == 4 || face == 5)
uvs.Add(new Vector2(v.z, v.y));
//Increment vert count
if (vcount >= 5)
{
tri++;
vcount = 0;
firstIndex += 6;
}
else
vcount++;
}
}
}
}
Vector3[] verts = new Vector3[(vertices.Count)];
int[] tris = new int[triangles.Count];
Vector2[] uvmap = new Vector2[uvs.Count];
//Convert Vertices, Triangles and UV's lists into arrays.
for (int i = 0; i < vertices.Count ; i++)
verts[i] = vertices[i];
for (int t = 0; t < triangles.Count ; t++)
tris[t] = triangles[t];
for (int z = 0; z < uvs.Count; z++)
uvmap[z] = uvs[z];
//Build the mesh
mesh.vertices = verts;
mesh.triangles = tris;
mesh.uv = uvmap;
mesh.Optimize();
mesh.RecalculateNormals();
GetComponent<MeshCollider>().sharedMesh = mesh;
}
The code pretty much checks loops through all of the blocks in a chunk, checking which faces are visible and adding them to the mesh. I then check which face a vertice is a part of and assign the appropriate UV. This is perfect to get the single texture mapped, but is useless for what I want to get done.
If anyone has any experience with this or has any ideas of what I can try to fix this it would be much appreciated :) thanks.
Answer by Bunny83 · Jun 02, 2019 at 09:30 AM
The way you generate your UV coordinates doesn't make much sense. You essentially just use the planar projected local space position of the vertex as UV coordinate. The UV space and the world / local space are two completely independent spaces. The UV space goes from 0 to 1 in both axis. You essentially define where this vertex is located on the texture by specifying a value between 0 and 1 (0 means left for the U coordinate and bottom for the V coordinate where as 1 means right for the U and top for the V coordinate).
When you generate a quad you will have to specify the 4 corners of the portion on the texture atlas you want to map to this quad. What you need is a a logical mapping of a certain "block type" + side (north,east,south,west,top,bottom) to a set of uv coordinates on the atlas. The "old" minecraft had a fix 256x256 texture with a fix grid of 16x16 texture patches where each patch was 16x16 pixels. To better support mods the texture atlas is now usually packed at initialization time when the game start. During packing individual textures into an atlas you have to keep track where on the atlas a certain texture is located (in the sense of UV coordinates).
If you want to support higher quality textures and you have many different textures they probably won't fit onto a single atlas. In such a case things get more complicated. To some degree you could use a special shader and use another vertex attribute to encode from which atlas it should sample the texture from. Another solution is to split a chunk into several seperate meshes depending to which atlast / material a block / face belongs. If you want to support transparent materials you probably have to do this anyways for those faces.
What's the best solution depends on what features you want to support, how your atlas(es) are setup / created, what platforms you want to support, etc...
Good to hear ;). Note you seem to use "Vector3" for the voxel coordinates. Since those coordinates are usually whole numbers you probably want to use "Vector3i" ins$$anonymous$$d. It's a similar struct but ins$$anonymous$$d of float values it has 3 integer values. This avoids many issues with rounding
Your answer
Follow this Question
Related Questions
Creating caves in voxel procedural mesh 0 Answers
What is wrong with this mesh editing code? 0 Answers
Anyone got a Voxel generator I can use? 0 Answers
Color changes at distance in procedural terrain 0 Answers
Minecraft-like ore generation 1 Answer