Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

[WIP] Procedural World Generator Idea (Project in motion)

Discussion in 'Scripting' started by ob103ninja, Sep 2, 2016.

  1. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    Been working on a certain idea for a very long time, and it's about time I started working on the actual code. Problem is, I barely know where to start.

    Basically, I came up with a form of voxel terrain that is much more smooth than just blocks, but still treats areas as cubes (called Cubits in this example). The individual cubits can take one of 77 different 8-vertices-maximum shapes and their possible rotations, shown below -
    (excuse my mistake of putting 20 instead of 24)

    After the terrain generates, they first smooth out based on the height map and terrain carvers such as passes, cliffs, canyons and mountain ranges, using the different shapes, and then they smooth out even further via vertice displacement by one-tenth a cubit.

    Smooth shading would be applied to the initially generated terrain, and hard shading would be applied to manually placed objects and dug-out terrain. The initial terrain would generate a single mesh all across the surface, and the manually placed/dug terrain would be individual meshes held by the cubit objects.

    An array handling the data of each and every cubit in the world would look something like this -
    int _WorldArray [
    xWidth,
    yHeight,
    zLength,
    cubitType, //amount of terrain materials
    shape // maximum 77
    ]
    Again, what I'm looking for is methods of generating all 77 of those cube shapes, methods of generating the actual terrain appearance, and etc.

    Got any tutorials, code snippets or anything that could help?

    In fact, if you can build the whole thing yourself, I'll be forever grateful. I would even hire you if I ended up making a game production business.
     
  2. kru

    kru

    Joined:
    Jan 19, 2013
    Posts:
    452
    What you're describing is known as the Marching Cubes algorithm. The patent on it expired a few years ago, so there should be plenty of source code examples floating around the googles.
     
  3. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,840
    I actually have built that. He game didn't take off, but the tech worked fine. (See here and also these.)

    You can certainly hire me to resurrect it for you... I'd love to dust that code off and do something with it! (PM me if you're serious.)
     
  4. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    For the initial terrain, yes. However, I'm more concerned about the actual voxel area being generated. I am a partial noob with Unity, so I doubt I'd be able to implement a marching cube script (nevermind MAKING one), so at the moment I'm pretty certain I might have someone do it for me.

    Thinking about asking you, seems like you got the idea behind it :D Though I'm only seeing cubes. Do you think you'd be able to mash it up with a marching cubes algorithm and get it to work? My goal is to have a nearly realistic terrain generator to fit a certain artstyle I'm aiming for (realworld background/toonshader characters, like in "Amazing world of Gumball").
     
  5. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,840
    Look more carefully — at the roof and the chimney, for example. We also had lots of other shapes (corner slopes etc.) that popped up a lot in the terrain, though apparently didn't capture any good examples of that in the screenshots.

    Incidentally, I don't agree that Marching Cubes is really the relevant algorithm here (though I have used that in the past for something completely different). What you have described (and what we implemented) is really just a voxel engine, with more voxel shapes than simple cubes, enabling a less blocky terrain.
     
  6. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Found this as well, may help!

    http://forum.unity3d.com/threads/tutorial-procedural-meshes-and-voxel-terrain-c.198651/

    also this one:

    http://answers.unity3d.com/questions/703018/marching-cubes-terrain-deformation-problem.html

    and another:

    https://www.lynda.com/Unity-3D-tutorials/Advanced-Unity-3D-Game-Programming/160270-2.html

    Coincidentally, the whole voxel terrain thing was done by Nova Logic on the Comanche series back in the day, I also believe Shattered Steel and...one other game I used to play heavy back in the 90's as well....yup, I'm old.

    https://github.com/keijiro/unity-isosurface-test

    Found another one
     
    Last edited: Sep 3, 2016
    ob103ninja and JoeStrout like this.
  7. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    Thanks Joe and Brad! I'm keeping this thread pinned to my desktop as a shortcut. I'll use it as a reference.
     
    JoeStrout likes this.
  8. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    That tutorial in that first link is actually quite easy to understand. So far I have this -
     
    JoeStrout likes this.
  9. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    Some more progress -

     
    JoeStrout likes this.
  10. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    So this is a problem.



    Don't know what made it start doing that. I set all the cubits to stone (cubits[x,y,z]=2) to view it better.
    I'll drop the C# code here so you can help me figure out what the heck happened.

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class Worldgen : MonoBehaviour {
    7.  
    8.     public List<Vector3> newVertices = new List<Vector3> ();
    9.     public List<int> newTriangles = new List<int> ();
    10.     public List<Vector2> newUV = new List<Vector2> ();
    11.  
    12.     public int worldsize = 8;   //Width&Length of the world in cubits
    13.     public int worldheight = 8; //Height of the world in cubits
    14.     public bool update = false; //Update function. Called after any data changes.
    15.     public int[,,] cubits;      //Holds material data for the individual cubits
    16.     public int[,,] cubitShape;  //Holds shape data for the individual cubits
    17.  
    18.     //public int[][] sequence = new int[][] {}; Will eventually replace the already working sequence code in SetPoints()
    19.  
    20.     public int[][] exposers = new int[][] {//Holds data regarding what faces might expose adjacent cubits. For mesh reduction.
    21.         //-Sequence is Front, Left, Back, Right, Top, Bottom-
    22.         //Cube
    23.         new int[] { 0, 0, 0, 0, 0, 0 },
    24.         //Slopes
    25.         new int[] { 0, 1, 0, 0, 1, 0 },
    26.         new int[] { 1, 0, 0, 0, 1, 0 },
    27.         new int[] { 0, 0, 0, 1, 1, 0 },
    28.         new int[] { 0, 0, 1, 0, 1, 0 },
    29.         new int[] { 1, 1, 0, 0, 0, 0 },
    30.         new int[] { 1, 0, 0, 1, 0, 0 },
    31.         new int[] { 0, 0, 1, 1, 0, 0 },
    32.         new int[] { 0, 1, 1, 0, 0, 0 },
    33.         new int[] { 0, 1, 0, 0, 0, 1 },
    34.         new int[] { 1, 0, 0, 0, 0, 1 },
    35.         new int[] { 0, 0, 0, 1, 0, 1 },
    36.         new int[] { 0, 0, 1, 0, 0, 1 },
    37.         //Heptas
    38.         new int[] { 1, 1, 0, 0, 1, 0 },
    39.         new int[] { 1, 0, 0, 1, 1, 0 },
    40.         new int[] { 0, 0, 1, 1, 1, 0 },
    41.         new int[] { 0, 1, 1, 0, 1, 0 },
    42.         new int[] { 1, 1, 0, 0, 0, 1 },
    43.         new int[] { 1, 0, 0, 1, 0, 1 },
    44.         new int[] { 0, 0, 1, 1, 0, 1 },
    45.         new int[] { 0, 1, 1, 0, 0, 1 },
    46.         //Tetras
    47.         new int[] { 1, 1, 1, 1, 1, 1 },
    48.         new int[] { 1, 1, 1, 1, 1, 1 },
    49.         new int[] { 1, 1, 1, 1, 1, 1 },
    50.         new int[] { 1, 1, 1, 1, 1, 1 },
    51.         new int[] { 1, 1, 1, 1, 1, 1 },
    52.         new int[] { 1, 1, 1, 1, 1, 1 },
    53.         new int[] { 1, 1, 1, 1, 1, 1 },
    54.         new int[] { 1, 1, 1, 1, 1, 1 }
    55.     };
    56.  
    57.     private int cb;  //Cubit counter. Important for adding to the Triangles list.
    58.     //private int[] drawface = new int[6]; Future array to be used to reduce certain triangles.
    59.     private float texSize = 0.5f; //Ratio to 1 to divide the texture space. 0.5 = 4 textures, 0.25 = 16 textures, etc
    60.     private Vector2[] matID = new Vector2[] { //Holds data for the texture positions
    61.         new Vector2 (0, 0),
    62.         new Vector2 (0, 1),
    63.         new Vector2 (1, 0),
    64.         new Vector2 (1, 1)
    65.     };
    66.  
    67.     private Mesh mesh;
    68.     private MeshCollider col;
    69.  
    70.     void Start () {
    71.         //Initializes certain variables.
    72.         cb = 0;
    73.         newVertices.Clear();
    74.         newTriangles.Clear();
    75.         newUV.Clear();
    76.         mesh = GetComponent<MeshFilter> ().mesh;
    77.         col = GetComponent<MeshCollider> ();
    78.         /*float x = transform.position.x;
    79.         float y = transform.position.y;
    80.         float z = transform.position.z;*/
    81.         //Placeholder. Later on, these next 3 lines would be put in a method that would be called only when the world first generates.
    82.         CreateTerrain(worldsize, worldheight);
    83.         BuildTerrain ();
    84.         UpdateMesh ();
    85.     }
    86.  
    87.     void Update() {
    88.         // Using a bool was probably not the best method, might switch out with a method later.
    89.         // Updates the mesh when one or more cubits are changed by, say, the player.
    90.         if (update) {
    91.             BuildTerrain ();
    92.             UpdateMesh ();
    93.             update = false;
    94.         }
    95.     }
    96.  
    97.     void MakeMesh(int x, int y, int z, int mat, int shape, int xOffs, int zOffs){
    98.         // Adds a cubit-size section of the mesh to the entirety.
    99.         SetPoints (xOffs, y, zOffs, shape);
    100.         SetUVMap (mat);
    101.         CubeTriangles (x, y, z);
    102.         cb += 24;
    103.     }
    104.  
    105.     void UpdateMesh(){
    106.         //Clears the mesh from last iteration
    107.         mesh.Clear ();
    108.         //Applies cubits to mesh
    109.         mesh.vertices = newVertices.ToArray();
    110.         mesh.triangles = newTriangles.ToArray();
    111.         mesh.uv = newUV.ToArray ();
    112.         mesh.Optimize ();
    113.         mesh.RecalculateNormals ();
    114.         col.sharedMesh = mesh;
    115.         //Clears temporary lists and variables
    116.         cb = 0;
    117.         newVertices.Clear();
    118.         newTriangles.Clear();
    119.         newUV.Clear();
    120.     }
    121.  
    122.     void CreateTerrain(int ws, int wh) {
    123.         //Initial terrain generation.
    124.         cubits = new int[ws+2, wh+2, ws+2]; //The size is incremented by one in each direction
    125.         cubitShape = new int[ws+2, wh+2, ws+2]; //to give a spacer for the cubit arrays.
    126.         int clx = cubits.GetLength (0) - 1;
    127.         int clxb = cubits.GetLowerBound (0);
    128.         int cly = cubits.GetLength (1) - 1;
    129.         int clyb = cubits.GetLowerBound (1);
    130.         int clz = cubits.GetLength (2) - 1;
    131.         int clzb = cubits.GetLowerBound (2);
    132.  
    133.         for (int vx = 0; vx < cubits.GetLength (0); vx++) {
    134.             for (int vy = 0; vy < cubits.GetLength (1); vy++) {
    135.                 for (int vz = 0; vz < cubits.GetLength (2); vz++)
    136.                 {
    137.                     //Terrain formation algorithm (Sets unused edge-spacer cubits as well)
    138.                     if (vy <= 8) {
    139.                         cubits [vx, vy, vz] = 2;//Placeholder
    140.                     }
    141.                     //Pseudo Marching Cubes
    142.                     if (AdjacentsInBounds(vx,vy,vz,clxb,clyb,clzb,clx,cly,clz))
    143.                     {
    144.                         MarchTerrain (vx, vy, vz);
    145.                     }
    146.                 }
    147.             }
    148.         }
    149.     }
    150.  
    151.     void MarchTerrain(int x, int y, int z) {
    152.         cubitShape [x, y, z] = 0; // Placeholder shape. Terrain smoothing algorithm not yet implemented.
    153.     }
    154.  
    155.     void BuildTerrain() {
    156.         //
    157.         int clx = cubits.GetLength (0);
    158.         int clxb = cubits.GetLowerBound (0);
    159.         int cly = cubits.GetLength (1);
    160.         int clyb = cubits.GetLowerBound (1);
    161.         int clz = cubits.GetLength (2);
    162.         int clzb = cubits.GetLowerBound (2);
    163.         int xKey = (int)clx - (clx / 2);
    164.         int zKey = (int)clz - (clz / 2);
    165.  
    166.         for (int vx = 0; vx < clx; vx++) { //Width
    167.             for (int vz = 0; vz < clz; vz++) { //Length
    168.                 for (int vy = 0; vy < cly; vy++) { //Height
    169.                     if (cubits [vx, vy, vz] != 0) { //If not empty
    170.                         if (AdjacentsInBounds(vx,vy,vz,clxb,clyb,clzb,clx-1,cly-1,clz-1))
    171.                         {
    172.                             MakeMesh (
    173.                                 vx, vy, vz, //Mesh position
    174.                                 cubits [vx, vy, vz] - 1, //Material. Subtracted by one on purpose.
    175.                                 cubitShape [vx, vy, vz], //Shape
    176.                                 vx - xKey, vz - zKey //Offset horizontals
    177.                             );
    178.                         }
    179.                     }
    180.                 }
    181.             }
    182.         }
    183.     }
    184.  
    185.     bool AdjacentsInBounds(int x, int y, int z, int x2, int y2, int z2, int x3, int y3, int z3){
    186.         //Makes sure the cubit isn't an edge spacer cubit
    187.         if (x > x2 && x < x3 &&
    188.             y > y2 && y < y3 &&
    189.             z > z2 && z < z3) {
    190.             return true;
    191.         } else {
    192.             return false;
    193.         }
    194.     }
    195.  
    196.     void SetPoints (float xa, float ya, float za, int shape){
    197.         Vector3[] pointID = new Vector3[8];
    198.         int[] sequence = new int[8];
    199.         float xb = xa + 1;
    200.         float yb = ya - 1;
    201.         float zb = za + 1;
    202.         Vector3[] pBase = new Vector3[8] { //Base vertice positions for the shapes
    203.             new Vector3 (xa, ya, za), //Vertice [0]
    204.             new Vector3 (xb, ya, za), //Vertice [1]
    205.             new Vector3 (xb, yb, za), //Vertice [2]
    206.             new Vector3 (xa, yb, za), //Vertice [3]
    207.             new Vector3 (xa, ya, zb), //Vertice [4]
    208.             new Vector3 (xb, ya, zb), //Vertice [5]
    209.             new Vector3 (xb, yb, zb), //Vertice [6]
    210.             new Vector3 (xa, yb, zb)  //Vertice [7]
    211.         };
    212.         //Converts cubes to other solids - (#) = how many of each, [#] = vertices moved for each
    213.         if (shape == 0) { //Cube (1) [0]
    214.             sequence = new int[]{ 0, 1, 2, 3, 4, 5, 6, 7 };
    215.         } else if (shape > 0 && shape < 13) {
    216.             switch (shape) { //Slopes (12) [2]
    217.             case 1:
    218.                 sequence = new int[]{ 3, 1, 2, 3, 7, 5, 6, 7 }; //topleft slope
    219.                 break;
    220.             case 2:
    221.                 sequence = new int[]{ 3, 2, 2, 3, 4, 5, 6, 7 }; //topfront slope
    222.                 break;
    223.             case 3:
    224.                 sequence = new int[]{ 0, 2, 2, 3, 4, 6, 6, 7 }; //topright slope
    225.                 break;
    226.             case 4:
    227.                 sequence = new int[]{ 0, 1, 2, 3, 7, 6, 6, 7 }; //topback slope
    228.                 break;
    229.             case 5:
    230.                 sequence = new int[]{ 4, 1, 2, 7, 4, 5, 6, 7 }; //leftfront slope
    231.                 break;
    232.             case 6:
    233.                 sequence = new int[]{ 0, 0, 3, 3, 4, 5, 6, 7 }; //frontright slope
    234.                 break;
    235.             case 7:
    236.                 sequence = new int[]{ 0, 1, 2, 3, 4, 4, 7, 7 }; //rightback slope
    237.                 break;
    238.             case 8:
    239.                 sequence = new int[]{ 0, 1, 2, 3, 0, 5, 6, 3 }; //backleft slope
    240.                 break;
    241.             case 9:
    242.                 sequence = new int[]{ 0, 1, 2, 0, 4, 5, 6, 4 }; //bottomleft slope
    243.                 break;
    244.             case 10:
    245.                 sequence = new int[]{ 0, 1, 1, 0, 4, 5, 6, 7 }; //bottomfront slope
    246.                 break;
    247.             case 11:
    248.                 sequence = new int[]{ 0, 1, 1, 3, 4, 5, 5, 7 }; //bottomright slope
    249.                 break;
    250.             case 12:
    251.                 sequence = new int[]{ 0, 1, 2, 3, 4, 5, 5, 4 }; //bottomback slope
    252.                 break;
    253.             }
    254.         } else if (shape > 13 && shape < 21) {
    255.             switch (shape) { //Heptas (One corner flattened, corner is on the right of the face) (8) [1]
    256.             case 13:
    257.                 sequence = new int[]{ 4, 1, 2, 3, 4, 5, 6, 7 }; //topleft hepta
    258.                 break;
    259.             case 14:
    260.                 sequence = new int[]{ 0, 0, 2, 3, 4, 5, 6, 7 }; //topfront hepta
    261.                 break;
    262.             case 15:
    263.                 sequence = new int[]{ 0, 1, 2, 3, 4, 1, 6, 7 }; //topright hepta
    264.                 break;
    265.             case 16:
    266.                 sequence = new int[]{ 0, 1, 2, 3, 5, 5, 6, 7 }; //topback hepta
    267.                 break;
    268.             case 17:
    269.                 sequence = new int[]{ 0, 1, 2, 2, 4, 5, 6, 7 }; //bottomleft hepta
    270.                 break;
    271.             case 18:
    272.                 sequence = new int[]{ 0, 1, 6, 3, 4, 5, 6, 7 }; //bottomfront hepta
    273.                 break;
    274.             case 19:
    275.                 sequence = new int[]{ 0, 1, 2, 3, 4, 5, 7, 7 }; //bottomright hepta
    276.                 break;
    277.             case 20:
    278.                 sequence = new int[]{ 0, 1, 2, 3, 4, 5, 6, 3 }; //bottomback hepta
    279.                 break;
    280.             }
    281.         } else if (shape > 20 && shape < 29) {
    282.             switch (shape) { //Tetras (Corner on right of face) (8) [4]
    283.             case 21:
    284.                 sequence = new int[]{ 2, 2, 2, 2, 7, 5, 6, 7 }; //bottomright tetra
    285.                 break;
    286.             case 22:
    287.                 sequence = new int[]{ 3, 6, 6, 3, 4, 6, 6, 7 }; //bottomback tetra
    288.                 break;
    289.             case 23:
    290.                 sequence = new int[]{ 0, 2, 2, 3, 7, 7, 7, 7 }; //bottomleft tetra
    291.                 break;
    292.             case 24:
    293.                 sequence = new int[]{ 3, 1, 2, 3, 3, 6, 6, 3 }; //bottomfront tetra
    294.                 break;
    295.             case 25:
    296.                 sequence = new int[]{ 4, 1, 1, 4, 4, 5, 6, 4 }; //topright tetra
    297.                 break;
    298.             case 26:
    299.                 sequence = new int[]{ 0, 0, 0, 0, 4, 5, 5, 7 }; //topback tetra
    300.                 break;
    301.             case 27:
    302.                 sequence = new int[]{ 0, 1, 1, 3, 4, 1, 1, 4 }; //topleft tetra
    303.                 break;
    304.             case 28:
    305.                 sequence = new int[]{ 0, 1, 2, 0, 5, 5, 5, 5 }; //topfront tetra
    306.                 break;
    307.             }
    308.         }
    309.         /*
    310.             (1)cube[0]         Finished
    311.             (8)heptas[1]    Finished
    312.             (24)notches[1]     Unimplemented, currently not needed
    313.             (4)octas[2]     Unimplemented, currently not needed
    314.             (12)slopes[2]     Finished
    315.             (24)pentas[3]     Unimplemented, currently not needed
    316.             (8)tetras[4]     Finished
    317.         */
    318.         int t = 0;
    319.         pointID = new Vector3[8];
    320.         foreach (int s in sequence){
    321.             pointID[t] = pBase[s];
    322.             t++;
    323.         }
    324.         t = 0;
    325.  
    326.         foreach (int s in new int[] { //Vertice mapping sequence
    327.             0,1,3,1,2,3, //front
    328.             4,0,7,0,3,7, //left
    329.             5,4,6,4,7,6, //back
    330.             1,5,2,5,6,2, //right
    331.             4,5,0,5,1,0, //top
    332.             3,2,7,2,6,7  //bottom
    333.         })
    334.         {
    335.             newVertices.Add (pointID [s]);
    336.         }
    337.     }
    338.  
    339.     void SetUVMap(int mat = 0){
    340.         Vector2[] texPos = new Vector2[4] {
    341.             new Vector2 (texSize * matID [mat].x,           texSize * matID [mat].y + texSize),
    342.             new Vector2 (texSize * matID [mat].x + texSize, texSize * matID [mat].y + texSize),
    343.             new Vector2 (texSize * matID [mat].x + texSize, texSize * matID [mat].y),
    344.             new Vector2 (texSize * matID [mat].x,           texSize * matID [mat].y)
    345.         };
    346.         for (int i = 0; i < 6; i++) {
    347.             foreach (int v in new int[] { 0, 1, 3, 1, 2, 3 }) {
    348.                 newUV.Add (texPos [v]);
    349.             }
    350.         }
    351.     }
    352.  
    353.     void CubeTriangles(int x, int y, int z){
    354.         for (int u = 0; u < 6; u++) {
    355.             for (int v = 0; v < 6; v++) {
    356.                 //if (FaceExposed (x, y, z, u)) {
    357.                     newTriangles.Add ((u * 6) + v + cb);
    358.                 //}
    359.             }
    360.         }
    361.     }
    362.  
    363.     bool FaceExposed(int x, int y, int z, int faces){ //Checks to see which faces are not completely covered by another.
    364.         /*##*/ if (faces < 1 && exposers [cubitShape [x, y, z]] [0] == 0) {
    365.             if (exposers [cubitShape [x, y, z - 1]] [2] == 1 || cubits [x, y, z - 1] == 0) {//Front
    366.                 return true;
    367.             }
    368.         } else if (faces < 2 && exposers [cubitShape [x, y, z]] [1] == 0) {
    369.             if (exposers [cubitShape [x - 1, y, z]] [3] == 1 || cubits [x - 1, y, z] == 0) {//Left
    370.                 return true;
    371.             }
    372.         } else if (faces < 3 && exposers [cubitShape [x, y, z]] [2] == 0) {
    373.             if (exposers [cubitShape [x, y, z + 1]] [0] == 1 || cubits [x, y, z + 1] == 0) {//Back
    374.                 return true;
    375.             }
    376.         } else if (faces < 4 && exposers [cubitShape [x, y, z]] [3] == 0) {
    377.             if (exposers [cubitShape [x + 1, y, z]] [1] == 1 || cubits [x + 1, y, z] == 0) {//Right
    378.                 return true;
    379.             }
    380.         } else if (faces < 5 && exposers [cubitShape [x, y, z]] [4] == 0) {
    381.             if (exposers [cubitShape [x, y + 1, z]] [5] == 1 || cubits [x, y + 1, z] == 0) {//Top
    382.                 return true;
    383.             }
    384.         } else if (faces < 6 && exposers [cubitShape [x, y, z]] [5] == 0) {
    385.             if (exposers [cubitShape [x, y - 1, z]] [4] == 1 || cubits [x, y - 1, z] == 0) {//Bottom
    386.                 return true;
    387.             }
    388.         }
    389.         return false;
    390.     }
    391. }

    Edit - I made this to use as a helper from time to time. It's for the individual cubits, in regard to the vertice positions and stuff. The numbers outside of the square brackets might be obsolete as there is 36 verices each cubit. Makes it easier to manage the UVs and smoothing.
     
    Last edited: Sep 13, 2016
  11. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    AHA! After forever of looking around, I realized that cb was being increased by 24 instead of 36 each time. That explains the ratio at which cubes were missing.

     
    Last edited: Sep 13, 2016
  12. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    I keep running into issues.

    After implementing chunk generation, I ended up with errors, each of which I fixed up until this point.
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. /*
    5. Worldgen units of length measurement -
    6.   FingerSpan (fs) = 0.1 Unity unit     - Fine tune smoothing
    7.   Cubit (cb) = 1 Unity unit, or 10 fs  - Individual voxel units
    8.   Ground (gr) = 8 cb                   - Chunks
    9.   Domain (dm) = 8 gr                   - Not really a unit, just used as reference
    10. */
    11. public class Worldgen : MonoBehaviour {
    12.  
    13.     public int worldsize = 8;       //Width&Length of the world in grounds
    14.     public int worldheight = 8;     //Height of the world in grounds
    15.     public int groundSize = 8;         //Length of the Ground(gr) unit in Cubits(cb)
    16.     public GameObject ground;          //Prefab is set in the inspector
    17.     public GameObject[,,] grounds;     //Object array for the chunks
    18.     public int[,,] cubits;          //Holds material data for the individual cubits
    19.     public int[,,] cubitShape;      //Holds shape data for the individual cubits
    20.     public bool update = false;     //Update function. Called after any data changes.
    21.  
    22.     public int[][] sequence = new int[][] {
    23.         //Converts cubes to other solids. Never changed.
    24.         // (#) = how many of each, [#] = vertices moved for each
    25.         new int[]{ 0, 1, 2, 3, 4, 5, 6, 7 }, //cube
    26.         //Slopes (12) [2]
    27.         new int[]{ 3, 1, 2, 3, 7, 5, 6, 7 }, //topleft slope
    28.         new int[]{ 3, 2, 2, 3, 4, 5, 6, 7 }, //topfront slope
    29.         new int[]{ 0, 2, 2, 3, 4, 6, 6, 7 }, //topright slope
    30.         new int[]{ 0, 1, 2, 3, 7, 6, 6, 7 }, //topback slope
    31.         new int[]{ 4, 1, 2, 7, 4, 5, 6, 7 }, //leftfront slope
    32.         new int[]{ 0, 0, 3, 3, 4, 5, 6, 7 }, //frontright slope
    33.         new int[]{ 0, 1, 2, 3, 4, 4, 7, 7 }, //rightback slope
    34.         new int[]{ 0, 1, 2, 3, 0, 5, 6, 3 }, //backleft slope
    35.         new int[]{ 0, 1, 2, 0, 4, 5, 6, 4 }, //bottomleft slope
    36.         new int[]{ 0, 1, 1, 0, 4, 5, 6, 7 }, //bottomfront slope
    37.         new int[]{ 0, 1, 1, 3, 4, 5, 5, 7 }, //bottomright slope
    38.         new int[]{ 0, 1, 2, 3, 4, 5, 5, 4 }, //bottomback slope
    39.         //Heptas (One corner flattened, corner is on the right of the face) (8) [1]
    40.         new int[]{ 4, 1, 2, 3, 4, 5, 6, 7 }, //topleft hepta
    41.         new int[]{ 0, 0, 2, 3, 4, 5, 6, 7 }, //topfront hepta
    42.         new int[]{ 0, 1, 2, 3, 4, 1, 6, 7 }, //topright hepta
    43.         new int[]{ 0, 1, 2, 3, 5, 5, 6, 7 }, //topback hepta
    44.         new int[]{ 0, 1, 2, 2, 4, 5, 6, 7 }, //bottomleft hepta
    45.         new int[]{ 0, 1, 6, 3, 4, 5, 6, 7 }, //bottomfront hepta
    46.         new int[]{ 0, 1, 2, 3, 4, 5, 7, 7 }, //bottomright hepta
    47.         new int[]{ 0, 1, 2, 3, 4, 5, 6, 3 }, //bottomback hepta
    48.         //Tetras (Corner on right of face) (8) [4]
    49.         new int[]{ 2, 2, 2, 2, 7, 5, 6, 7 }, //bottomright tetra
    50.         new int[]{ 3, 6, 6, 3, 4, 6, 6, 7 }, //bottomback tetra
    51.         new int[]{ 0, 2, 2, 3, 7, 7, 7, 7 }, //bottomleft tetra
    52.         new int[]{ 3, 1, 2, 3, 3, 6, 6, 3 }, //bottomfront tetra
    53.         new int[]{ 4, 1, 1, 4, 4, 5, 6, 4 }, //topright tetra
    54.         new int[]{ 0, 0, 0, 0, 4, 5, 5, 7 }, //topback tetra
    55.         new int[]{ 0, 1, 1, 3, 4, 1, 1, 4 }, //topleft tetra
    56.         new int[]{ 0, 1, 2, 0, 5, 5, 5, 5 }  //topfront tetra
    57.     };
    58.     /*
    59.             (1)cube[0]         Finished
    60.             (8)heptas[1]    Finished
    61.             (24)notches[1]     Unimplemented, currently not needed
    62.             (4)octas[2]     Unimplemented, currently not needed
    63.             (12)slopes[2]     Finished
    64.             (24)pentas[3]     Unimplemented, currently not needed
    65.             (8)tetras[4]     Finished
    66.     */
    67.     public int[][] exposers = new int[][] {
    68.         //Holds data regarding what faces might expose adjacent cubits. Used for mesh reduction.
    69.         //- Horizontal sequence is Front, Left, Back, Right, Top, Bottom
    70.         //- Vertical sequence is the same as the sequence[][] array
    71.         //Cube
    72.         new int[] { 0, 0, 0, 0, 0, 0 },
    73.         //Slopes
    74.         new int[] { 0, 1, 0, 0, 1, 0 },
    75.         new int[] { 1, 0, 0, 0, 1, 0 },
    76.         new int[] { 0, 0, 0, 1, 1, 0 },
    77.         new int[] { 0, 0, 1, 0, 1, 0 },
    78.         new int[] { 1, 1, 0, 0, 0, 0 },
    79.         new int[] { 1, 0, 0, 1, 0, 0 },
    80.         new int[] { 0, 0, 1, 1, 0, 0 },
    81.         new int[] { 0, 1, 1, 0, 0, 0 },
    82.         new int[] { 0, 1, 0, 0, 0, 1 },
    83.         new int[] { 1, 0, 0, 0, 0, 1 },
    84.         new int[] { 0, 0, 0, 1, 0, 1 },
    85.         new int[] { 0, 0, 1, 0, 0, 1 },
    86.         //Heptas
    87.         new int[] { 1, 1, 0, 0, 1, 0 },
    88.         new int[] { 1, 0, 0, 1, 1, 0 },
    89.         new int[] { 0, 0, 1, 1, 1, 0 },
    90.         new int[] { 0, 1, 1, 0, 1, 0 },
    91.         new int[] { 1, 1, 0, 0, 0, 1 },
    92.         new int[] { 1, 0, 0, 1, 0, 1 },
    93.         new int[] { 0, 0, 1, 1, 0, 1 },
    94.         new int[] { 0, 1, 1, 0, 0, 1 },
    95.         //Tetras
    96.         new int[] { 1, 1, 1, 1, 1, 1 },
    97.         new int[] { 1, 1, 1, 1, 1, 1 },
    98.         new int[] { 1, 1, 1, 1, 1, 1 },
    99.         new int[] { 1, 1, 1, 1, 1, 1 },
    100.         new int[] { 1, 1, 1, 1, 1, 1 },
    101.         new int[] { 1, 1, 1, 1, 1, 1 },
    102.         new int[] { 1, 1, 1, 1, 1, 1 },
    103.         new int[] { 1, 1, 1, 1, 1, 1 }
    104.     };
    105.  
    106.     public List<Vector3> newVertices = new List<Vector3> ();
    107.     public List<int> newTriangles = new List<int> ();
    108.     public List<Vector2> newUV = new List<Vector2> ();
    109.     private Mesh[,,] grMesh;
    110.     private MeshCollider[,,] grCol;
    111.  
    112.     private int cb;  //Cubit counter per ground. Important for adding to the Triangles list.
    113.     private float texSize = 0.5f; //Ratio to 1 to divide the texture space. 0.5 = 4 textures, 0.25 = 16 textures, etc
    114.     private Vector2[] matID = new Vector2[] { //Holds data for the texture positions; works with TexSize
    115.         new Vector2 (0, 0),
    116.         new Vector2 (0, 1),
    117.         new Vector2 (1, 0),
    118.         new Vector2 (1, 1)
    119.     };
    120.      
    121.     void Start () {
    122.         //Initialize terrain
    123.         CreateNewWorld();
    124.     }
    125.  
    126.     void Update() {
    127.         // Using a bool was probably not the best method, might switch out with a method later.
    128.         // Updates the mesh when one or more cubits are changed by, say, the player.
    129.         if (update) {
    130.             UpdateGrounds ();
    131.             update = false;
    132.         }
    133.     }
    134.  
    135.     void CreateNewWorld() {
    136.         CreateTerrain(worldsize, worldheight, groundSize);
    137.         InitGrounds ();
    138.         /*BuildTerrain ();
    139.         UpdateMesh ();*/
    140.         UpdateGrounds ();
    141.     }
    142.  
    143.     void InitGrounds() {
    144.         int xzOffs = worldsize / 2;
    145.         for (int x = 0; x < worldsize; x++) {
    146.             for (int y = 0; y < worldheight; y++) {
    147.                 for (int z = 0; z < worldsize; z++) {
    148.                     grounds[x,y,z] = Instantiate(ground,
    149.                         new Vector3 ((x-xzOffs) * (groundSize-1), y * (groundSize-1), (z-xzOffs) * (groundSize-1)),
    150.                         new Quaternion(0,0,0,0)) as GameObject;
    151.                     grMesh [x, y, z] = grounds [x, y, z].GetComponent<MeshFilter> ().mesh;
    152.                     grCol [x, y, z] = grounds [x, y, z].GetComponent<MeshCollider> ();
    153.                 }
    154.             }
    155.         }
    156.     }
    157.  
    158.     void UpdateGrounds() {
    159.         for (int x = 0; x < worldsize; x++) {
    160.             for (int y = 0; y < worldheight; y++) {
    161.                 for (int z = 0; z < worldsize; z++) {
    162.                     cb = 0;
    163.                     newVertices.Clear ();
    164.                     newTriangles.Clear ();
    165.                     newUV.Clear ();
    166.                     //mesh = grounds [x, y, z].GetComponent<MeshFilter> ().mesh;
    167.                     //col = grounds [x, y, z].GetComponent<MeshCollider> ();
    168.                     BuildTerrain (x, y, z);
    169.                     if (newTriangles.Count == 0) {
    170.                         cb = 0;
    171.                         newVertices.Clear ();
    172.                         newTriangles.Clear ();
    173.                         newUV.Clear ();
    174.                     } else {
    175.                         UpdateObjectMesh (x, y, z);
    176.                     }
    177.                 }
    178.             }
    179.         }
    180.     }
    181.  
    182.     void MakeMesh(int x, int y, int z, int mat, int shape, int xOffs, int zOffs){
    183.         // Adds a cubit-size section of the mesh to the entirety.
    184.         SetPoints (xOffs, y, zOffs, shape);
    185.         SetUVMap (mat);
    186.         CubeTriangles (x, y, z);
    187.         cb++;
    188.     }
    189.  
    190.     void UpdateObjectMesh(int x, int y, int z){
    191.         //Clears the mesh from last iteration
    192.         grMesh [x, y, z].Clear ();
    193.         //Applies cubits to mesh
    194.         grMesh [x, y, z].vertices = newVertices.ToArray();
    195.         grMesh [x, y, z].triangles = newTriangles.ToArray ();
    196.         grMesh [x, y, z].uv = newUV.ToArray ();
    197.         grMesh [x, y, z].Optimize ();
    198.         grMesh [x, y, z].RecalculateNormals ();
    199.         grCol [x, y, z].sharedMesh = grMesh [x, y, z];
    200.         //Clears temporary lists and variables
    201.         cb = 0;
    202.         newVertices.Clear();
    203.         newTriangles.Clear();
    204.         newUV.Clear();
    205.     }
    206.  
    207.     void CreateTerrain(int ws, int wh, int gs) {
    208.         //Initial terrain generation.
    209.         cubits = new int[(ws*gs)+2, (wh*gs)+2, (ws*gs)+2]; //The size is incremented by one in each direction
    210.         cubitShape = new int[(ws*gs)+2, (wh*gs)+2, (ws*gs)+2]; //to give a spacer for the cubit arrays.
    211.  
    212.         grounds = new GameObject[ws, wh, ws];
    213.         grMesh = new Mesh[ws, wh, ws];
    214.         grCol = new MeshCollider[ws, wh, ws];
    215.  
    216.         int clx = cubits.GetLength (0) - 1;
    217.         int clxb = cubits.GetLowerBound (0);
    218.         int cly = cubits.GetLength (1) - 1;
    219.         int clyb = cubits.GetLowerBound (1);
    220.         int clz = cubits.GetLength (2) - 1;
    221.         int clzb = cubits.GetLowerBound (2);
    222.  
    223.         for (int vx = 0; vx < cubits.GetLength (0); vx++) {
    224.             for (int vy = 0; vy < cubits.GetLength (1); vy++) {
    225.                 for (int vz = 0; vz < cubits.GetLength (2); vz++)
    226.                 {
    227.                     //Terrain formation algorithm (Sets unused edge-spacer cubits as well)
    228.                     if (vy <= 2+(vz/4)) { //Placeholder
    229.                         cubits [vx, vy, vz] = 2;
    230.                     }
    231.                 }
    232.             }
    233.         }
    234.         for (int vx = 0; vx < cubits.GetLength (0); vx++) {
    235.             for (int vy = 0; vy < cubits.GetLength (1); vy++) {
    236.                 for (int vz = 0; vz < cubits.GetLength (2); vz++)
    237.                 {
    238.                     //Pseudo Marching Cubes
    239.                     if (InBounds (vx, vy, vz, clxb, clyb, clzb, clx, cly, clz) && cubits [vx, vy, vz] != 0) {
    240.                         if (NotSurrounded (vx, vy, vz)) {
    241.                             MarchTerrain (vx, vy, vz);
    242.                         }
    243.                     }
    244.                 }
    245.             }
    246.         }
    247.     }
    248.  
    249.     void BuildTerrain(int x = 0, int y = 0, int z = 0) {
    250.         //Forms the individual meshes.
    251.         int clx = cubits.GetLength (0) - 1;
    252.         int clxb = cubits.GetLowerBound (0);
    253.         int cly = cubits.GetLength (1) - 1;
    254.         int clyb = cubits.GetLowerBound (1);
    255.         int clz = cubits.GetLength (2) - 1;
    256.         int clzb = cubits.GetLowerBound (2);
    257.         //int xKey = (int)clx - (clx / 2);
    258.         //int zKey = (int)clz - (clz / 2);
    259.  
    260.         for (int vx = 0; vx < groundSize; vx++) {             //Width
    261.             for (int vy = 0; vy < groundSize; vy++) {         //Length
    262.                 for (int vz = 0; vz < groundSize; vz++) {     //Height
    263.                     if (cubits [vx, vy, vz] != 0) {         //If cubit specified is not empty
    264.                         if (InBounds (vx + x, vy + y, vz + z, clxb, clyb, clzb, clx, cly, clz)) {
    265.                             MakeMesh (
    266.                                 vx + x, vy + y, vz + z,                 //Mesh position
    267.                                 cubits [vx + x, vy + y, vz + z] - 1,     //Material
    268.                                 cubitShape [vx + x, vy + y, vz + z],     //Shape
    269.                                 vx + x - 1, vz + z - 1                     //Offset horizontals
    270.                             );
    271.                         }
    272.                     }
    273.                 }
    274.             }
    275.         }
    276.     }
    277.  
    278.     bool NotSurrounded(int x, int y, int z){
    279.         if (
    280.             cubits[x,y,z - 1] == 0 ||
    281.             cubits[x,y,z + 1] == 0 ||
    282.             cubits[x - 1,y,z] == 0 ||
    283.             cubits[x + 1,y,z] == 0 ||
    284.             cubits[x,y - 1,z] == 0 ||
    285.             cubits[x,y + 1,z] == 0 ||
    286.             cubits[x - 1,y - 1,z - 1] == 0 ||
    287.             cubits[x - 1,y - 1,z + 1] == 0 ||
    288.             cubits[x + 1,y - 1,z - 1] == 0 ||
    289.             cubits[x + 1,y - 1,z + 1] == 0 ||
    290.             cubits[x - 1,y + 1,z - 1] == 0 ||
    291.             cubits[x - 1,y + 1,z + 1] == 0 ||
    292.             cubits[x + 1,y + 1,z - 1] == 0 ||
    293.             cubits[x + 1,y + 1,z + 1] == 0 ||
    294.             cubits[x - 1,y,z - 1] == 0 ||
    295.             cubits[x - 1,y,z + 1] == 0 ||
    296.             cubits[x + 1,y,z - 1] == 0 ||
    297.             cubits[x + 1,y,z + 1] == 0 ||
    298.             cubits[x,y - 1,z - 1] == 0 ||
    299.             cubits[x,y + 1,z - 1] == 0 ||
    300.             cubits[x,y - 1,z + 1] == 0 ||
    301.             cubits[x,y + 1,z + 1] == 0 ||
    302.             cubits[x - 1,y - 1,z] == 0 ||
    303.             cubits[x + 1,y - 1,z] == 0 ||
    304.             cubits[x - 1,y + 1,z] == 0 ||
    305.             cubits[x + 1,y + 1,z] == 0
    306.         ) {
    307.             return true;
    308.         }
    309.         return false;
    310.     }
    311.  
    312.     void MarchTerrain(int x, int y, int z) {
    313.         short[] adjacents = new short[6] {0,0,0,0,0,0};
    314.         string adjStr = "";
    315.  
    316.         //short[] cornerAdjacents = new short[8] {0,0,0,0,0,0,0,0};
    317.         //string cornrStr = "";
    318.         if (cubits [x, y, z - 1] != 0) {
    319.             adjacents [0] = 1;
    320.         }
    321.         if (cubits [x - 1, y, z] != 0) {
    322.             adjacents [1] = 1;
    323.         }
    324.         if (cubits [x, y, z + 1] != 0) {
    325.             adjacents [2] = 1;
    326.         }
    327.         if (cubits [x + 1, y, z] != 0) {
    328.             adjacents [3] = 1;
    329.         }
    330.         if (cubits [x, y + 1, z] != 0) {
    331.             adjacents [4] = 1;
    332.         }
    333.         if (cubits [x, y - 1, z] != 0) {
    334.             adjacents [5] = 1;
    335.         }
    336.         foreach (short s in adjacents) {
    337.             adjStr += s;
    338.         }
    339.         switch (adjStr) {
    340.         /*
    341.         case "000000":
    342.             cubitShape [x, y, z] = 0;
    343.             break; */
    344.         case "010001":
    345.             cubitShape [x, y, z] = 3;
    346.             break;
    347.         case "111001":
    348.             cubitShape [x, y, z] = 3;
    349.             break;
    350.         case "100001":
    351.             cubitShape [x, y, z] = 4;
    352.             break;
    353.         case "110101":
    354.             cubitShape [x, y, z] = 4;
    355.             break;
    356.         case "000101":
    357.             cubitShape [x, y, z] = 1;
    358.             break;
    359.         case "101101":
    360.             cubitShape [x, y, z] = 1;
    361.             break;
    362.         case "001001":
    363.             cubitShape [x, y, z] = 2;
    364.             break;
    365.         case "011101":
    366.             cubitShape [x, y, z] = 2;
    367.             break;
    368.         default:
    369.             cubitShape [x, y, z] = 0;
    370.             break;
    371.         }
    372.         //Front, Left, Back, Right, Top, Bottom
    373.     }
    374.  
    375.     bool InBounds(int x, int y, int z, int x2, int y2, int z2, int x3, int y3, int z3){
    376.         //Makes sure the cubit isn't an edge spacer cubit
    377.         if (x > x2 && x < x3 &&
    378.             y > y2 && y < y3 &&
    379.             z > z2 && z < z3) {
    380.             return true;
    381.         } else {
    382.             return false;
    383.         }
    384.     }
    385.  
    386.     void SetPoints (float xa, float ya, float za, int shape){
    387.         float xb = xa + 1;
    388.         float yb = ya - 1;
    389.         float zb = za + 1;
    390.         Vector3[] pBase = new Vector3[8] { //Base vertice positions for the shapes
    391.             new Vector3 (xa, ya, za), //Vertice [0]
    392.             new Vector3 (xb, ya, za), //Vertice [1]
    393.             new Vector3 (xb, yb, za), //Vertice [2]
    394.             new Vector3 (xa, yb, za), //Vertice [3]
    395.             new Vector3 (xa, ya, zb), //Vertice [4]
    396.             new Vector3 (xb, ya, zb), //Vertice [5]
    397.             new Vector3 (xb, yb, zb), //Vertice [6]
    398.             new Vector3 (xa, yb, zb)  //Vertice [7]
    399.         };
    400.  
    401.         int t = 0;
    402.         Vector3[] pointID = new Vector3[8];
    403.         foreach (int s in sequence[shape]){
    404.             pointID[t] = pBase[s];
    405.             t++;
    406.         }
    407.         t = 0;
    408.         foreach (int s in new int[] { //Vertice mapping pattern.
    409.             0,1,3,1,2,3, //front
    410.             4,0,7,0,3,7, //left
    411.             5,4,6,4,7,6, //back
    412.             1,5,2,5,6,2, //right
    413.             4,5,0,5,1,0, //top
    414.             3,2,7,2,6,7  //bottom
    415.         })
    416.         {
    417.             newVertices.Add (pointID [s]);
    418.         }
    419.     }
    420.  
    421.     void SetUVMap(int mat = 0){
    422.         Vector2[] texPos = new Vector2[] {
    423.             new Vector2 (texSize * matID [mat].x,           texSize * matID [mat].y + texSize),
    424.             new Vector2 (texSize * matID [mat].x + texSize, texSize * matID [mat].y + texSize),
    425.             new Vector2 (texSize * matID [mat].x + texSize, texSize * matID [mat].y),
    426.             new Vector2 (texSize * matID [mat].x,           texSize * matID [mat].y)
    427.         };
    428.         for (int i = 0; i < 6; i++) {
    429.             foreach (int v in new int[] { 0, 1, 3, 1, 2, 3 }) {
    430.                 newUV.Add (texPos [v]);
    431.             }
    432.         }
    433.     }
    434.  
    435.     void CubeTriangles(int x, int y, int z){
    436.         for (int u = 0; u < 6; u++) {
    437.             for (int v = 0; v < 6; v++) {
    438.                 if (CanBeRendered (x, y, z, u)) {
    439.                     newTriangles.Add ((u * 6) + v + (cb * 36));
    440.                 }
    441.             }
    442.         }
    443.     }
    444.  
    445.     bool CanBeRendered (int x, int y, int z, int faces){
    446.         if (FaceExposed (x, y, z, faces) && FaceIsNotExposer (x, y, z, faces)) {
    447.             return true;
    448.         } else if (!FaceIsNotExposer (x, y, z, faces)) {
    449.             return true;
    450.         }
    451.         return false;
    452.     }
    453.  
    454.     bool FaceIsNotExposer(int x, int y, int z, int faces){
    455.         if (faces < 1) {
    456.             if (exposers [cubitShape [x, y, z]] [0] == 0) {
    457.                 return true;
    458.             }
    459.         } else if (faces < 2) {
    460.             if (exposers [cubitShape [x, y, z]] [1] == 0) {
    461.                 return true;
    462.             }
    463.         } else if (faces < 3) {
    464.             if (exposers [cubitShape [x, y, z]] [2] == 0) {
    465.                 return true;
    466.             }
    467.         } else if (faces < 4) {
    468.             if (exposers [cubitShape [x, y, z]] [3] == 0) {
    469.                 return true;
    470.             }
    471.         } else if (faces < 5) {
    472.             if (exposers [cubitShape [x, y, z]] [4] == 0) {
    473.                 return true;
    474.             }
    475.         } else if (faces < 6) {
    476.             if (exposers [cubitShape [x, y, z]] [5] == 0) {
    477.                 return true;
    478.             }
    479.         }
    480.         return false;
    481.     }
    482.  
    483.     bool FaceExposed(int x, int y, int z, int faces){ //Checks to see which faces are not completely covered by another.
    484.         /*##*/ if (faces < 1 && exposers [cubitShape [x, y, z]] [0] == 0) {
    485.             if (exposers [cubitShape [x, y, z - 1]] [2] == 1 || cubits [x, y, z - 1] == 0) {//Front
    486.                 return true;
    487.             }
    488.         } else if (faces < 2 && exposers [cubitShape [x, y, z]] [1] == 0) {
    489.             if (exposers [cubitShape [x - 1, y, z]] [3] == 1 || cubits [x - 1, y, z] == 0) {//Left
    490.                 return true;
    491.             }
    492.         } else if (faces < 3 && exposers [cubitShape [x, y, z]] [2] == 0) {
    493.             if (exposers [cubitShape [x, y, z + 1]] [0] == 1 || cubits [x, y, z + 1] == 0) {//Back
    494.                 return true;
    495.             }
    496.         } else if (faces < 4 && exposers [cubitShape [x, y, z]] [3] == 0) {
    497.             if (exposers [cubitShape [x + 1, y, z]] [1] == 1 || cubits [x + 1, y, z] == 0) {//Right
    498.                 return true;
    499.             }
    500.         } else if (faces < 5 && exposers [cubitShape [x, y, z]] [4] == 0) {
    501.             if (exposers [cubitShape [x, y + 1, z]] [5] == 1 || cubits [x, y + 1, z] == 0) {//Top
    502.                 return true;
    503.             }
    504.         } else if (faces < 6 && exposers [cubitShape [x, y, z]] [5] == 0) {
    505.             if (exposers [cubitShape [x, y - 1, z]] [4] == 1 || cubits [x, y - 1, z] == 0) {//Bottom
    506.                 return true;
    507.             }
    508.         }
    509.         return false;
    510.     }
    511.     // End of code.
    512. }
    It keeps telling me there's an array out of bounds in the SetUVMap() method, on the line with the Vector2 array (line 422). I can't figure out what the heck it wants.
     
  13. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,181
    Code (csharp):
    1. void SetUVMap(int mat =0) {
    2.     if(mat < 0 || mat > matID.Length - 1) {
    3.         Debug.LogError("Whoops!");
    4.     }
    When you use array (or object) initalizers like that, and one of the objects throw an error, the line the error shows up on is the initialization line.

    C# is really bad at exceptions! Why isn't the index that's out of bounds printed?
     
  14. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    Good question.

    Weird stuff happens though when I tell it to bypass by setting the material to the default, which is stone -
    Code (CSharp):
    1. void SetUVMap(int mat = 0)
    2.     {
    3.         int mt = mat;
    4.         if (mt > 3 || mt < 0){
    5.             mt = 1;
    6.         }

    The highlighted object, as well as the other ones along the X dimension from it, is doing it right, aside from the fact that it isn't rendering the first x and z rows (you can see the z dimension for the cubits is 7, not the wanted 8).

    It's supposed to steadily rise every four cubits in the Z direction.... I don't think this looks right xD

    By the way, I'm designing a 3d-space chunk environment instead of the usual 2d-space chunk environment. Chunks are called ground or grounds in the script.
     
  15. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    Found something strange... The reason the material keeps getting out of bounds is because it's inverted. Used this and it solved that problem, but I fear the thing that's causing that is also causing the other glitches -
    Code (CSharp):
    1.     void SetUVMap(int mat = 0)
    2.     {
    3.         int mt = mat;
    4.         if (mt < 0) {
    5.             mt = mt * -1;
    6.         }
    Edit - Yet something else strange.



    Increased worldsize to 16 and decreased worldheight to 2, then set the increment each Z row to 7. Something's definitely passing through, but this... is weird.
     
    Last edited: Sep 15, 2016
  16. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
    Let me teach you about Mathf.Abs.
     
    ob103ninja likes this.
  17. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    Thanks, that simplifies things :D
    Code (CSharp):
    1. void SetUVMap(int mat = 0)
    2.     {
    3.         Vector2[] texPos = new Vector2[] {
    4.             new Vector2 (texSize * matID [Mathf.Abs(mat)].x,           texSize * matID [Mathf.Abs(mat)].y + texSize),
    5.             new Vector2 (texSize * matID [Mathf.Abs(mat)].x + texSize, texSize * matID [Mathf.Abs(mat)].y + texSize),
    6.             new Vector2 (texSize * matID [Mathf.Abs(mat)].x + texSize, texSize * matID [Mathf.Abs(mat)].y),
    7.             new Vector2 (texSize * matID [Mathf.Abs(mat)].x,           texSize * matID [Mathf.Abs(mat)].y)
    8.         };
    9.         for (int i = 0; i < 6; i++) {
    10.             foreach (int v in new int[] { 0, 1, 3, 1, 2, 3 }) {
    11.                 newUV.Add (texPos [v]);
    12.             }
    13.         }
    14.     }
    Also, I FIXED IT. I feel like an idiot too, because the solution was one single line of code. Again.

    Code (CSharp):
    1. void BuildTerrain(int x, int y, int z) {
    2.         //Forms the individual meshes.
    3.         int clx = cubits.GetLength (0) - 1;
    4.         int clxb = cubits.GetLowerBound (0);
    5.         int cly = cubits.GetLength (1) - 1;
    6.         int clyb = cubits.GetLowerBound (1);
    7.         int clz = cubits.GetLength (2) - 1;
    8.         int clzb = cubits.GetLowerBound (2);
    9.         //int xKey = (int)clx - (clx / 2);
    10.         //int zKey = (int)clz - (clz / 2);
    11.  
    12.         for (int vx = 0; vx < groundSize; vx++) {
    13.             for (int vy = 0; vy < groundSize; vy++) {
    14.                 for (int vz = 0; vz < groundSize; vz++) {
    15.                     if (cubits [vx + x, vy + y, vz + z] != 0) { // <<< This was vx,vy,vz instead of what it is now
    16.                         if (InBounds (vx + x, vy + y, vz + z, clxb, clyb, clzb, clx, cly, clz)) {
    17.                             if (NotSurrounded (vx + x, vy + y, vz + z)) { //<<< This is new, and helps reduce data
    18.                                 MakeMesh (
    19.                                     vx + x, vy + y, vz + z,
    20.                                     cubits [vx + x, vy + y, vz + z] - 1,
    21.                                     cubitShape [vx + x, vy + y, vz + z],
    22.                                     -1//Offset horizontals
    23.                                 );
    24.                             }
    25.                         }
    26.                     }
    27.                 }
    28.             }
    29.         }
    30.     }
     
  18. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,181
    Well, then, since we're allowed to nitpick, this:

    Code (CSharp):
    1.  
    2. Vector2[] texPos = new Vector2[] {
    3.     new Vector2 (texSize * matID [Mathf.Abs(mat)].x,           texSize * matID [Mathf.Abs(mat)].y + texSize),
    4.     new Vector2 (texSize * matID [Mathf.Abs(mat)].x + texSize, texSize * matID [Mathf.Abs(mat)].y + texSize),
    5.     new Vector2 (texSize * matID [Mathf.Abs(mat)].x + texSize, texSize * matID [Mathf.Abs(mat)].y),
    6.     new Vector2 (texSize * matID [Mathf.Abs(mat)].x,           texSize * matID [Mathf.Abs(mat)].y)
    7. };
    Could be:

    Code (CSharp):
    1.  
    2. var x = texSize * matID [Mathf.Abs(mat)].x;
    3. var y = texSize * matID [Mathf.Abs(mat)].y;
    4.  
    5. Vector2[] texPos = new Vector2[] {
    6.     new Vector2 (x,           y + texSize),
    7.     new Vector2 (x + texSize, y + texSize),
    8.     new Vector2 (x + texSize, y),
    9.     new Vector2 (x,           y)
    10. };
    refactor your locals for great good!
     
    Boz0r likes this.
  19. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    Well, would that, by any bit, run slower? Or faster? That UV method is used during runtime whenever the terrain is edited or initialized, so any minor amount of time there would add up on slower machines.
     
  20. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
    It would be faster. You're doing two multiplications four times, and Mathf.abs() eight times, but he's only doing them once, and saving them.
     
  21. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,181
    I mean, there's a good possibility that the compiler realizes what's going on in his version and just magically turns it into my version. Depends on how smart it is.

    The difference in execution speed will not be noticeable, though. The difference in how fast it is to read is, like, in several orders of magnitude.
     
  22. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    Alright, I'll implement that and see how it goes.
     
  23. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    K, so I tested it, and the working time does go down. Thanks!

    Also implementing the ability for a player to remove cubits (and soon, add them). Had some fun and got these snapshots.


     
    Baste likes this.
  24. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    Some more progress -
     
    Baste likes this.
  25. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45


    Apparently there's more shape possibilities than I originally thought. This might not even be the final list.

    147 total.
    That's insane.
    Someone please tell me there's a better way of doing this than just writing the individual coordinates for every single one of these... and I don't want them to smooth out to those positions automatically every time. Some of them are specifically for players to make themselves.
     
  26. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    Alright, been working on the random terrain part a little more. I made these very quickly in blender for an example -



    I need to figure out how to generate these in a 2d array somehow, with the exception of the one on the top right as I did that one. The colored one is a value array as well but represents biomes for grass tinting and other things. The cell noise in the topleft would be for terrain height scaling and the rough noise in the bottomright... I forget what I was thinking when I did that one.

    Also, here's the code for the soft noise generator. I made my own method in place of perlin noise however, since I wanted to try my hand at it.
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class TerraNoise : MonoBehaviour {
    6.     public static int[,] heightMapBase;
    7.     public static int[][,] heightMap;
    8.     public static int blurArraysAmnt;
    9.     public static int[] blurRadius = new int[16] {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17};
    10.     public static System.Random noise;
    11.  
    12.     public static int waitOver;
    13.  
    14.     public static IEnumerator SetHeightMapBase(int x, int y, int seed){
    15.         noise = new System.Random (seed);
    16.         heightMapBase = new int[x, y];
    17.         heightMap = new int[blurArraysAmnt] [,];
    18.         for(int vx = 0; vx < x; vx++){
    19.             for (int vy = 0; vy < y; vy++) {
    20.                 heightMapBase [vx, vy] = noise.Next (-256, 256);
    21.             }
    22.         }
    23.         decimal[,] smearXY;
    24.         int invX = 0;
    25.         int invY = 0;
    26.         for (int i = 0; i < blurArraysAmnt; i++)
    27.         {
    28.             Worldgen.loadingTextOutput = "Building terrain heightmap formant " + (i + 1).ToString() + "/" + blurArraysAmnt.ToString();
    29.             heightMap [i] = new int[x, y];
    30.             smearXY = new decimal[(blurRadius [i] * 2) + 1, (blurRadius [i] * 2) + 1];
    31.             for(int blx = -blurRadius[i]; blx <= blurRadius[i]; blx++){
    32.                 for (int bly = -blurRadius[i]; bly < blurRadius[i]; bly++) {
    33.                     invX = Mathf.Abs(Mathf.Abs(blx) - blurRadius[i]);
    34.                     invY = Mathf.Abs(Mathf.Abs(bly) - blurRadius[i]);
    35.                     if (Mathf.Abs(blx)+Mathf.Abs(bly)<=blurRadius[i]){
    36.                         smearXY [blx + blurRadius[i], bly + blurRadius[i]] = (decimal)((float)(invX + invY - blurRadius[i] + 1) / (float)(blurRadius [i] + 1));
    37.                     }
    38.                 }
    39.             }
    40.             for(int vx = 0; vx < x; vx++){
    41.                 for (int vy = 0; vy < y; vy++)
    42.                 {
    43.                     for(int blx = -blurRadius[i]; blx <= blurRadius[i]; blx++){
    44.                         for (int bly = -blurRadius[i]; bly < blurRadius[i]; bly++) {
    45.                             if (blx+vx >= 0 && blx+vx <= heightMapBase.GetUpperBound (0) &&
    46.                                 bly+vy >= 0 && bly+vy <= heightMapBase.GetUpperBound (1)) {
    47.                                 heightMap [i] [blx + vx, bly + vy] += (int) (heightMapBase[vx, vy] * smearXY [blx + blurRadius[i], bly + blurRadius[i]]);
    48.                             }
    49.                         }
    50.                     }
    51.                 }
    52.                 if (Worldgen.timerFPS [2] <= 0) {
    53.                     Worldgen.timerFPS [2] = 6;
    54.                 }
    55.                 if (Worldgen.timerFPS [2] <= 1) {
    56.                     Worldgen.loadingBarOutput = (float)vx / (float)x;
    57.                     yield return new WaitUntil (() => Worldgen.timerFPS [2] <= 0);
    58.                 }
    59.                 Worldgen.timerFPS [2] -= 1;
    60.                 /*Worldgen.timerFPS [2] = 1;
    61.                 Worldgen.loadingBarOutput = (float)vx / (float)x;
    62.                 yield return new WaitUntil (() => Worldgen.timerFPS [2] <= 0);*/
    63.             }
    64.         }
    65.         Worldgen.waitingBlurLoad [0] = 1;
    66.         yield return new WaitForEndOfFrame ();
    67.     }
    68. }
    69.  
    My goal was to make something that works quicker than perlin yet have it so that it is more easily modifiable while being generated. Of course, it's not as quick as I hoped, but it works. Any way I could optimize this?

    Edit - Some of the values are set by the voxel code, so it's dependent on it.

    Code (CSharp):
    1. TerraNoise.blurArraysAmnt = 1;
    2.         TerraNoise.blurRadius = new int[] { 14 };
    3.  
    4.         waitingBlurLoad [0] = 0;
    5.         while (waitingBlurLoad [0] == 0) {
    6.             if (waitingBlurLoad [1] == 0) {
    7.                 StartCoroutine(TerraNoise.SetHeightMapBase (cubits.GetLength (0), cubits.GetLength (2), 4126));
    8.                 waitingBlurLoad [1] = 1;
    9.             }
    10.             if (waitingBlurLoad [0] == 0) {
    11.                 timerFPS [0] = 1;
    12.                 yield return new WaitUntil (() => timerFPS [0] <= 0);
    13.             }
    14.         }
    15.         waitingBlurLoad [0] = 0;
    And yes, everything is in coroutines now. That way the code is able to run in the background, so that a loading bar can be used.