- Home /
How do I UV-map a procedurally generated mesh(like a mine-craft chunk but with optimized geometry)?
I have a mesh generated based on voxel-data. Think mine-craft. Now I have optimized the geometry by combining faces. So I have a bunch of different sized rectangles instead of even sized squares. How do I get these textured based on what type of voxel they represent?
If every square had one face it would be easy, I just could use a texture atlas and scale + offset each face uv based on voxel. BUT I'm dealing with rectangles.
The solution I'm currently working on involves splitting each face type into a submesh or different mesh and then applying a different material to each one. But I'm not sure that this is going to be efficient, let's say I've got 32 different tiletypes in a chunk I'd end up with 32 submeshes per chunk. Not sure how well Unity is going to handle this.
Any ideas how to get this working with something like a texture atlas instead?
Maybe there is something we could do with a shader?
Any suggestions appreciated.
Uhmmm, why do you combine faces when you actually need them seperated? 32 Submeshes means you have 32 drawcalls per chunk, that's not really "optimised". The Vertex count isn't that important. that's why $$anonymous$$ecraft uses only one material for the whole terrain.
There was already a question about tiling an atlas texture, however this is much more complicated than using simply more vertices.
This thread in the forums is a long and detailed technical discussion about $$anonymous$$inecraft-style voxel worlds. It covers some of the information you're looking for.
I create a single mesh out of the potentially visible block faces, two triangles per block face. I get one draw call, runs at hundreds of frames per second. Probably doesn't need to be optimized any more than that.
http://www.youtube.com/watch?v=$$anonymous$$a7btlZUHU$$anonymous$$
@bunny and Dave:
The "1 face per voxel outside"-method is definitely a good one, and I recommend it at least as a starting if not end point for people who want to make their $$anonymous$$ecraft clone.
I'm looking to create more of a general framework for box-based level and object creation/modification based on voxeldata. So more speed is always a good thing(higher voxelresolution etc). Different techniques have different advantages, it's also pretty easy to mix techniques(+we can heavily optimize for static worlds)
optimized geometry with submesh/mesh technique:
we have to keep the mesh count to a reasonable $$anonymous$$imum
I don't expect to get a draw call per mesh but per material(batching)!
we can save TONS of unnecessary geometry
we can consider bigger chunks (if 1 chunk is 1 mesh and we double the chunk dimensions we save 3 meshes)
1face per voxel outside + atlas:
very straight forward to work with
works pretty well
tons of unnecessary geometry
texture atlas limits texture size and causes problems for mimapping + filtering (that's why $$anonymous$$ecraft doesn't use mipmaps)
Answer by Eric5h5 · Feb 11, 2013 at 09:35 PM
If you want to use a texture atlas then you'd need to get rid of the optimization and just use 1 quad per voxel. So it's a question of whether you want to save draw calls or vertices. I'd guess which is better would depend on the kind of world you'd typically have; if there are large flat areas with the same texture, and you're saving tons of vertices by using rectangles, then probably it would be better to use submeshes.
I think that's a very solid summary of the problem, I'll have to spend some time testing, I'll def support various methods.
However, it seems that there should be a possibly superior solution involving shaders.
http://www.volumesoffun.com/polyvox/documentation/dev/manual/Texture$$anonymous$$apping.html
"Texture arrays These provide the perfect solution to the problem of handling a large number of textures... at least if they are supported by your hardware. They were introduced with OpenGL 3 and Direct3D 10 but older versions of OpenGL may still be able to access the functionality via extensions. They allow you to bind an array of textures to the shader, and the advantage compared to a texture atlas is that the hardware understands that the textures are seperate and so avoids the filtering and mipmapping issues. Beyond the hardware requirements, the only real limitation is that all the textures must be the same size."
Anybody knows more about this or would like to share an opinion?
Answer by MountDoomTeam · Feb 11, 2013 at 07:41 PM
some things to research-
Combine children extended version export object script
the planar uv script- http://docs.unity3d.com/Documentation/ScriptReference/Mesh-uv.html if you have objects divided into planar surfaces, you can calculate the UVs for each plane individually with that.
I just put an extra name/tag on some of my tiles to say if they pointed North South East West up down, and used 6 cases of the above formula to UV them perfectly.
also there is a script that automatically makes texture Atlas somewhere on this forum.
Normally, if you textured your model before combining it/deleting triangles that are duplicates, well the texture information would still be there when you combine it and when you create rectangles.
feel free to post any code for merging squares into rectangles, it would be useful for me also! I'd love to see it!
Edit--------------
I can't believe it I commented in length and then I got a call, and I pressed post your answer and disappeared everything.
Texture Atlas doesn't make mistakes, you specify the part of the texture instead of the file name. when the code allows for the atlas it's fine.
Check this awesome page, it has Kubik projection which is what I had in mind a vaguely for my project and also for cubes it should be very good. I can probably paste the code afterwards because I'm probably doing it today. http://docs.luxology.com/modo/501/help/pages/shaderendering/ShaderItems/TextureLocator.html
the other way to do it is convert the normals to UVs using the render function from Unifi wiki mc blob marching cubes code.
I divided the function into 2, to render the vertices and triangles, And the UVs afterwards:
/*Unity and Sample Specific*/
private void renderMesh()
{
int i;
/*Clear the Vertices that don't have any real information assigned to them */
for(i=0;i<vertP;i++) {fv[i]=newVertex[i];fn[i]=newNormal[i];
}
for(i=0;i<triP;i++) {ft[i]=newTri[i];}
for(i=triP;i<ft.Length;i++) {ft[i]=0;}
Mesh mesh=((MeshFilter) GetComponent("MeshFilter")).mesh;
mesh.vertices = fv ;
mesh.uv = fuv;
mesh.triangles = ft;
mesh.normals = fn;
/*For Disco Ball Effect*/
mesh.RecalculateNormals();
}
private void nls()
{
int i;
Mesh mesh=((MeshFilter) GetComponent("MeshFilter")).mesh;
fn = mesh.normals;
/*Clear the Vertices that don't have any real information assigned to them */
for(i=0;i<vertP;i++) {
fuv[i]=tada2[tadac2++];
Vector3 fuvt=transform.TransformPoint(fn[i]).normalized;
//fuv[i].x=(fuvt.x+1f)*.5f;fuv[i].y=(fuvt.y+1f)*.5f;}
fuv[i].x=fn[i].x;fuv[i].y=fn[i].y;}
for(i=vertP;i<fv.Length;i++) {fv[i][0]=0;fn[i][0]=0;fuv[i][0]=0;
fv[i][1]=0;fn[i][1]=0;fuv[i][1]=0;
fv[i][2]=0;}
//Mesh mesh=((MeshFilter) GetComponent("MeshFilter")).mesh;
mesh.uv = fuv;
gameObject.AddComponent("MeshCollider");
/*For Disco Ball Effect*/
//mesh.RecalculateNormals();
}
None of this helps me unfortunately.
Combine children extended version export object script: great script, thank you russia (and neodrop). I don't see it applying to the problem at hand though, I think it's more for tileprefabbased maps.
the planar uv script: that's exactly the issue, I can't use face mapping since I'm saving a lot of unnecessary faces. So if I have 2 neighbouring blockfaces combined into one: ins$$anonymous$$d of: the grasspart of the texture atlas 2 times I'd get: the grasspart of the texture atlas + whatever is next to it
"Normally, if you textured your model before combining it/deleting triangles that are duplicates, well the texture information would still be there when you combine it and when you create rectangles.": UVcoordinates are mapped to vertices! I am creating only geometry I figure to be necessary.
INFO POST: I'd love to help in any way I can, once I finish it I will release the whole thing to the community for blockbased level creation and voxel items(like a destructable house for example).
for theory(also read the follow up article):
http://0fps.wordpress.com/2012/06/30/meshing-in-a-$$anonymous$$ecraft-game/
for praxis: I take the voxeldata and create a procedural mesh from it, I don't create and then destroy I just create. I go from layer to layer (like first all visible top faces of the toplayer) and put it into a 2d array so I can visualize it better like:
000221100
000221100
000001100
111101100
(0 is empty) based on this data I create the faces. basesize for this example would be 16 faces or so, greedily optimized it should be 3 faces.
I do the greedy optimization like this: 1.I go through each point 2.I try to grow the biggest "rectangle mask" I can, first I grow its width as far as possible than it's height(side note: this doesn't actually grow the biggest rectangle possible since it prefers width over height but works well enough) 3.I create a face based on this "rectangle mask" and mark the points inside the face as processed
INFO POST 2:Some Code in progress(probably confusing):
facematrixw = sizex;
facematrixh = sizez;
facematrix = new int [facematrixw,facematrixh];//<---don't create bugs here
for (j=0;j<sizey;j++)//the first of the nested loop is the level we're scanning, so j which refers to y has to come first for top and bottom faces
{
for (i=0;i<sizex;i++) for (k=0;k<sizez;k++) //now i refers to x, k refers to y, CREATE FACE$$anonymous$$ATRIX FOR THE LEVEL WE'RE SCANNING
{
if (!Solid(i,j,k)){facematrix[i,k]=0;continue;} //if there isn't a block it can't have a face so we skip it
if (!Solid (i,j+1,k)){facematrix[i,k]=volume[i,j,k];} //if there is a block we copy it's data to the face matrix
}
//now that we've got a 2dmatrix of the faces, we'll grow the mask to its maxw and then to it's maxh and then we'll process it
for (int x=0;x<sizex;x++)for (int y=0;y<sizez;y++) if (facematrix[x,y]!=0)//for every face (if facematrix is 0 then there is no face or its processed)
{
sweepmask=facematrix[x,y]; //first we only mask to the tiletype found at x,y
for (xsweep=0;xsweep<facematrixw-x;xsweep++){if(facematrix[x+xsweep,y]!=sweepmask){break;}} //GROW $$anonymous$$AS$$anonymous$$WIDTH this should result in: xsweep = maximum we can sweep into the x direction from x,y
for (ysweep=0;ysweep<facematrixh-y;ysweep++){if(facematrix[x,y+ysweep]!=sweepmask){break;}} //GROW $$anonymous$$AS$$anonymous$$HEIGHT this should result in: ysweep = maximum we can sweep into the y direction from x,y
//Debug.Log(xsweep + " XsweepY " + ysweep);
//after we've found the maximum size of our sweepmask we have to 1.mark the processed faces as disabled 2.create a face from the maskdata
for (int a=x;a<x+xsweep;a++)for (int b=y;b<y+ysweep;b++){facematrix[a,b]=0; Debug.Log (a+" "+b+" = 0");}//1.mark processed faces
AddTopFace (i+x,j,k+y,xsweep*dim,ysweep*dim);//2.create face from sweepmask
}
}
still, if you have a way to go through your vertices and say which plane/triangle they belong to, X,Y and Z plane, you can just give the vertex the transform.transformpoint on XY, YZ, or XZ, and it will give them proper UV.
sorry I can't understand that code at the moment I am slow and I was just learning marching cubes all afternoon. it looks like you know what you are doing though.
The problem is that when you are using a texture atlas and tiling different textures ins$$anonymous$$d of [a]xx[b]xx[aaa] you'd get [a]xx[b]xx[abc] since we're not tiling just the same part of the atlas texture but the WHOLE atlas. texture atlas is kind of a hack.
Coding this is relatively easy, it's just hard to visualize, especially the first time around since you need to do a lot of stuff you're probably not accustomed to, posted the code snippet just in case.
But more importantly: According to my research there are better ways to represent voxels than using marching cubes. Dual Contouring: http://procworld.blogspot.co.uk/2010/11/from-voxels-to-polygons.html
Your answer
Follow this Question
Related Questions
Color changes at distance in procedural terrain 0 Answers
Texture mapping procedural mesh with texture atlas? (Minecraft style terrain gen) 1 Answer
Huge C# Procedural Generation Errors. Why? 1 Answer
[Optimization] How can i optimize this voxel terrain more? 3 Answers
Creating caves in voxel procedural mesh 0 Answers