Search Unity

Procedural UV maps (some help needed)

Discussion in 'Scripting' started by GeroldS, Mar 1, 2008.

  1. GeroldS

    GeroldS

    Joined:
    Dec 30, 2007
    Posts:
    34
    Maybe someone can help me to get a grip on "mesh.uv", i seem to be lost somewhere in my procedural meshing between array index 16732 and 16733 ;-)

    Here is what i want to do:

    I got a texture with 4 different small tile textures in it. 2x2 squares. one each for sand, grass, water, rock.

    I also got a 100x100 plane mesh (procedurally generated and later transformed).

    I now want to place the small tiles from the texture "randomly" onto the plane.

    I guess i have to somehow make custom UVs that map a segment of a texture to faces. I just cant figure out how it works. I hope someone here can give me (or direct me to) a primer on how UV maps are actually built using "mesh.uv". Can't figure it out from whats written in the documentation.

    --
    o/ Pray for help.
     
  2. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    UV maps go from 0.0 to 1.0. Any values above or below that just repeat the 0..1 range (so e.g. 1..2 is the same as 0..1). Let's assume your texture is set up like this:

    Code (csharp):
    1.   0        1      
    2. 1 Sand Grass
    3.   Sand Grass
    4.   Sand Grass
    5.   Water Rock
    6.   Water Rock
    7. 0 Water Rock
    A square with sand would therefore have four vertices, with the UV values for the respective vertices being

    Code (csharp):
    1. (0.0, 1.0) (0.5, 1.0)
    2. (0.0, 0.5) (0.5, 0.5)
    A square with rock would be

    Code (csharp):
    1. (0.5, 0.5) (1.0, 0.5)
    2. (0.5, 0.0) (1.0, 0.0)
    Note that all vertices are going to have to be split because of UV seams in this case. That is, each square needs four vertices; it's not going to be feasible to have vertices shared between neighboring squares. In the best case, a 100x100 plane would have 10,000 vertices, which would be possible, for example, if you had the texture mapped once over the entire mesh. Here, it's going to be 40,000 vertices. But I guess you know that, since you mentioned array index 16732...watch out for 27138--that one's a doozy. ;)

    Note also that you have to watch out for mipmap problems. Unless you're not using mipmaps at all, you probably want to inset the values a little so there's space between the textures or else you usually get texture bleeding at higher mipmap levels. In other words, use the range of, say, 0.55 through 0.95 instead of 0.5 through 1.0 (that was just for the sake of demonstration above). Yes, this makes seamlessly tiling textures somewhat problematic when you're using texture atlases like this.

    Hope that makes some sense!

    --Eric
     
  3. GeroldS

    GeroldS

    Joined:
    Dec 30, 2007
    Posts:
    34
    Thanks Eric, that helped! Here is what i managed to create so far:



    Code (csharp):
    1. function Start () {
    2.    
    3.     var mesh = new Mesh ();
    4.     GetComponent (MeshFilter).mesh = mesh;
    5.    
    6.     mesh.Clear();
    7.     var vert = new Array();
    8.     var tria = new Array();
    9.     var uvs = new Array();
    10.     var tiles = new Array();
    11.     var u : float = 0.25;
    12.     var v : float = 0.25;
    13.  
    14.     var fields : int = 10;
    15.     var scale : float = 1;
    16.    
    17.     // lets make a few maps
    18.        
    19.     // elevation / tile positions
    20.     // makes an array that stores the cathesian coordinates of each tile
    21.     for (x = 0; x < fields; x++) {
    22.         for (z = 0; z < fields; z++) {
    23.             tileIndex = x * fields + z;
    24.             y = Random.value;
    25.             tiles[tileIndex] = Vector3(x,y,z);
    26.         }
    27.     }
    28.     Debug.Log("Tiles initialized: " + tiles.length);
    29.    
    30.     // vertices
    31.     // generate 4 vertices for each tile
    32.     // vertices are not shared between tiles, each has his own 4 vertices
    33.     // the vertices of any tile-index can be found at indices:
    34.     // 0,0 = tileIndex * 4
    35.     // 1,0 = tileIndex * 4 + 1
    36.     // 0,1 = tileIndex * 4 + 2
    37.     // 1,1 = tileIndex * 4 + 3
    38.     vert.length = tiles.length * 4;
    39.     i = 0;
    40.     for (tile in tiles) {
    41.         vertIndex = i * 4; // vertIndex = tileIndex * 4
    42.         vert[vertIndex    ] = Vector3(tile.x+0, tile.y, tile.z+0) * scale;
    43.         vert[vertIndex + 1] = Vector3(tile.x+1, tile.y, tile.z+0) * scale;
    44.         vert[vertIndex + 2] = Vector3(tile.x+0, tile.y, tile.z+1) * scale;
    45.         vert[vertIndex + 3] = Vector3(tile.x+1, tile.y, tile.z+1) * scale;
    46.         i++;
    47.     }
    48.    
    49.     // triangles
    50.     // generates 2 triangles for each tile to make a quad
    51.     tria.length = tiles.length * 6;
    52.     i = 0;
    53.     for (tile in tiles) {
    54.         triaIndex = i * 6; // triaIndex = tileIndex * 6
    55.         vertIndex = i * 4;
    56.         tria[triaIndex    ] = vertIndex + 3; // 1,1
    57.         tria[triaIndex + 1] = vertIndex + 1; // 1,0
    58.         tria[triaIndex + 2] = vertIndex + 2; // 0,1
    59.         tria[triaIndex + 3] = vertIndex + 2; // 0,1
    60.         tria[triaIndex + 4] = vertIndex + 1; // 1,0
    61.         tria[triaIndex + 5] = vertIndex + 0; // 0,0
    62.         i++;
    63.     }
    64.  
    65.     // assign the vertices and triangles to the mesh now.
    66.     mesh.vertices = vert;
    67.     mesh.triangles = tria;
    68.     Debug.Log("Vertices initialized: " + tria.length);
    69.     Debug.Log("Triangles initialized: " + vert.length);
    70.    
    71.     // initialize the normals
    72.     mesh.RecalculateNormals();
    73.     var norm = mesh.normals;
    74.     for (i=0;i<norm.Length;i++) {
    75.         norm[i] = Vector3.up;
    76.     }
    77.     mesh.normals = norm;
    78.     Debug.Log("Normals initialized: " + norm.Length);
    79.    
    80.     // UV mapping
    81.     uvs.length = vert.length; // there will be as many UVs as there are vertices
    82.     // we have to map each vertix to a location on the texture. 0,0 for the lower
    83.     // left, 1,1 for the top right position on the texture. Or any value in between
    84.     // lets iterate through the tiles once more
    85.     i = 0;
    86.     for (tile in tiles) {
    87.         // assign a tile depending on the tile's elevation
    88.         // (tile.y which was initialized randomly in the beginning)
    89.         if (tile.y >= 0) { // water
    90.             u = 0.5;
    91.             v = 0.5;
    92.         }
    93.         if (tile.y > 0.5) { // sand
    94.             u = 0.5;
    95.             v = 0;
    96.         }
    97.         if (tile.y > 0.8) { // grass
    98.             u = 0;
    99.             v = 0.5;
    100.         }
    101.         if (tile.y > 0.9) { // rock
    102.             u = 0;
    103.             v = 0;
    104.         }
    105.         uvIndex = i * 4;
    106.         uvs[uvIndex + 0] = Vector2(u,       v);
    107.         uvs[uvIndex + 1] = Vector2(u+0.5,   v);
    108.         uvs[uvIndex + 2] = Vector2(u,       v+0.5);
    109.         uvs[uvIndex + 3] = Vector2(u+0.5,   v+0.5);
    110.         i++;
    111.     }
    112.  
    113.     mesh.uv = uvs;
    114. }