Search Unity

Procedural Planet Terrain Engine

Discussion in 'Scripting' started by BradMick, May 5, 2016.

  1. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    I can see where the problem lies, just not sure how to fix it (and not sure I have the aptitude to explain it.... but here goes). It's all in QuadSurface GenerateSurfaceCoordinates() particularly how the Vertex Coordinates are calculated.

    If we put some of the calculation in a separate variable, the problem starts to expose itself :
    Code (csharp):
    1. // vertex position and offset
    2. float xPos = (float)j * increment - 0.5f + ((float)xIndex / Mathf.Pow(2, subDivisions));
    3. float yPos = 0.5f;
    4. float zPos = (float)i * increment - 0.5f + ((float)zIndex / Mathf.Pow(2, subDivisions));
    5.  
    6. // ((float)xIndex / Mathf.Pow(2, subDivisions)) <-- this is basically an offset, based on the position of the subDivision
    7. float xOffset = ((float)xIndex / Mathf.Pow(2, subDivisions));
    8. float zOffset = ((float)zIndex / Mathf.Pow(2, subDivisions));
    9.  
    10. float xPos = (float)j * increment - 0.5f + xOffset;
    11. float yPos = 0.5f;
    12. float zPos = (float)i * increment - 0.5f + zOffset;
    The offset has to be relative to the previous subDivision : where the previous subDivision started is what this offset should equal. Clear as mud?!
    Code (csharp):
    1. // what previous surface is this surface a branch of ????
    2.  
    3. // previous subDivision :
    4.  
    5. // 0_______1_______2
    6. // |_______|_______|
    7.  
    8.  
    9. // this subDivision :
    10.  
    11. // ........0___1___2
    12. // |_______|___|___|
    13. // |_______|_______|
    for this surface/subDivision offset, we need to know what the previous surface/subDivision offset was. I did some experimentation, but couldn't find the formula for now. Shall check back later to see if you worked it out!
     
    Last edited: Sep 3, 2016
  2. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Yup, you're spot on! And I solved it through the....second layer of Subdivisions. Basically what I've worked out...everything has to increase by 1.

    we start with indices of (0, 0) (that very first face...hereafter known as LOD 0)
    when we subdivide, we gain: (0,0) (1,0) (0, 1) (1, 1)
    if we subdivide say....(1,0), that becomes (2, 0) (2, 1) (3, 0) (3, 1)

    So that was good to go...but now that I'm 3 layers deep, i'm 1 off again. So I don't know if I need to change the GenerateSurfaceCoordinates bit or the SubDivide bit...From the screen shot you can see that it's offset by 1 index wise.

    I know what your saying and I hope you know that I'm saying...hah! It all makes sense, I just cant' puzzle out the formula yet. I feel that I'm close...right on the cusp.

    I did find this: https://github.com/Tiggilyboo/UnityQuadTreePlanet

    Which gave me a bit of insight into the LOD system, and some of the ideas I've adapted to my framework. But because of how he builds his geometry versus how I build mine I'm struggling to parse the two together for sure.

    Anyway, here's the latest and greatest code!

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class GeneratePlanet : MonoBehaviour
    5. {
    6.     [Header("Planet Info")]
    7.     [Range(3.0f, 7.0f)] public int   LOD = 3;
    8.     [Range(0.0f, 1.0f)] public float WATERLEVEL = 0.5f;
    9.     public float PLANETRADIUS = 1; //m, will eventually be km
    10.     public float WORLDHEIGHT = 0.1f;
    11.     [Header("Texture and Noise Variables")]
    12.     public int   TEXTURESIZE = 256;
    13.     public float NOISESCALE = 1.45f;
    14.     public float NOISEOFFSET = 1.56f;
    15.     public int   OCTAVES = 8;
    16.     [Header("Planet Colors")]
    17.     public Gradient PLANETCOLORS;
    18.     [Header("Test")]
    19.     public bool USEVERTEXCOLORS = false;
    20.     public Material vertexColors;
    21.  
    22.  
    23.     //The planet!
    24.     private QuadSphere planet;
    25.     //Textures!
    26.     private TextureGenerator textureGenerator;
    27.     //Will eventually be made private...need to see what's going on for now...
    28.     [Header("Will eventually be private...")]
    29.     public Texture2D[] textures;
    30.  
    31.     void Start()
    32.     {
    33.         //PLANETCOLORS = PlanetUtilities.CreateColourGradient(PLANETCOLORS);
    34.         //Generate textures!
    35.         textureGenerator = new TextureGenerator(TEXTURESIZE,NOISEOFFSET, OCTAVES, NOISESCALE, PLANETCOLORS);
    36.         //Assign the textures created above to the local textures to make passing them along easier...
    37.         textures = textureGenerator.surfaceTextures;
    38.  
    39.         planet = new QuadSphere("Planet1", LOD, WATERLEVEL, PLANETRADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT, textures);
    40.     }
    41.  
    42.     void Update()
    43.     {
    44.  
    45.     }
    46.  
    47.     //This is meant for the Solar System Generator in the future...
    48.     /*public GeneratePlanet(string name, int lod, int radius)
    49.     {
    50.         planet = new QuadSphere(name, lod, radius);
    51.     }*/
    52. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class QuadSphere
    5. {
    6.     GameObject      planet;
    7.     QuadSurface[]   surfaces;
    8.  
    9.     public QuadSphere(string name, int lod, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures)
    10.     {
    11.         //Initialize the array
    12.         surfaces = new QuadSurface[6];
    13.  
    14.         //Create a new new QuadSurface using the specified LOD.
    15.         for (int i = 0; i < 6; i++)
    16.         {
    17.             //LOD 0
    18.             surfaces[i] = new QuadSurface(lod, 0, 0, 0, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, (quadFace)i);
    19.         }
    20.  
    21.         //Testing...
    22.         //LOD 1
    23.         surfaces[0].SubDivide();
    24.         //LOD 2
    25.         surfaces[0].tree[0].SubDivide();
    26.         surfaces[0].tree[2].SubDivide();
    27.         //LOD 3
    28.         surfaces[0].tree[2].tree[1].SubDivide();
    29.  
    30.             planet = new GameObject(name);
    31.         for (int i = 0; i < surfaces.Length; i++)
    32.             surfaces[i].surface.transform.parent = planet.transform;
    33.     }
    34. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class QuadSurface
    5. {
    6.     public QuadSurface   root;
    7.     public QuadSurface[] tree;
    8.  
    9.     public GameObject   surface  { get; private set; }
    10.     public MeshRenderer renderer { get; private set; }
    11.     public MeshCollider collider { get; private set; }
    12.     public MeshFilter   filter   { get; private set; }
    13.  
    14.     private int         LEVELOFDETAIL, SUBDIVISIONS, XINDEX, ZINDEX;
    15.     private float       WATERLEVEL, RADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT;
    16.     private Texture2D[] TEXTURES;
    17.     private quadFace    QUADFACE;
    18.  
    19.     public bool[] neighbors;
    20.     public bool   generated = false;
    21.     public bool   finalized = false;
    22.  
    23.     //Vertex Colors Testing
    24.     GameObject     planetGenerator;
    25.     GeneratePlanet generator;
    26.     Material       planetMaterial;
    27.     Color[]        planetColors;
    28.     Gradient       planetGradient;
    29.  
    30.     public QuadSurface(QuadSurface root, int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    31.     {
    32.         InitializeSurfaceGenerator(root, lod, subDivisions, xIndex, zIndex, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, face);
    33.     }
    34.  
    35.     public QuadSurface(int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    36.     {
    37.         InitializeSurfaceGenerator(null, lod, subDivisions, xIndex, zIndex, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, face);
    38.     }
    39.  
    40.     void InitializeSurfaceGenerator(QuadSurface root, int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    41.     {
    42.         LEVELOFDETAIL = lod;
    43.         SUBDIVISIONS  = subDivisions;
    44.         XINDEX        = xIndex;
    45.         ZINDEX        = zIndex;
    46.         WATERLEVEL    = waterLevel;
    47.         RADIUS        = radius;
    48.         NOISEOFFSET   = noiseOffset;
    49.         OCTAVES       = octaves;
    50.         NOISESCALE    = noiseScale;
    51.         WORLDHEIGHT   = worldHeight;
    52.         TEXTURES      = textures;
    53.         QUADFACE      = face;
    54.  
    55.         //Vertex Colors Testing
    56.         planetGenerator = GameObject.Find("PlanetGenerator");
    57.         generator       = planetGenerator.GetComponent<GeneratePlanet>();
    58.         planetMaterial  = generator.vertexColors;
    59.         planetGradient  = generator.PLANETCOLORS;
    60.  
    61.  
    62.         GenerateSurfaceCoordinates(root, subDivisions, xIndex, zIndex, face);
    63.     }
    64.  
    65.     void GenerateSurfaceCoordinates(QuadSurface root, int subDivisions, int xIndex, int zIndex, quadFace face)
    66.     {
    67.         //The base number of faces is 2, which means we need +1 vert to make that happen.
    68.         int vertsPerQuad = (int)Mathf.Pow(2, LEVELOFDETAIL) + 1;
    69.  
    70.         //Appropriately size (and create) the Vertex Array
    71.         Vector3[] vertices = new Vector3[vertsPerQuad * vertsPerQuad];
    72.         //Appropriately size (and create) the Normal Array
    73.         Vector3[] normals = new Vector3[vertices.Length];    
    74.         //Appropriately size (and create) the UV Array
    75.         Vector2[] uvs = new Vector2[vertices.Length];
    76.         //Vertex Colors Testing
    77.         Color[] colors = new Color[vertices.Length];
    78.         planetColors = colors;
    79.  
    80.         //Calculate the increment to keep the vertices constrained to a 1x1 cube.
    81.         float increment     = ((1.0f / Mathf.Pow(2, LEVELOFDETAIL)) / Mathf.Pow(2, subDivisions));
    82.         float uvIncrement   = (1.0f / Mathf.Pow(2, LEVELOFDETAIL)) / Mathf.Pow(2, subDivisions);
    83.  
    84.         for (int i = 0, index = 0; i < vertsPerQuad; i++)
    85.         {
    86.             for (int j = 0; j < vertsPerQuad; j++, index++)
    87.             {
    88.                 //Vertex Coordinates
    89.                 float xPos = (float)j * increment - 0.5f + ((float)xIndex / Mathf.Pow(2, subDivisions));
    90.                 float yPos = 0.5f;
    91.                 float zPos = (float)i * increment - 0.5f + ((float)zIndex / Mathf.Pow(2, subDivisions));
    92.                 //UV Coordinates
    93.                 float xUV = (float)j * uvIncrement + ((float)xIndex / Mathf.Pow(2, subDivisions));
    94.                 float zUV = (float)i * uvIncrement + ((float)zIndex / Mathf.Pow(2, subDivisions));
    95.  
    96.                 switch (face)
    97.                 {
    98.                     case quadFace.TOP:
    99.                         //Assign Vertex Coordinates
    100.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    101.                         break;
    102.  
    103.                     case quadFace.BOTTOM:
    104.                         //Assign Vertex Coordinates
    105.                         vertices[index] = new Vector3(xPos, -yPos, -zPos);
    106.                         break;
    107.  
    108.                     case quadFace.LEFT:
    109.                         //Assign Vertex Coordinates
    110.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    111.                         break;
    112.  
    113.                     case quadFace.RIGHT:
    114.                         //Assign Vertex Coordinates
    115.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    116.                         break;
    117.  
    118.                     case quadFace.FRONT:
    119.                         //Assign Vertex Coordinates
    120.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    121.                         break;
    122.  
    123.                     case quadFace.BACK:
    124.                         //Assign Vertex Coordinates
    125.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    126.                         break;
    127.                 }
    128.                 //Assign UV Coordinates
    129.                 uvs[index] = new Vector2(xUV, zUV);
    130.  
    131.                 //Spherify the vertices!
    132.                 vertices[index] = PlanetUtilities.SmoothVertices(vertices[index]);
    133.  
    134.                 float noise = PlanetUtilities.GetNoise(vertices[index], NOISEOFFSET, OCTAVES, NOISESCALE);
    135.                 //Vertex Colors Testing
    136.                 colors[index] = planetGradient.Evaluate(noise);
    137.              
    138.                 //Keep the oceans flat! - thanks alucardj!
    139.                 if (noise > WATERLEVEL)
    140.                     noise = (noise - WATERLEVEL) * (1.0f / (1.0f - WATERLEVEL));
    141.                 else
    142.                     noise = 0.0f;
    143.  
    144.                 vertices[index] *= RADIUS + (noise * WORLDHEIGHT);
    145.  
    146.                 normals[index] = vertices[index].normalized;
    147.             }
    148.         }
    149.         //Create the Surface Object
    150.         CreateSurfaceObject(root, xIndex, zIndex, face, vertsPerQuad, vertices, normals, uvs);
    151.     }
    152.  
    153.     void CreateSurfaceObject(QuadSurface root, int xIndex, int zIndex, quadFace face, int vertsPerQuad, Vector3[] vertexArray, Vector3[] normalArray, Vector2[] uvArray)
    154.     {
    155.         this.root = root;
    156.  
    157.         //Create the game object to which we'll attach everything to
    158.         surface = new GameObject("LOD: " + SUBDIVISIONS + " Face: " + face + " (" + xIndex + ", " + zIndex + ")");
    159.         //Add the mesh Renderer
    160.         renderer = surface.AddComponent<MeshRenderer>();
    161.         //Add the MeshCollider
    162.         collider = surface.AddComponent<MeshCollider>();
    163.         //Add the MeshFilter
    164.         filter   = surface.AddComponent<MeshFilter>();
    165.  
    166.         //Initialize the renderer
    167.         renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    168.         renderer.receiveShadows = true;
    169.         renderer.enabled = true;
    170.  
    171.         if (generator.USEVERTEXCOLORS == false)
    172.             //Assign the generated textures to the quadSphere
    173.             surface.GetComponent<Renderer>().material.mainTexture = TEXTURES[(int)face];
    174.         else
    175.             //Vertex Colors testing
    176.             surface.GetComponent<Renderer>().material = planetMaterial;
    177.  
    178.         if (root != null)
    179.             surface.transform.parent = root.surface.transform;
    180.  
    181.         CreateSurfaceMesh(face, vertsPerQuad, vertexArray, normalArray, uvArray);
    182.     }
    183.  
    184.     void CreateSurfaceMesh(quadFace face, int vertsPerQuad, Vector3[] vertexArray, Vector3[] normalArray, Vector2[] uvArray)
    185.     {
    186.         //If this has already been generated, enable the renderer
    187.         if (generated)
    188.         {
    189.             renderer.enabled = true;
    190.             if (root != null)
    191.                 root.collider.enabled = false;
    192.             collider.enabled = true;
    193.             return;
    194.         }
    195.  
    196.         int vertBufferSize = vertsPerQuad - 1;
    197.         //Size the vertex buffer
    198.         int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    199.         //Create a new Mesh object
    200.         Mesh surfaceMesh;
    201.         //Create a new Mesh using the Objects MeshFilter
    202.         surfaceMesh = surface.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    203.         surfaceMesh.name = surface.name;
    204.  
    205.         //Step through the Vertex Buffer
    206.         for (int triIndex = 0, vertIndex = 0, i = 0; i < vertBufferSize; i++, vertIndex++)
    207.         {
    208.             for (int j = 0; j < vertBufferSize; j++, triIndex += 6, vertIndex++)
    209.             {
    210.                 //  1
    211.                 //  | \
    212.                 //  |  \
    213.                 //  |   \
    214.                 //  0----2
    215.                 vertexBuffer[triIndex] = vertIndex;
    216.                 vertexBuffer[triIndex + 1] = vertIndex + vertsPerQuad;
    217.                 vertexBuffer[triIndex + 2] = vertIndex + 1;
    218.                 //  1----3
    219.                 //    \  |
    220.                 //     \ |
    221.                 //      \|
    222.                 //       2
    223.                 vertexBuffer[triIndex + 3] = vertIndex + vertsPerQuad;
    224.                 vertexBuffer[triIndex + 4] = vertIndex + vertsPerQuad + 1;
    225.                 vertexBuffer[triIndex + 5] = vertIndex + 1;
    226.             }
    227.         }
    228.         //Assign the Vertex Array from Generate Surface Coordinates to the Mesh Vertex Array
    229.         surfaceMesh.vertices = vertexArray;
    230.         //Assign the UV Coordinates
    231.         surfaceMesh.uv = uvArray;
    232.         //Assign the Vertex Buffer
    233.         surfaceMesh.triangles = vertexBuffer;
    234.         //Vertex Colors Testing
    235.         surfaceMesh.colors = planetColors;
    236.         //Recalculate the Normals
    237.         //surfaceMesh.RecalculateNormals();
    238.         surfaceMesh.normals = normalArray; //Done manually...
    239.         //Recalculate Bounds
    240.         surfaceMesh.RecalculateBounds();
    241.         //After the Mesh has been created, pass it back to the MeshCollider
    242.         surface.GetComponent<MeshCollider>().sharedMesh = surfaceMesh;
    243.  
    244.         //If root is ever null...Destroy!
    245.         if (root != null)
    246.             root.Destroy();
    247.  
    248.         generated = true;
    249.     }
    250.  
    251.     public void Destroy()
    252.     {
    253.         renderer.enabled = false;
    254.         generated = false;
    255.     }
    256.  
    257.     //Merges our quadtrees!
    258.     public void MergeQuadTree()
    259.     {
    260.         if (tree == null)
    261.             return;
    262.  
    263.         for (int i = 0; i < tree.Length; i++)
    264.         {
    265.             tree[i].MergeQuadTree();
    266.             tree[i].Destroy();
    267.         }
    268.  
    269.         tree = null;
    270.     }
    271.  
    272.     public void SubDivide()
    273.     {
    274.         SUBDIVISIONS++;
    275.         Debug.Log(SUBDIVISIONS);
    276.         tree = new QuadSurface[4];
    277.  
    278.         tree[0] = new QuadSurface(this, LEVELOFDETAIL, SUBDIVISIONS, 0 + (XINDEX * SUBDIVISIONS), 0 + (ZINDEX * SUBDIVISIONS), WATERLEVEL, RADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT, TEXTURES, QUADFACE);
    279.         tree[1] = new QuadSurface(this, LEVELOFDETAIL, SUBDIVISIONS, 0 + (XINDEX * SUBDIVISIONS), 1 + (ZINDEX * SUBDIVISIONS), WATERLEVEL, RADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT, TEXTURES, QUADFACE);
    280.         tree[2] = new QuadSurface(this, LEVELOFDETAIL, SUBDIVISIONS, 1 + (XINDEX * SUBDIVISIONS), 0 + (ZINDEX * SUBDIVISIONS), WATERLEVEL, RADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT, TEXTURES, QUADFACE);
    281.         tree[3] = new QuadSurface(this, LEVELOFDETAIL, SUBDIVISIONS, 1 + (XINDEX * SUBDIVISIONS), 1 + (ZINDEX * SUBDIVISIONS), WATERLEVEL, RADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT, TEXTURES, QUADFACE);
    282.  
    283.         Destroy();
    284.     }
    285. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public enum quadFace { TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK }
    5.  
    6. public static class PlanetUtilities
    7. {
    8.     //From Catlike Coding!
    9.     public static Vector3 SmoothVertices(Vector3 vertex)
    10.     {
    11.         Vector3 v = vertex * 2f;
    12.         float x2 = v.x * v.x;
    13.         float y2 = v.y * v.y;
    14.         float z2 = v.z * v.z;
    15.  
    16.         Vector3 s;
    17.         s.x = v.x * Mathf.Sqrt(1f - y2 / 2f - z2 / 2f + y2 * z2 / 3f);
    18.         s.y = v.y * Mathf.Sqrt(1f - x2 / 2f - z2 / 2f + x2 * z2 / 3f);
    19.         s.z = v.z * Mathf.Sqrt(1f - x2 / 2f - y2 / 2f + x2 * y2 / 3f);
    20.  
    21.         return s;
    22.     }
    23.  
    24.     // using a Simplex Noise library :
    25.     //  http://cabbynode.net/2012/06/10/perlin-simplex-noise-for-c-and-xna/
    26.  
    27.     public static float GetNoise(Vector3 vertex, float noiseOffset, float octaves, float noiseScale)
    28.     {
    29.         // add offset
    30.         vertex.x += noiseOffset;
    31.         vertex.y += noiseOffset;
    32.         vertex.z += noiseOffset;
    33.  
    34.         // fractal noise
    35.         float amp = 1f;
    36.         float noise = 0f;
    37.         float gain = 1f;
    38.         float factor = 0f;
    39.         Vector3 calcVert;
    40.  
    41.         for (int i = 0; i < octaves; i++)
    42.         {
    43.             factor += 1f / gain;
    44.             calcVert = vertex * noiseScale * gain;
    45.             noise += Noise.Noise.GetNoise(calcVert.x, calcVert.y, calcVert.z) * (amp / gain);
    46.             gain *= 2f;
    47.         }
    48.  
    49.         // normalize noise by octave iteration factor
    50.         noise /= factor;
    51.  
    52.         return noise;
    53.     }
    54.  
    55.     public static Gradient CreateColourGradient(Gradient planetColors)
    56.     {
    57.         // using colour scale from LibNoise example : http://libnoise.sourceforge.net/tutorials/tutorial3.html
    58.         //renderer.AddGradientPoint (-1.0000, utils::Color (  0,  0, 128, 255)); // deeps
    59.         //renderer.AddGradientPoint (-0.2500, utils::Color (  0,  0, 255, 255)); // shallow
    60.         //renderer.AddGradientPoint ( 0.0000, utils::Color (  0, 128, 255, 255)); // shore
    61.         //renderer.AddGradientPoint ( 0.0625, utils::Color (240, 240,  64, 255)); // sand
    62.         //renderer.AddGradientPoint ( 0.1250, utils::Color ( 32, 160,  0, 255)); // grass
    63.         //renderer.AddGradientPoint ( 0.3750, utils::Color (224, 224,  0, 255)); // dirt
    64.         //renderer.AddGradientPoint ( 0.7500, utils::Color (128, 128, 128, 255)); // rock
    65.         //renderer.AddGradientPoint ( 1.0000, utils::Color (255, 255, 255, 255)); // snow
    66.  
    67.         planetColors = new Gradient();
    68.  
    69.         GradientColorKey[] gck = new GradientColorKey[8];
    70.         gck[0].color = CalculateGradientColour(0, 0, 128);
    71.         gck[0].time = CalculateGradientTime(-1.0000);
    72.         gck[1].color = CalculateGradientColour(0, 0, 255);
    73.         gck[1].time = CalculateGradientTime(-0.2500);
    74.         gck[2].color = CalculateGradientColour(0, 128, 255);
    75.         gck[2].time = CalculateGradientTime(0.0000);
    76.         gck[3].color = CalculateGradientColour(240, 240, 64);
    77.         gck[3].time = CalculateGradientTime(0.0625);
    78.         gck[4].color = CalculateGradientColour(32, 160, 0);
    79.         gck[4].time = CalculateGradientTime(0.1250);
    80.         gck[5].color = CalculateGradientColour(224, 224, 0);
    81.         gck[5].time = CalculateGradientTime(0.3750);
    82.         gck[6].color = CalculateGradientColour(128, 128, 128);
    83.         gck[6].time = CalculateGradientTime(0.7500);
    84.         gck[7].color = CalculateGradientColour(255, 255, 255);
    85.         gck[7].time = CalculateGradientTime(1.0000);
    86.  
    87.         GradientAlphaKey[] gak = new GradientAlphaKey[2];
    88.         gak[0].alpha = 1f;
    89.         gak[0].time = 0f;
    90.         gak[1].alpha = 1f;
    91.         gak[1].time = 1f;
    92.  
    93.         planetColors.SetKeys(gck, gak);
    94.  
    95.         return planetColors;
    96.     }
    97.  
    98.  
    99.     static Color CalculateGradientColour(int r, int g, int b)
    100.     {
    101.         return new Color((float)r / 255f, (float)g / 255f, (float)b / 255f);
    102.     }
    103.  
    104.  
    105.     static float CalculateGradientTime(double t)
    106.     {
    107.         return (float)((t + 1) * 0.5);
    108.     }
    109. }

    Code (CSharp):
    1. //SOURCE: alucardj
    2. //http://forum.unity3d.com/conversations/procedural-textures.465272/
    3.  
    4. using UnityEngine;
    5. using System.Collections;
    6. using System.Collections.Generic;
    7.  
    8. public class TextureGenerator
    9. {
    10.     public Texture2D[] surfaceTextures { get; private set; }
    11.  
    12.     private int   textureSize;
    13.     private float noiseOffset, octaves, noiseScale;
    14.     private Gradient planetColors;
    15.  
    16.     public TextureGenerator(int size, float offset, float oct, float scale, Gradient colors)
    17.     {
    18.         textureSize  = size;
    19.         noiseOffset  = offset;
    20.         octaves      = oct;
    21.         noiseScale   = scale;
    22.         planetColors = colors;
    23.  
    24.         surfaceTextures = new Texture2D[6];
    25.  
    26.         for (int i = 0; i < 6; i++ )
    27.             GenerateSurfaceTextures((quadFace)i);
    28.     }
    29.  
    30.     void GenerateSurfaceTextures(quadFace face)
    31.     {
    32.         Texture2D texture = new Texture2D(textureSize, textureSize);
    33.  
    34.         //Create the Color List that will be applied to the texture
    35.         List<Color> colorList = new List<Color>();
    36.         Color color;
    37.  
    38.         //Appropriately size (and create) the Vertex Array
    39.         Vector3[] vertices = new Vector3[textureSize * textureSize];
    40.  
    41.         // Calculate the increment to keep the vertices constrained to a 1x1 cube.
    42.         float increment = 1.0f / ((float)textureSize - 1);
    43.  
    44.         for (int i = 0, index = 0; i < textureSize; i++)
    45.         {
    46.             for (int j = 0; j < textureSize; j++, index++)
    47.             {
    48.                 // Vertex Coordinates
    49.                 float xPos = (float)j * increment - 0.5f;
    50.                 float yPos = 0.5f;
    51.                 float zPos = (float)i * increment - 0.5f;
    52.  
    53.                 // Assign Vertex Coordinates
    54.                 switch (face)
    55.                 {
    56.                     case quadFace.TOP:
    57.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    58.                         break;
    59.  
    60.                     case quadFace.BOTTOM:
    61.                         vertices[index] = new Vector3(xPos, -yPos, -zPos);
    62.                         break;
    63.  
    64.                     case quadFace.LEFT:
    65.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    66.                         break;
    67.  
    68.                     case quadFace.RIGHT:
    69.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    70.                         break;
    71.  
    72.                     case quadFace.FRONT:
    73.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    74.                         break;
    75.  
    76.                     case quadFace.BACK:
    77.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    78.                         break;
    79.                 }
    80.  
    81.                 //Spherify the Vertices!
    82.                 vertices[index] = PlanetUtilities.SmoothVertices(vertices[index]);
    83.  
    84.                 //Get noise!
    85.                 float noise = PlanetUtilities.GetNoise(vertices[index], noiseOffset, octaves, noiseScale);
    86.  
    87.                 // get Colour
    88.                 /*if (useGreyscale)
    89.                 {
    90.                     colour = new Color(noise, noise, noise, 1f);
    91.                 }
    92.                 else
    93.                 {
    94.                     colour = planetColours.Evaluate(noise);
    95.                 }*/
    96.  
    97.                 color = planetColors.Evaluate(noise);
    98.  
    99.                 // Add Colour to Texture Colour Array
    100.                 colorList.Add(color);
    101.             }
    102.         }
    103.  
    104.         // Apply Texture Colour Array to the Texture
    105.         texture.SetPixels(colorList.ToArray());
    106.         texture.wrapMode = TextureWrapMode.Clamp;
    107.         texture.Apply();
    108.  
    109.         surfaceTextures[(int)face] = texture;
    110.     }
    111. }

    Update:

    Okay...so...in my Excel book that I use to sort out formulas this is what I've worked out...The X and Z indexes need to iterate by 2 each time through. So....easy way to explain...

    If subD = 2, then indices are
    (0, 0) (2, 0) (4, 0) (6, 0) for one row
    (0, 2) (2, 2) (4, 2) (6, 2) for the next row
    (0, 4) (2, 4) (4, 4) (6, 4) for the next row
    (0, 6) (2, 6) (4, 6) (6, 6) for the last row

    attached is the spreadsheet.
     

    Attached Files:

    Last edited: Sep 3, 2016
  3. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Solved it!

    So...it was simpler than I was making it. Basically I wasn't leveraging the tree the way it was meant to be leveraged. So, this is how it does it:

    Code (CSharp):
    1.         for (int i = 0, index = 0; i < 2; i++)
    2.         {
    3.             for (int j = 0; j < 2; j++, index++)
    4.             {
    5.                 //This is the line...makes perfect sense after I wrote it....
    6.                 tree[index] = new QuadSurface(this, LEVELOFDETAIL, this.SUBDIVISIONS + 1, i + (this.XINDEX * 2), j + (this.ZINDEX * 2), WATERLEVEL, RADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT, TEXTURES, QUADFACE);
    7.             }
    8.         }
    Using the for loop, we iterate through the tree to create 4 new surfaces. We take into account the parents subdivision level and also the parents x and z indices, multiplying them by 2 (makes sense since everything is powers of 2) and then add 1. I've subdivided through 4 levels (changed the verbage...because for the purposes of this program LOD defines the number of verts you start with). That's it. It was that simple. Fun how a nights rest will make you realize you're dumb and the answer was right in front of you all along...go figure, hah!

    New code!

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class GeneratePlanet : MonoBehaviour
    5. {
    6.     [Header("Planet Info")]
    7.     [Range(3.0f, 7.0f)] public int   LOD = 3;
    8.     [Range(0.0f, 1.0f)] public float WATERLEVEL = 0.5f;
    9.     public float PLANETRADIUS = 1; //m, will eventually be km
    10.     public float WORLDHEIGHT = 0.1f;
    11.     [Header("Texture and Noise Variables")]
    12.     public int   TEXTURESIZE = 256;
    13.     public float NOISESCALE = 1.45f;
    14.     public float NOISEOFFSET = 1.56f;
    15.     public int   OCTAVES = 8;
    16.     [Header("Planet Colors")]
    17.     public Gradient PLANETCOLORS;
    18.     [Header("Test")]
    19.     public bool USEVERTEXCOLORS = false;
    20.     public Material vertexColors;
    21.  
    22.  
    23.     //The planet!
    24.     private QuadSphere planet;
    25.     //Textures!
    26.     private TextureGenerator textureGenerator;
    27.     //Will eventually be made private...need to see what's going on for now...
    28.     [Header("Will eventually be private...")]
    29.     public Texture2D[] textures;
    30.  
    31.     void Start()
    32.     {
    33.         //PLANETCOLORS = PlanetUtilities.CreateColourGradient(PLANETCOLORS);
    34.         //Generate textures!
    35.         textureGenerator = new TextureGenerator(TEXTURESIZE,NOISEOFFSET, OCTAVES, NOISESCALE, PLANETCOLORS);
    36.         //Assign the textures created above to the local textures to make passing them along easier...
    37.         textures = textureGenerator.surfaceTextures;
    38.  
    39.         planet = new QuadSphere("Planet1", LOD, WATERLEVEL, PLANETRADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT, textures);
    40.     }
    41.  
    42.     void Update()
    43.     {
    44.  
    45.     }
    46.  
    47.     //This is meant for the Solar System Generator in the future...
    48.     /*public GeneratePlanet(string name, int lod, int radius)
    49.     {
    50.         planet = new QuadSphere(name, lod, radius);
    51.     }*/
    52. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class QuadSphere
    5. {
    6.     GameObject      planet;
    7.     QuadSurface[]   surfaces;
    8.  
    9.     public QuadSphere(string name, int lod, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures)
    10.     {
    11.         //Initialize the array
    12.         surfaces = new QuadSurface[6];
    13.  
    14.         //Create a new new QuadSurface using the specified LOD.
    15.         for (int i = 0; i < 6; i++)
    16.         {
    17.             //SUB-D 0
    18.             surfaces[i] = new QuadSurface(lod, 0, 0, 0, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, (quadFace)i);
    19.         }
    20.  
    21.         //Testing...
    22.         //SUB-D 1
    23.         surfaces[0].SubDivide();
    24.         //SUB-D 2
    25.         surfaces[0].tree[0].SubDivide();
    26.         surfaces[0].tree[2].SubDivide();
    27.         //SUB-D 3
    28.         surfaces[0].tree[2].tree[1].SubDivide();
    29.         //SUB-D 4
    30.         surfaces[0].tree[2].tree[1].tree[3].SubDivide();
    31.  
    32.             planet = new GameObject(name);
    33.         for (int i = 0; i < surfaces.Length; i++)
    34.             surfaces[i].surface.transform.parent = planet.transform;
    35.     }
    36. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class QuadSurface
    5. {
    6.     public QuadSurface   root;
    7.     public QuadSurface[] tree;
    8.    
    9.     public GameObject   surface  { get; private set; }
    10.     public MeshRenderer renderer { get; private set; }
    11.     public MeshCollider collider { get; private set; }
    12.     public MeshFilter   filter   { get; private set; }
    13.  
    14.     private int         LEVELOFDETAIL, SUBDIVISIONS, XINDEX, ZINDEX;
    15.     private float       WATERLEVEL, RADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT;
    16.     private Texture2D[] TEXTURES;
    17.     private quadFace    QUADFACE;
    18.  
    19.     public bool[] neighbors;
    20.     public bool   generated = false;
    21.     public bool   finalized = false;
    22.  
    23.     //Vertex Colors Testing
    24.     GameObject     planetGenerator;
    25.     GeneratePlanet generator;
    26.     Material       planetMaterial;
    27.     Color[]        planetColors;
    28.     Gradient       planetGradient;
    29.  
    30.     public QuadSurface(QuadSurface root, int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    31.     {
    32.         InitializeSurfaceGenerator(root, lod, subDivisions, xIndex, zIndex, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, face);
    33.     }
    34.  
    35.     public QuadSurface(int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    36.     {
    37.         InitializeSurfaceGenerator(null, lod, subDivisions, xIndex, zIndex, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, face);
    38.     }
    39.  
    40.     void InitializeSurfaceGenerator(QuadSurface root, int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    41.     {
    42.         LEVELOFDETAIL = lod;
    43.         SUBDIVISIONS  = subDivisions;
    44.         XINDEX        = xIndex;
    45.         ZINDEX        = zIndex;
    46.         WATERLEVEL    = waterLevel;
    47.         RADIUS        = radius;
    48.         NOISEOFFSET   = noiseOffset;
    49.         OCTAVES       = octaves;
    50.         NOISESCALE    = noiseScale;
    51.         WORLDHEIGHT   = worldHeight;
    52.         TEXTURES      = textures;
    53.         QUADFACE      = face;
    54.  
    55.         //Vertex Colors Testing
    56.         planetGenerator = GameObject.Find("PlanetGenerator");
    57.         generator       = planetGenerator.GetComponent<GeneratePlanet>();
    58.         planetMaterial  = generator.vertexColors;
    59.         planetGradient  = generator.PLANETCOLORS;
    60.  
    61.  
    62.         GenerateSurfaceCoordinates(root, subDivisions, xIndex, zIndex, face);
    63.     }
    64.  
    65.     void GenerateSurfaceCoordinates(QuadSurface root, int subDivisions, int xIndex, int zIndex, quadFace face)
    66.     {
    67.         //The base number of faces is 2, which means we need +1 vert to make that happen.
    68.         int vertsPerQuad = (int)Mathf.Pow(2, LEVELOFDETAIL) + 1;
    69.  
    70.         //Appropriately size (and create) the Vertex Array
    71.         Vector3[] vertices = new Vector3[vertsPerQuad * vertsPerQuad];
    72.         //Appropriately size (and create) the Normal Array
    73.         Vector3[] normals = new Vector3[vertices.Length];      
    74.         //Appropriately size (and create) the UV Array
    75.         Vector2[] uvs = new Vector2[vertices.Length];
    76.         //Vertex Colors Testing
    77.         Color[] colors = new Color[vertices.Length];
    78.         planetColors = colors;
    79.  
    80.         //Calculate the increment to keep the vertices constrained to a 1x1 cube.
    81.         float increment     = ((1.0f / Mathf.Pow(2, LEVELOFDETAIL)) / Mathf.Pow(2, subDivisions));
    82.         float uvIncrement   = (1.0f / Mathf.Pow(2, LEVELOFDETAIL)) / Mathf.Pow(2, subDivisions);
    83.  
    84.         for (int i = 0, index = 0; i < vertsPerQuad; i++)
    85.         {
    86.             for (int j = 0; j < vertsPerQuad; j++, index++)
    87.             {
    88.                 //Vertex Coordinates
    89.                 float xPos = (float)j * increment - 0.5f + ((float)xIndex / Mathf.Pow(2, subDivisions));
    90.                 float yPos = 0.5f;
    91.                 float zPos = (float)i * increment - 0.5f + ((float)zIndex / Mathf.Pow(2, subDivisions));
    92.                 //UV Coordinates
    93.                 float xUV = (float)j * uvIncrement + ((float)xIndex / Mathf.Pow(2, subDivisions));
    94.                 float zUV = (float)i * uvIncrement + ((float)zIndex / Mathf.Pow(2, subDivisions));
    95.  
    96.                 switch (face)
    97.                 {
    98.                     case quadFace.TOP:
    99.                         //Assign Vertex Coordinates
    100.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    101.                         break;
    102.  
    103.                     case quadFace.BOTTOM:
    104.                         //Assign Vertex Coordinates
    105.                         vertices[index] = new Vector3(xPos, -yPos, -zPos);
    106.                         break;
    107.  
    108.                     case quadFace.LEFT:
    109.                         //Assign Vertex Coordinates
    110.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    111.                         break;
    112.  
    113.                     case quadFace.RIGHT:
    114.                         //Assign Vertex Coordinates
    115.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    116.                         break;
    117.  
    118.                     case quadFace.FRONT:
    119.                         //Assign Vertex Coordinates
    120.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    121.                         break;
    122.  
    123.                     case quadFace.BACK:
    124.                         //Assign Vertex Coordinates
    125.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    126.                         break;
    127.                 }
    128.                 //Assign UV Coordinates
    129.                 uvs[index] = new Vector2(xUV, zUV);
    130.  
    131.                 //Spherify the vertices!
    132.                 vertices[index] = PlanetUtilities.SmoothVertices(vertices[index]);
    133.  
    134.                 float noise = PlanetUtilities.GetNoise(vertices[index], NOISEOFFSET, OCTAVES, NOISESCALE);
    135.                 //Vertex Colors Testing
    136.                 colors[index] = planetGradient.Evaluate(noise);
    137.                
    138.                 //Keep the oceans flat! - thanks alucardj!
    139.                 if (noise > WATERLEVEL)
    140.                     noise = (noise - WATERLEVEL) * (1.0f / (1.0f - WATERLEVEL));
    141.                 else
    142.                     noise = 0.0f;
    143.  
    144.                 vertices[index] *= RADIUS + (noise * WORLDHEIGHT);
    145.  
    146.                 normals[index] = vertices[index].normalized;
    147.             }
    148.         }
    149.         //Create the Surface Object
    150.         CreateSurfaceObject(root, xIndex, zIndex, face, vertsPerQuad, vertices, normals, uvs);
    151.     }
    152.  
    153.     void CreateSurfaceObject(QuadSurface root, int xIndex, int zIndex, quadFace face, int vertsPerQuad, Vector3[] vertexArray, Vector3[] normalArray, Vector2[] uvArray)
    154.     {
    155.         this.root = root;
    156.  
    157.         //Create the game object to which we'll attach everything to
    158.         surface = new GameObject("SUB-D: " + SUBDIVISIONS + " Face: " + face + " (" + xIndex + ", " + zIndex + ")");
    159.         //Add the mesh Renderer
    160.         renderer = surface.AddComponent<MeshRenderer>();
    161.         //Add the MeshCollider
    162.         collider = surface.AddComponent<MeshCollider>();
    163.         //Add the MeshFilter
    164.         filter   = surface.AddComponent<MeshFilter>();
    165.  
    166.         //Initialize the renderer
    167.         renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    168.         renderer.receiveShadows = true;
    169.         renderer.enabled = true;
    170.  
    171.         if (generator.USEVERTEXCOLORS == false)
    172.             //Assign the generated textures to the quadSphere
    173.             surface.GetComponent<Renderer>().material.mainTexture = TEXTURES[(int)face];
    174.         else
    175.             //Vertex Colors testing
    176.             surface.GetComponent<Renderer>().material = planetMaterial;
    177.  
    178.         if (root != null)
    179.             surface.transform.parent = root.surface.transform;
    180.  
    181.         CreateSurfaceMesh(face, vertsPerQuad, vertexArray, normalArray, uvArray);
    182.     }
    183.  
    184.     void CreateSurfaceMesh(quadFace face, int vertsPerQuad, Vector3[] vertexArray, Vector3[] normalArray, Vector2[] uvArray)
    185.     {
    186.         //If this has already been generated, enable the renderer
    187.         if (generated)
    188.         {
    189.             renderer.enabled = true;
    190.             if (root != null)
    191.                 root.collider.enabled = false;
    192.             collider.enabled = true;
    193.             return;
    194.         }
    195.  
    196.         int vertBufferSize = vertsPerQuad - 1;
    197.         //Size the vertex buffer
    198.         int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    199.         //Create a new Mesh object
    200.         Mesh surfaceMesh;
    201.         //Create a new Mesh using the Objects MeshFilter
    202.         surfaceMesh = surface.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    203.         surfaceMesh.name = surface.name;
    204.  
    205.         //Step through the Vertex Buffer
    206.         for (int triIndex = 0, vertIndex = 0, i = 0; i < vertBufferSize; i++, vertIndex++)
    207.         {
    208.             for (int j = 0; j < vertBufferSize; j++, triIndex += 6, vertIndex++)
    209.             {
    210.                 //  1
    211.                 //  | \
    212.                 //  |  \
    213.                 //  |   \
    214.                 //  0----2
    215.                 vertexBuffer[triIndex] = vertIndex;
    216.                 vertexBuffer[triIndex + 1] = vertIndex + vertsPerQuad;
    217.                 vertexBuffer[triIndex + 2] = vertIndex + 1;
    218.                 //  1----3
    219.                 //    \  |
    220.                 //     \ |
    221.                 //      \|
    222.                 //       2
    223.                 vertexBuffer[triIndex + 3] = vertIndex + vertsPerQuad;
    224.                 vertexBuffer[triIndex + 4] = vertIndex + vertsPerQuad + 1;
    225.                 vertexBuffer[triIndex + 5] = vertIndex + 1;
    226.             }
    227.         }
    228.         //Assign the Vertex Array from Generate Surface Coordinates to the Mesh Vertex Array
    229.         surfaceMesh.vertices = vertexArray;
    230.         //Assign the UV Coordinates
    231.         surfaceMesh.uv = uvArray;
    232.         //Assign the Vertex Buffer
    233.         surfaceMesh.triangles = vertexBuffer;
    234.         //Vertex Colors Testing
    235.         surfaceMesh.colors = planetColors;
    236.         //Recalculate the Normals
    237.         //surfaceMesh.RecalculateNormals();
    238.         surfaceMesh.normals = normalArray; //Done manually...
    239.         //Recalculate Bounds
    240.         surfaceMesh.RecalculateBounds();
    241.         //After the Mesh has been created, pass it back to the MeshCollider
    242.         surface.GetComponent<MeshCollider>().sharedMesh = surfaceMesh;
    243.  
    244.         //If root is ever null...Destroy!
    245.         if (root != null)
    246.             root.Destroy();
    247.  
    248.         generated = true;
    249.     }
    250.  
    251.     public void Destroy()
    252.     {
    253.         renderer.enabled = false;
    254.         generated = false;
    255.     }
    256.  
    257.     //Merges our quadtrees!
    258.     public void MergeQuadTree()
    259.     {
    260.         if (tree == null)
    261.             return;
    262.  
    263.         for (int i = 0; i < tree.Length; i++)
    264.         {
    265.             tree[i].MergeQuadTree();
    266.             tree[i].Destroy();
    267.         }
    268.  
    269.         tree = null;
    270.     }
    271.  
    272.     public void SubDivide()
    273.     {
    274.         tree = new QuadSurface[4];
    275.  
    276.         for (int i = 0, index = 0; i < 2; i++)
    277.         {
    278.             for (int j = 0; j < 2; j++, index++)
    279.             {
    280.                 //This is the line...makes perfect sense after I wrote it....
    281.                 tree[index] = new QuadSurface(this, LEVELOFDETAIL, this.SUBDIVISIONS + 1, i + (this.XINDEX * 2), j + (this.ZINDEX * 2), WATERLEVEL, RADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT, TEXTURES, QUADFACE);
    282.             }
    283.         }
    284.  
    285.         Destroy();
    286.     }
    287. }

    Code (CSharp):
    1. //SOURCE: alucardj
    2. //http://forum.unity3d.com/conversations/procedural-textures.465272/
    3.  
    4. using UnityEngine;
    5. using System.Collections;
    6. using System.Collections.Generic;
    7.  
    8. public class TextureGenerator
    9. {
    10.     public Texture2D[] surfaceTextures { get; private set; }
    11.  
    12.     private int   textureSize;
    13.     private float noiseOffset, octaves, noiseScale;
    14.     private Gradient planetColors;
    15.  
    16.     public TextureGenerator(int size, float offset, float oct, float scale, Gradient colors)
    17.     {
    18.         textureSize  = size;
    19.         noiseOffset  = offset;
    20.         octaves      = oct;
    21.         noiseScale   = scale;
    22.         planetColors = colors;
    23.  
    24.         surfaceTextures = new Texture2D[6];
    25.  
    26.         for (int i = 0; i < 6; i++ )
    27.             GenerateSurfaceTextures((quadFace)i);
    28.     }
    29.  
    30.     void GenerateSurfaceTextures(quadFace face)
    31.     {
    32.         Texture2D texture = new Texture2D(textureSize, textureSize);
    33.  
    34.         //Create the Color List that will be applied to the texture
    35.         List<Color> colorList = new List<Color>();
    36.         Color color;
    37.  
    38.         //Appropriately size (and create) the Vertex Array
    39.         Vector3[] vertices = new Vector3[textureSize * textureSize];
    40.  
    41.         // Calculate the increment to keep the vertices constrained to a 1x1 cube.
    42.         float increment = 1.0f / ((float)textureSize - 1);
    43.  
    44.         for (int i = 0, index = 0; i < textureSize; i++)
    45.         {
    46.             for (int j = 0; j < textureSize; j++, index++)
    47.             {
    48.                 // Vertex Coordinates
    49.                 float xPos = (float)j * increment - 0.5f;
    50.                 float yPos = 0.5f;
    51.                 float zPos = (float)i * increment - 0.5f;
    52.  
    53.                 // Assign Vertex Coordinates
    54.                 switch (face)
    55.                 {
    56.                     case quadFace.TOP:
    57.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    58.                         break;
    59.  
    60.                     case quadFace.BOTTOM:
    61.                         vertices[index] = new Vector3(xPos, -yPos, -zPos);
    62.                         break;
    63.  
    64.                     case quadFace.LEFT:
    65.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    66.                         break;
    67.  
    68.                     case quadFace.RIGHT:
    69.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    70.                         break;
    71.  
    72.                     case quadFace.FRONT:
    73.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    74.                         break;
    75.  
    76.                     case quadFace.BACK:
    77.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    78.                         break;
    79.                 }
    80.  
    81.                 //Spherify the Vertices!
    82.                 vertices[index] = PlanetUtilities.SmoothVertices(vertices[index]);
    83.  
    84.                 //Get noise!
    85.                 float noise = PlanetUtilities.GetNoise(vertices[index], noiseOffset, octaves, noiseScale);
    86.  
    87.                 // get Colour
    88.                 /*if (useGreyscale)
    89.                 {
    90.                     colour = new Color(noise, noise, noise, 1f);
    91.                 }
    92.                 else
    93.                 {
    94.                     colour = planetColours.Evaluate(noise);
    95.                 }*/
    96.  
    97.                 color = planetColors.Evaluate(noise);
    98.  
    99.                 // Add Colour to Texture Colour Array
    100.                 colorList.Add(color);
    101.             }
    102.         }
    103.  
    104.         // Apply Texture Colour Array to the Texture
    105.         texture.SetPixels(colorList.ToArray());
    106.         texture.wrapMode = TextureWrapMode.Clamp;
    107.         texture.Apply();
    108.  
    109.         surfaceTextures[(int)face] = texture;
    110.     }
    111. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public enum quadFace { TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK }
    5.  
    6. public static class PlanetUtilities
    7. {
    8.     //From Catlike Coding!
    9.     public static Vector3 SmoothVertices(Vector3 vertex)
    10.     {
    11.         Vector3 v = vertex * 2f;
    12.         float x2 = v.x * v.x;
    13.         float y2 = v.y * v.y;
    14.         float z2 = v.z * v.z;
    15.  
    16.         Vector3 s;
    17.         s.x = v.x * Mathf.Sqrt(1f - y2 / 2f - z2 / 2f + y2 * z2 / 3f);
    18.         s.y = v.y * Mathf.Sqrt(1f - x2 / 2f - z2 / 2f + x2 * z2 / 3f);
    19.         s.z = v.z * Mathf.Sqrt(1f - x2 / 2f - y2 / 2f + x2 * y2 / 3f);
    20.  
    21.         return s;
    22.     }
    23.  
    24.     // using a Simplex Noise library :
    25.     //  http://cabbynode.net/2012/06/10/perlin-simplex-noise-for-c-and-xna/
    26.  
    27.     public static float GetNoise(Vector3 vertex, float noiseOffset, float octaves, float noiseScale)
    28.     {
    29.         // add offset
    30.         vertex.x += noiseOffset;
    31.         vertex.y += noiseOffset;
    32.         vertex.z += noiseOffset;
    33.  
    34.         // fractal noise
    35.         float amp = 1f;
    36.         float noise = 0f;
    37.         float gain = 1f;
    38.         float factor = 0f;
    39.         Vector3 calcVert;
    40.  
    41.         for (int i = 0; i < octaves; i++)
    42.         {
    43.             factor += 1f / gain;
    44.             calcVert = vertex * noiseScale * gain;
    45.             noise += Noise.Noise.GetNoise(calcVert.x, calcVert.y, calcVert.z) * (amp / gain);
    46.             gain *= 2f;
    47.         }
    48.  
    49.         // normalize noise by octave iteration factor
    50.         noise /= factor;
    51.  
    52.         return noise;
    53.     }
    54.  
    55.     public static Gradient CreateColourGradient(Gradient planetColors)
    56.     {
    57.         // using colour scale from LibNoise example : http://libnoise.sourceforge.net/tutorials/tutorial3.html
    58.         //renderer.AddGradientPoint (-1.0000, utils::Color (  0,  0, 128, 255)); // deeps
    59.         //renderer.AddGradientPoint (-0.2500, utils::Color (  0,  0, 255, 255)); // shallow
    60.         //renderer.AddGradientPoint ( 0.0000, utils::Color (  0, 128, 255, 255)); // shore
    61.         //renderer.AddGradientPoint ( 0.0625, utils::Color (240, 240,  64, 255)); // sand
    62.         //renderer.AddGradientPoint ( 0.1250, utils::Color ( 32, 160,  0, 255)); // grass
    63.         //renderer.AddGradientPoint ( 0.3750, utils::Color (224, 224,  0, 255)); // dirt
    64.         //renderer.AddGradientPoint ( 0.7500, utils::Color (128, 128, 128, 255)); // rock
    65.         //renderer.AddGradientPoint ( 1.0000, utils::Color (255, 255, 255, 255)); // snow
    66.  
    67.         planetColors = new Gradient();
    68.  
    69.         GradientColorKey[] gck = new GradientColorKey[8];
    70.         gck[0].color = CalculateGradientColour(0, 0, 128);
    71.         gck[0].time = CalculateGradientTime(-1.0000);
    72.         gck[1].color = CalculateGradientColour(0, 0, 255);
    73.         gck[1].time = CalculateGradientTime(-0.2500);
    74.         gck[2].color = CalculateGradientColour(0, 128, 255);
    75.         gck[2].time = CalculateGradientTime(0.0000);
    76.         gck[3].color = CalculateGradientColour(240, 240, 64);
    77.         gck[3].time = CalculateGradientTime(0.0625);
    78.         gck[4].color = CalculateGradientColour(32, 160, 0);
    79.         gck[4].time = CalculateGradientTime(0.1250);
    80.         gck[5].color = CalculateGradientColour(224, 224, 0);
    81.         gck[5].time = CalculateGradientTime(0.3750);
    82.         gck[6].color = CalculateGradientColour(128, 128, 128);
    83.         gck[6].time = CalculateGradientTime(0.7500);
    84.         gck[7].color = CalculateGradientColour(255, 255, 255);
    85.         gck[7].time = CalculateGradientTime(1.0000);
    86.  
    87.         GradientAlphaKey[] gak = new GradientAlphaKey[2];
    88.         gak[0].alpha = 1f;
    89.         gak[0].time = 0f;
    90.         gak[1].alpha = 1f;
    91.         gak[1].time = 1f;
    92.  
    93.         planetColors.SetKeys(gck, gak);
    94.  
    95.         return planetColors;
    96.     }
    97.  
    98.  
    99.     static Color CalculateGradientColour(int r, int g, int b)
    100.     {
    101.         return new Color((float)r / 255f, (float)g / 255f, (float)b / 255f);
    102.     }
    103.  
    104.  
    105.     static float CalculateGradientTime(double t)
    106.     {
    107.         return (float)((t + 1) * 0.5);
    108.     }
    109. }
     
  4. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Quickie update...Water shell testing.

    Basically the radius of the shell is equal to:

    World Radius + ( ( (World Height * Noise Scale) - 1 ) * (Water Level / 0.5) )

    I've scaled the planet up all kinds of ways and that little equation has worked perfectly. So now there's undersea ares too!

    Edited because I was missing the water level bit.
     

    Attached Files:

    Last edited: Sep 4, 2016
  5. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    WARNING: THIS CODE WILL CAUSE UNITY TO LOCK UP

    So now that that's out of the way, here is the first pass of getting the dynamic LOD system working. Right now I'm just focused on getting it to work when you move closer to and farther away from the planet.

    Here's what works...when you move closer to the planet, it subdivides...the problem is that it does it infinitely and things kind of freak out and lock up unity....BE VERY CAREFUL MOVING THE PLAYER TAGGED OBJECT CLOSER TO THE PLANET!

    I need an extra set of eyes to see what I'm missing or what I've done wrong. Here's the most up to date version!

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class GeneratePlanet : MonoBehaviour
    5. {
    6.     [Header("Planet Info")]
    7.     [Range(3.0f, 7.0f)] public int   LOD = 3;
    8.     [Range(0.0f, 1.0f)] public float WATERLEVEL = 0.5f;
    9.     public float PLANETRADIUS = 1; //m, will eventually be km
    10.     public float WORLDHEIGHT = 0.1f;
    11.     [Header("Texture and Noise Variables")]
    12.     public int   TEXTURESIZE = 256;
    13.     public float NOISESCALE = 1.45f;
    14.     public float NOISEOFFSET = 1.56f;
    15.     public int   OCTAVES = 8;
    16.     [Header("Planet Colors")]
    17.     public Gradient PLANETCOLORS;
    18.     [Header("Test")]
    19.     public bool USEVERTEXCOLORS = false;
    20.     public Material vertexColors;
    21.  
    22.  
    23.     //The planet!
    24.     private QuadSphere planet;
    25.     //Textures!
    26.     private TextureGenerator textureGenerator;
    27.     private Texture2D[]      textures;
    28.  
    29.     void Start()
    30.     {
    31.         //Generate textures!
    32.         textureGenerator = new TextureGenerator(TEXTURESIZE,NOISEOFFSET, OCTAVES, NOISESCALE, PLANETCOLORS);
    33.         //Assign the textures created above to the local textures to make passing them along easier...
    34.         textures = textureGenerator.surfaceTextures;
    35.  
    36.         planet = new QuadSphere("Planet1", LOD, WATERLEVEL, PLANETRADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT, textures);
    37.     }
    38.  
    39.     void Update()
    40.     {
    41.         for (int i = 0; i < 6; i++)
    42.             StartCoroutine(planet.Update(i));
    43.     }
    44. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class QuadSphere
    6. {
    7.     GameObject      planet;
    8.     GeneratePlanet  generator;
    9.     QuadSurface[]   surfaces;
    10.  
    11.     private int   VERTSPERQUAD;
    12.     private float RADIUS;
    13.  
    14.     public QuadSphere(string name, int lod, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures)
    15.     {
    16.         this.VERTSPERQUAD = (int)Mathf.Pow(2, lod) + 1;
    17.         this.RADIUS       = radius;
    18.         //Initialize the array
    19.         surfaces = new QuadSurface[6];
    20.  
    21.         //Create a new new QuadSurface using the specified LOD.
    22.         for (int i = 0; i < 6; i++)
    23.         {
    24.             //Sub Divisions 0, Indices = 0
    25.             surfaces[i] = new QuadSurface(lod, 0, 0, 0, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, (quadFace)i);
    26.         }
    27.  
    28.         planet = new GameObject(name);
    29.         generator = planet.GetComponent<GeneratePlanet>();
    30.  
    31.         for (int i = 0; i < surfaces.Length; i++)
    32.             surfaces[i].surface.transform.parent = planet.transform;
    33.     }
    34.  
    35.     private void FindActiveQuadTreeSurfaces(QuadSurface surface, ref List<QuadSurface> activeSurfaces)
    36.     {
    37.         if (surface.tree == null)
    38.             activeSurfaces.Add(surface);
    39.         else
    40.             for (int i = 0; i < surface.tree.Length; i++)
    41.                 FindActiveQuadTreeSurfaces(surface.tree[i], ref activeSurfaces);
    42.     }
    43.  
    44.     public IEnumerator Update(int surface)
    45.     {
    46.         GameObject player = GameObject.FindGameObjectWithTag("Player");
    47.         if (player == null)
    48.             yield break;
    49.  
    50.         List<QuadSurface> activeTree = new List<QuadSurface>();
    51.         FindActiveQuadTreeSurfaces(surfaces[surface], ref activeTree);
    52.  
    53.         for (int i = 0; i < activeTree.Count; i++)
    54.         {
    55.             float dist = Vector3.Distance(player.transform.position, activeTree[i].surfaceMesh.bounds.ClosestPoint(player.transform.position));
    56.  
    57.             if (dist > activeTree[i].RADIUS)
    58.             {
    59.                 //Debug.Log("The player is: " + dist + " from the Planet.");
    60.                 if (dist <= activeTree[i].RADIUS * 1000.0f || activeTree[i].ROOT == null)
    61.                 {
    62.                     activeTree[i].MergeQuadTree();
    63.                     activeTree[i].GenerateSurfaceCoordinates();
    64.                 }
    65.                 else
    66.                 {
    67.                     activeTree[i].MergeQuadTree();
    68.                     activeTree[i].ROOT.MergeQuadTree();
    69.                     activeTree[i].ROOT.GenerateSurfaceCoordinates();
    70.                 }
    71.             }
    72.             else if(activeTree[i].RADIUS > VERTSPERQUAD)
    73.             {
    74.                 if (activeTree[i].SUBDIVISIONS <= 6)
    75.                     activeTree[i].SubDivide();
    76.                 else
    77.                     activeTree[i].SUBDIVISIONS = 6;
    78.             }
    79.             else
    80.             {
    81.                 if (activeTree[i].surface.tag != "planet")
    82.                     activeTree[i].surface.tag = "Planet";
    83.             }
    84.         }
    85.     }
    86. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class QuadSurface
    5. {
    6.     public QuadSurface   ROOT;
    7.     public QuadSurface[] tree;  
    8.  
    9.     public GameObject   surface     { get; private set; }
    10.     public MeshRenderer renderer    { get; private set; }
    11.     public MeshCollider collider    { get; private set; }
    12.     public MeshFilter   filter      { get; private set; }
    13.     public Mesh         surfaceMesh { get; private set; }
    14.  
    15.     public int         LEVELOFDETAIL, SUBDIVISIONS, XINDEX, ZINDEX;
    16.     public float        WATERLEVEL, RADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT;
    17.     private Texture2D[] TEXTURES;
    18.     private quadFace    QUADFACE;
    19.  
    20.     public bool[] neighbors;
    21.     public bool   generated = false;
    22.     public bool   finalized = false;
    23.  
    24.     //Vertex Colors Testing
    25.     GameObject     planetGenerator;
    26.     GeneratePlanet generator;
    27.     Material       planetMaterial;
    28.     Color[]        planetColors;
    29.     Gradient       planetGradient;
    30.  
    31.     public QuadSurface(QuadSurface root, int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    32.     {
    33.         InitializeSurfaceGenerator(root, lod, subDivisions, xIndex, zIndex, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, face);
    34.     }
    35.  
    36.     public QuadSurface(int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    37.     {
    38.         InitializeSurfaceGenerator(null, lod, subDivisions, xIndex, zIndex, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, face);
    39.     }
    40.  
    41.     void InitializeSurfaceGenerator(QuadSurface root, int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    42.     {
    43.         this.ROOT = root;
    44.         this.LEVELOFDETAIL = lod;
    45.         this.SUBDIVISIONS  = subDivisions;
    46.         this.XINDEX        = xIndex;
    47.         this.ZINDEX = zIndex;
    48.         this.WATERLEVEL = waterLevel;
    49.         this.RADIUS = radius;
    50.         this.NOISEOFFSET = noiseOffset;
    51.         this.OCTAVES = octaves;
    52.         this.NOISESCALE = noiseScale;
    53.         this.WORLDHEIGHT = worldHeight;
    54.         this.TEXTURES = textures;
    55.         this.QUADFACE = face;
    56.  
    57.         //Vertex Colors Testing
    58.         planetGenerator = GameObject.Find("PlanetGenerator");
    59.         generator       = planetGenerator.GetComponent<GeneratePlanet>();
    60.         planetMaterial  = generator.vertexColors;
    61.         planetGradient  = generator.PLANETCOLORS;
    62.  
    63.         //Create the game object to which we'll attach everything to
    64.         surface = new GameObject("SUB-D: " + SUBDIVISIONS + " Face: " + QUADFACE + " (" + XINDEX + ", " + ZINDEX + ")");
    65.         //Add the mesh Renderer
    66.         renderer = surface.AddComponent<MeshRenderer>();
    67.         //Add the MeshCollider
    68.         collider = surface.AddComponent<MeshCollider>();
    69.         //Add the MeshFilter
    70.         filter = surface.AddComponent<MeshFilter>();
    71.  
    72.         //Initialize the renderer
    73.         renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    74.         renderer.receiveShadows = true;
    75.         renderer.enabled = true;
    76.  
    77.         if (generator.USEVERTEXCOLORS == false)
    78.             //Assign the generated textures to the quadSphere
    79.             surface.GetComponent<Renderer>().material.mainTexture = TEXTURES[(int)QUADFACE];
    80.         else
    81.             //Vertex Colors testing
    82.             surface.GetComponent<Renderer>().material = planetMaterial;
    83.  
    84.         if (ROOT != null)
    85.             surface.transform.parent = ROOT.surface.transform;
    86.  
    87.         GenerateSurfaceCoordinates();
    88.     }
    89.  
    90.     public void GenerateSurfaceCoordinates()
    91.     {
    92.         //The base number of faces is 2, which means we need +1 vert to make that happen.
    93.         int vertsPerQuad = (int)Mathf.Pow(2, LEVELOFDETAIL) + 1;
    94.  
    95.         //Appropriately size (and create) the Vertex Array
    96.         Vector3[] vertices = new Vector3[vertsPerQuad * vertsPerQuad];
    97.         //Appropriately size (and create) the Normal Array
    98.         Vector3[] normals = new Vector3[vertices.Length];      
    99.         //Appropriately size (and create) the UV Array
    100.         Vector2[] uvs = new Vector2[vertices.Length];
    101.         //Vertex Colors Testing
    102.         Color[] colors = new Color[vertices.Length];
    103.         planetColors = colors;
    104.  
    105.         //Calculate the increment to keep the vertices constrained to a 1x1 cube.
    106.         float increment     = ((1.0f / Mathf.Pow(2, LEVELOFDETAIL)) / Mathf.Pow(2, SUBDIVISIONS));
    107.         float uvIncrement   = (1.0f / Mathf.Pow(2, LEVELOFDETAIL)) / Mathf.Pow(2, SUBDIVISIONS);
    108.  
    109.         for (int i = 0, index = 0; i < vertsPerQuad; i++)
    110.         {
    111.             for (int j = 0; j < vertsPerQuad; j++, index++)
    112.             {
    113.                 //Vertex Coordinates
    114.                 float xPos = (float)j * increment - 0.5f + ((float)XINDEX / Mathf.Pow(2, SUBDIVISIONS));
    115.                 float yPos = 0.5f;
    116.                 float zPos = (float)i * increment - 0.5f + ((float)ZINDEX / Mathf.Pow(2, SUBDIVISIONS));
    117.                 //UV Coordinates
    118.                 float xUV = (float)j * uvIncrement + ((float)XINDEX / Mathf.Pow(2, SUBDIVISIONS));
    119.                 float zUV = (float)i * uvIncrement + ((float)ZINDEX / Mathf.Pow(2, SUBDIVISIONS));
    120.  
    121.                 switch (QUADFACE)
    122.                 {
    123.                     case quadFace.TOP:
    124.                         //Assign Vertex Coordinates
    125.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    126.                         break;
    127.  
    128.                     case quadFace.BOTTOM:
    129.                         //Assign Vertex Coordinates
    130.                         vertices[index] = new Vector3(xPos, -yPos, -zPos);
    131.                         break;
    132.  
    133.                     case quadFace.LEFT:
    134.                         //Assign Vertex Coordinates
    135.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    136.                         break;
    137.  
    138.                     case quadFace.RIGHT:
    139.                         //Assign Vertex Coordinates
    140.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    141.                         break;
    142.  
    143.                     case quadFace.FRONT:
    144.                         //Assign Vertex Coordinates
    145.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    146.                         break;
    147.  
    148.                     case quadFace.BACK:
    149.                         //Assign Vertex Coordinates
    150.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    151.                         break;
    152.                 }
    153.                 //Assign UV Coordinates
    154.                 uvs[index] = new Vector2(xUV, zUV);
    155.  
    156.                 //Spherify the vertices!
    157.                 vertices[index] = PlanetUtilities.SmoothVertices(vertices[index]);
    158.  
    159.                 float noise = PlanetUtilities.GetNoise(vertices[index], NOISEOFFSET, OCTAVES, NOISESCALE);
    160.                 //Vertex Colors Testing
    161.                 colors[index] = planetGradient.Evaluate(noise);
    162.                
    163.                 //Keep the oceans flat! - thanks alucardj!
    164.                 if (noise > WATERLEVEL)
    165.                     noise = (noise - WATERLEVEL) * (1.0f / (1.0f - WATERLEVEL));
    166.                 else
    167.                     noise = 0.0f;
    168.  
    169.                 vertices[index] *= RADIUS + (noise * WORLDHEIGHT);
    170.  
    171.                 normals[index] = vertices[index].normalized;
    172.             }
    173.         }
    174.         //Create the Surface Object
    175.         CreateSurfaceMesh(vertsPerQuad, vertices, normals, uvs);
    176.     }
    177.  
    178.     void CreateSurfaceMesh(int vertsPerQuad, Vector3[] vertexArray, Vector3[] normalArray, Vector2[] uvArray)
    179.     {
    180.         //If this has already been generated, enable the renderer
    181.         if (generated)
    182.         {
    183.             renderer.enabled = true;
    184.             if (ROOT != null)
    185.                 ROOT.collider.enabled = false;
    186.             collider.enabled = true;
    187.             return;
    188.         }
    189.  
    190.         int vertBufferSize = vertsPerQuad - 1;
    191.         //Size the vertex buffer
    192.         int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    193.  
    194.         //Create a new Mesh using the Objects MeshFilter
    195.         surfaceMesh = surface.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    196.         surfaceMesh.name = surface.name;
    197.  
    198.         //Step through the Vertex Buffer
    199.         for (int triIndex = 0, vertIndex = 0, i = 0; i < vertBufferSize; i++, vertIndex++)
    200.         {
    201.             for (int j = 0; j < vertBufferSize; j++, triIndex += 6, vertIndex++)
    202.             {
    203.                 //  1
    204.                 //  | \
    205.                 //  |  \
    206.                 //  |   \
    207.                 //  0----2
    208.                 vertexBuffer[triIndex] = vertIndex;
    209.                 vertexBuffer[triIndex + 1] = vertIndex + vertsPerQuad;
    210.                 vertexBuffer[triIndex + 2] = vertIndex + 1;
    211.                 //  1----3
    212.                 //    \  |
    213.                 //     \ |
    214.                 //      \|
    215.                 //       2
    216.                 vertexBuffer[triIndex + 3] = vertIndex + vertsPerQuad;
    217.                 vertexBuffer[triIndex + 4] = vertIndex + vertsPerQuad + 1;
    218.                 vertexBuffer[triIndex + 5] = vertIndex + 1;
    219.             }
    220.         }
    221.         //Assign the Vertex Array from Generate Surface Coordinates to the Mesh Vertex Array
    222.         surfaceMesh.vertices = vertexArray;
    223.         //Assign the UV Coordinates
    224.         surfaceMesh.uv = uvArray;
    225.         //Assign the Vertex Buffer
    226.         surfaceMesh.triangles = vertexBuffer;
    227.         //Vertex Colors Testing
    228.         surfaceMesh.colors = planetColors;
    229.         //Recalculate the Normals
    230.         //surfaceMesh.RecalculateNormals();
    231.         surfaceMesh.normals = normalArray; //Done manually...
    232.         //Recalculate Bounds
    233.         surfaceMesh.RecalculateBounds();
    234.         //After the Mesh has been created, pass it back to the MeshCollider
    235.         surface.GetComponent<MeshCollider>().sharedMesh = surfaceMesh;
    236.  
    237.         //If root is ever null...Destroy!
    238.         if (ROOT != null)
    239.             ROOT.Destroy();
    240.  
    241.         generated = true;
    242.     }
    243.  
    244.     public void Destroy()
    245.     {
    246.         renderer.enabled = false;
    247.         generated = false;
    248.     }
    249.  
    250.     //Merges our quadtrees!
    251.     public void MergeQuadTree()
    252.     {
    253.         if (tree == null)
    254.             return;
    255.  
    256.         for (int i = 0; i < tree.Length; i++)
    257.         {
    258.             tree[i].MergeQuadTree();
    259.             tree[i].Destroy();
    260.         }
    261.  
    262.         tree = null;
    263.     }
    264.  
    265.     public void SubDivide()
    266.     {
    267.         tree = new QuadSurface[4];
    268.  
    269.         for (int i = 0, index = 0; i < 2; i++)
    270.         {
    271.             for (int j = 0; j < 2; j++, index++)
    272.             {
    273.                 //This is the line...makes perfect sense after I wrote it....
    274.                 tree[index] = new QuadSurface(this, LEVELOFDETAIL, this.SUBDIVISIONS + 1, i + (this.XINDEX * 2), j + (this.ZINDEX * 2), WATERLEVEL, RADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT, TEXTURES, QUADFACE);
    275.             }
    276.         }
    277.  
    278.         Destroy();
    279.     }
    280. }

    Code (CSharp):
    1. //SOURCE: alucardj
    2. //http://forum.unity3d.com/conversations/procedural-textures.465272/
    3.  
    4. using UnityEngine;
    5. using System.Collections;
    6. using System.Collections.Generic;
    7.  
    8. public class TextureGenerator
    9. {
    10.     public Texture2D[] surfaceTextures { get; private set; }
    11.  
    12.     private int   textureSize;
    13.     private float noiseOffset, octaves, noiseScale;
    14.     private Gradient planetColors;
    15.  
    16.     public TextureGenerator(int size, float offset, float oct, float scale, Gradient colors)
    17.     {
    18.         textureSize  = size;
    19.         noiseOffset  = offset;
    20.         octaves      = oct;
    21.         noiseScale   = scale;
    22.         planetColors = colors;
    23.  
    24.         surfaceTextures = new Texture2D[6];
    25.  
    26.         for (int i = 0; i < 6; i++ )
    27.             GenerateSurfaceTextures((quadFace)i);
    28.     }
    29.  
    30.     void GenerateSurfaceTextures(quadFace face)
    31.     {
    32.         Texture2D texture = new Texture2D(textureSize, textureSize);
    33.  
    34.         //Create the Color List that will be applied to the texture
    35.         List<Color> colorList = new List<Color>();
    36.         Color color;
    37.  
    38.         //Appropriately size (and create) the Vertex Array
    39.         Vector3[] vertices = new Vector3[textureSize * textureSize];
    40.  
    41.         // Calculate the increment to keep the vertices constrained to a 1x1 cube.
    42.         float increment = 1.0f / ((float)textureSize - 1);
    43.  
    44.         for (int i = 0, index = 0; i < textureSize; i++)
    45.         {
    46.             for (int j = 0; j < textureSize; j++, index++)
    47.             {
    48.                 // Vertex Coordinates
    49.                 float xPos = (float)j * increment - 0.5f;
    50.                 float yPos = 0.5f;
    51.                 float zPos = (float)i * increment - 0.5f;
    52.  
    53.                 // Assign Vertex Coordinates
    54.                 switch (face)
    55.                 {
    56.                     case quadFace.TOP:
    57.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    58.                         break;
    59.  
    60.                     case quadFace.BOTTOM:
    61.                         vertices[index] = new Vector3(xPos, -yPos, -zPos);
    62.                         break;
    63.  
    64.                     case quadFace.LEFT:
    65.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    66.                         break;
    67.  
    68.                     case quadFace.RIGHT:
    69.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    70.                         break;
    71.  
    72.                     case quadFace.FRONT:
    73.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    74.                         break;
    75.  
    76.                     case quadFace.BACK:
    77.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    78.                         break;
    79.                 }
    80.  
    81.                 //Spherify the Vertices!
    82.                 vertices[index] = PlanetUtilities.SmoothVertices(vertices[index]);
    83.  
    84.                 //Get noise!
    85.                 float noise = PlanetUtilities.GetNoise(vertices[index], noiseOffset, octaves, noiseScale);
    86.  
    87.                 // get Colour
    88.                 /*if (useGreyscale)
    89.                 {
    90.                     colour = new Color(noise, noise, noise, 1f);
    91.                 }
    92.                 else
    93.                 {
    94.                     colour = planetColours.Evaluate(noise);
    95.                 }*/
    96.  
    97.                 color = planetColors.Evaluate(noise);
    98.  
    99.                 // Add Colour to Texture Colour Array
    100.                 colorList.Add(color);
    101.             }
    102.         }
    103.  
    104.         // Apply Texture Colour Array to the Texture
    105.         texture.SetPixels(colorList.ToArray());
    106.         texture.wrapMode = TextureWrapMode.Clamp;
    107.         texture.Apply();
    108.  
    109.         surfaceTextures[(int)face] = texture;
    110.     }
    111. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public enum quadFace { TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK }
    5.  
    6. public static class PlanetUtilities
    7. {
    8.     //From Catlike Coding!
    9.     public static Vector3 SmoothVertices(Vector3 vertex)
    10.     {
    11.         Vector3 v = vertex * 2f;
    12.         float x2 = v.x * v.x;
    13.         float y2 = v.y * v.y;
    14.         float z2 = v.z * v.z;
    15.  
    16.         Vector3 s;
    17.         s.x = v.x * Mathf.Sqrt(1f - y2 / 2f - z2 / 2f + y2 * z2 / 3f);
    18.         s.y = v.y * Mathf.Sqrt(1f - x2 / 2f - z2 / 2f + x2 * z2 / 3f);
    19.         s.z = v.z * Mathf.Sqrt(1f - x2 / 2f - y2 / 2f + x2 * y2 / 3f);
    20.  
    21.         return s;
    22.     }
    23.  
    24.     // using a Simplex Noise library :
    25.     //  http://cabbynode.net/2012/06/10/perlin-simplex-noise-for-c-and-xna/
    26.  
    27.     public static float GetNoise(Vector3 vertex, float noiseOffset, float octaves, float noiseScale)
    28.     {
    29.         // add offset
    30.         vertex.x += noiseOffset;
    31.         vertex.y += noiseOffset;
    32.         vertex.z += noiseOffset;
    33.  
    34.         // fractal noise
    35.         float amp = 1f;
    36.         float noise = 0f;
    37.         float gain = 1f;
    38.         float factor = 0f;
    39.         Vector3 calcVert;
    40.  
    41.         for (int i = 0; i < octaves; i++)
    42.         {
    43.             factor += 1f / gain;
    44.             calcVert = vertex * noiseScale * gain;
    45.             noise += Noise.Noise.GetNoise(calcVert.x, calcVert.y, calcVert.z) * (amp / gain);
    46.             gain *= 2f;
    47.         }
    48.  
    49.         // normalize noise by octave iteration factor
    50.         noise /= factor;
    51.  
    52.         return noise;
    53.     }
    54.  
    55.     public static Gradient CreateColourGradient(Gradient planetColors)
    56.     {
    57.         // using colour scale from LibNoise example : http://libnoise.sourceforge.net/tutorials/tutorial3.html
    58.         //renderer.AddGradientPoint (-1.0000, utils::Color (  0,  0, 128, 255)); // deeps
    59.         //renderer.AddGradientPoint (-0.2500, utils::Color (  0,  0, 255, 255)); // shallow
    60.         //renderer.AddGradientPoint ( 0.0000, utils::Color (  0, 128, 255, 255)); // shore
    61.         //renderer.AddGradientPoint ( 0.0625, utils::Color (240, 240,  64, 255)); // sand
    62.         //renderer.AddGradientPoint ( 0.1250, utils::Color ( 32, 160,  0, 255)); // grass
    63.         //renderer.AddGradientPoint ( 0.3750, utils::Color (224, 224,  0, 255)); // dirt
    64.         //renderer.AddGradientPoint ( 0.7500, utils::Color (128, 128, 128, 255)); // rock
    65.         //renderer.AddGradientPoint ( 1.0000, utils::Color (255, 255, 255, 255)); // snow
    66.  
    67.         planetColors = new Gradient();
    68.  
    69.         GradientColorKey[] gck = new GradientColorKey[8];
    70.         gck[0].color = CalculateGradientColour(0, 0, 128);
    71.         gck[0].time = CalculateGradientTime(-1.0000);
    72.         gck[1].color = CalculateGradientColour(0, 0, 255);
    73.         gck[1].time = CalculateGradientTime(-0.2500);
    74.         gck[2].color = CalculateGradientColour(0, 128, 255);
    75.         gck[2].time = CalculateGradientTime(0.0000);
    76.         gck[3].color = CalculateGradientColour(240, 240, 64);
    77.         gck[3].time = CalculateGradientTime(0.0625);
    78.         gck[4].color = CalculateGradientColour(32, 160, 0);
    79.         gck[4].time = CalculateGradientTime(0.1250);
    80.         gck[5].color = CalculateGradientColour(224, 224, 0);
    81.         gck[5].time = CalculateGradientTime(0.3750);
    82.         gck[6].color = CalculateGradientColour(128, 128, 128);
    83.         gck[6].time = CalculateGradientTime(0.7500);
    84.         gck[7].color = CalculateGradientColour(255, 255, 255);
    85.         gck[7].time = CalculateGradientTime(1.0000);
    86.  
    87.         GradientAlphaKey[] gak = new GradientAlphaKey[2];
    88.         gak[0].alpha = 1f;
    89.         gak[0].time = 0f;
    90.         gak[1].alpha = 1f;
    91.         gak[1].time = 1f;
    92.  
    93.         planetColors.SetKeys(gck, gak);
    94.  
    95.         return planetColors;
    96.     }
    97.  
    98.  
    99.     static Color CalculateGradientColour(int r, int g, int b)
    100.     {
    101.         return new Color((float)r / 255f, (float)g / 255f, (float)b / 255f);
    102.     }
    103.  
    104.  
    105.     static float CalculateGradientTime(double t)
    106.     {
    107.         return (float)((t + 1) * 0.5);
    108.     }
    109. }
     
  6. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    So...the above warning no longer applies...well, it kind of does until I get it to start collapsing again. But, I solved the Sub-D issue I was having, mainly because I was using the wrong values before...I'm an idiot. Oh well...

    Anyway, other than the fact that the sub divisions don't go away but remain, things are working pretty well. Texture coordinates and vertex coloring are still working like they should. So now to figure out how to collapse the sub divisions as you move around the world.

    If anyone has any insights into how to make the collapse system work, please weigh in...I think it may be as simple as subtracting 1 from the tree with distance...but...yeah. Not sure.

    New code!

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class GeneratePlanet : MonoBehaviour
    5. {
    6.     [Header("Planet Info")]
    7.     [Range(3.0f,  7.0f)] public int   LOD          = 3;
    8.     [Range(0.0f, 10.0f)] public int   MAXSUBDLEVEL = 3;
    9.     [Range(0.0f,  1.0f)] public float WATERLEVEL   = 0.5f;
    10.     public float PLANETRADIUS = 1; //m, will eventually be km
    11.     public float WORLDHEIGHT  = 0.1f;
    12.     [Header("Texture and Noise Variables")]
    13.     public int   TEXTURESIZE = 256;
    14.     public float NOISESCALE  = 1.45f;
    15.     public float NOISEOFFSET = 1.56f;
    16.     public int   OCTAVES     = 8;
    17.     [Header("Planet Colors")]
    18.     public Gradient PLANETCOLORS;
    19.     [Header("Test")]
    20.     public bool USEVERTEXCOLORS = false;
    21.     public Material vertexColors;
    22.  
    23.     //The planet!
    24.     private QuadSphere planet;
    25.     //Textures!
    26.     private TextureGenerator textureGenerator;
    27.     private Texture2D[]      textures;
    28.  
    29.     void Start()
    30.     {
    31.         //Generate textures!
    32.         textureGenerator = new TextureGenerator(TEXTURESIZE,NOISEOFFSET, OCTAVES, NOISESCALE, PLANETCOLORS);
    33.         //Assign the textures created above to the local textures to make passing them along easier...
    34.         textures = textureGenerator.surfaceTextures;
    35.  
    36.         planet = new QuadSphere("Planet1", LOD, WATERLEVEL, PLANETRADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT, textures);
    37.     }
    38.  
    39.     void Update()
    40.     {
    41.         for (int i = 0; i < 6; i++)
    42.             StartCoroutine(planet.Update(i));
    43.     }
    44. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class QuadSphere
    6. {
    7.     GameObject      planet;
    8.     GameObject      planetGenerator;
    9.     GeneratePlanet  generator;
    10.     QuadSurface[]   surfaces;
    11.  
    12.     public QuadSphere(string name, int lod, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures)
    13.     {      
    14.         //Initialize the array
    15.         surfaces = new QuadSurface[6];
    16.  
    17.         //Create a new new QuadSurface using the specified LOD.
    18.         for (int i = 0; i < 6; i++)
    19.         {
    20.             //Sub Divisions 0, Indices = 0...this is the default!
    21.             surfaces[i] = new QuadSurface(lod, 0, 0, 0, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, (quadFace)i);
    22.         }
    23.  
    24.         planet          = new GameObject(name);
    25.         planetGenerator = GameObject.Find("PlanetGenerator");
    26.         generator       = planetGenerator.GetComponent<GeneratePlanet>();
    27.  
    28.         for (int i = 0; i < surfaces.Length; i++)
    29.             surfaces[i].surface.transform.parent = planet.transform;
    30.     }
    31.  
    32.     private void FindActiveQuadTreeSurfaces(QuadSurface surface, ref List<QuadSurface> activeSurfaces)
    33.     {
    34.         if (surface.tree == null)
    35.             activeSurfaces.Add(surface);
    36.         else
    37.             for (int i = 0; i < surface.tree.Length; i++)
    38.                 FindActiveQuadTreeSurfaces(surface.tree[i], ref activeSurfaces);
    39.     }
    40.  
    41.     public IEnumerator Update(int surface)
    42.     {
    43.         GameObject player = GameObject.FindGameObjectWithTag("Player");
    44.         if (player == null)
    45.             yield break;
    46.  
    47.         List<QuadSurface> activeTree = new List<QuadSurface>();
    48.         FindActiveQuadTreeSurfaces(surfaces[surface], ref activeTree);
    49.  
    50.         for (int i = 0; i < activeTree.Count; i++)
    51.         {
    52.             float dist = Vector3.Distance(player.transform.position, activeTree[i].surfaceMesh.bounds.ClosestPoint(player.transform.position));
    53.  
    54.             if (dist > activeTree[i].RADIUS)
    55.             {
    56.                 //Debug.Log("The player is: " + dist + " from the Planet.");
    57.                 if (dist <= activeTree[i].RADIUS * 1000.0f || activeTree[i].ROOT == null)
    58.                 {
    59.                     activeTree[i].MergeQuadTree();
    60.                     activeTree[i].GenerateSurfaceCoordinates();
    61.                 }
    62.                 else
    63.                 {
    64.                     activeTree[i].MergeQuadTree();
    65.                     activeTree[i].ROOT.MergeQuadTree();
    66.                     activeTree[i].ROOT.GenerateSurfaceCoordinates();
    67.                 }
    68.             }
    69.             else if(activeTree[i].SUBDIVISIONS < generator.MAXSUBDLEVEL)
    70.             {
    71.                 Debug.Log("Sub-D: " + activeTree[i].SUBDIVISIONS + " < " + generator.MAXSUBDLEVEL);
    72.                 activeTree[i].SubDivide();
    73.             }
    74.             else
    75.             {
    76.                 if (activeTree[i].surface.tag != "planet")
    77.                     activeTree[i].surface.tag = "planet";
    78.             }
    79.         }
    80.     }
    81. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class QuadSurface
    5. {
    6.     public QuadSurface   ROOT;
    7.     public QuadSurface[] tree;  
    8.  
    9.     public GameObject   surface      { get; private set; }
    10.     public MeshRenderer renderer     { get; private set; }
    11.     public MeshCollider collider     { get; private set; }
    12.     public MeshFilter   filter       { get; private set; }
    13.     public Mesh         surfaceMesh  { get; private set; }
    14.  
    15.     public int          SUBDIVISIONS { get; private set; }
    16.     public float        RADIUS       { get; private set; }
    17.  
    18.     private int         LEVELOFDETAIL, XINDEX, ZINDEX;
    19.     private float       WATERLEVEL, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT;
    20.     private Texture2D[] TEXTURES;
    21.     private quadFace    QUADFACE;
    22.  
    23.     public bool[] neighbors;
    24.     public bool   generated = false;
    25.     public bool   finalized = false;
    26.  
    27.     //Vertex Colors Testing
    28.     GameObject     planetGenerator;
    29.     GeneratePlanet generator;
    30.     Material       planetMaterial;
    31.     Color[]        planetColors;
    32.     Gradient       planetGradient;
    33.  
    34.     public QuadSurface(QuadSurface root, int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    35.     {
    36.         InitializeSurfaceGenerator(root, lod, subDivisions, xIndex, zIndex, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, face);
    37.     }
    38.  
    39.     public QuadSurface(int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    40.     {
    41.         InitializeSurfaceGenerator(null, lod, subDivisions, xIndex, zIndex, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, face);
    42.     }
    43.  
    44.     void InitializeSurfaceGenerator(QuadSurface root, int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    45.     {
    46.         this.ROOT           = root;
    47.         this.LEVELOFDETAIL  = lod;
    48.         this.SUBDIVISIONS   = subDivisions;
    49.         this.XINDEX         = xIndex;
    50.         this.ZINDEX         = zIndex;
    51.         this.WATERLEVEL     = waterLevel;
    52.         this.RADIUS         = radius;
    53.         this.NOISEOFFSET    = noiseOffset;
    54.         this.OCTAVES        = octaves;
    55.         this.NOISESCALE     = noiseScale;
    56.         this.WORLDHEIGHT    = worldHeight;
    57.         this.TEXTURES       = textures;
    58.         this.QUADFACE       = face;
    59.  
    60.         //Vertex Colors Testing
    61.         planetGenerator = GameObject.Find("PlanetGenerator");
    62.         generator       = planetGenerator.GetComponent<GeneratePlanet>();
    63.         planetMaterial  = generator.vertexColors;
    64.         planetGradient  = generator.PLANETCOLORS;
    65.  
    66.         //Create the game object to which we'll attach everything to
    67.         surface  = new GameObject("SUB-D: " + SUBDIVISIONS + " Face: " + QUADFACE + " (" + XINDEX + ", " + ZINDEX + ")");
    68.         //Add the mesh Renderer
    69.         renderer = surface.AddComponent<MeshRenderer>();
    70.         //Add the MeshCollider
    71.         collider = surface.AddComponent<MeshCollider>();
    72.         //Add the MeshFilter
    73.         filter   = surface.AddComponent<MeshFilter>();
    74.  
    75.         //Initialize the renderer
    76.         renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    77.         renderer.receiveShadows = true;
    78.         renderer.enabled = true;
    79.  
    80.         if (generator.USEVERTEXCOLORS == false)
    81.             //Assign the generated textures to the quadSphere
    82.             surface.GetComponent<Renderer>().material.mainTexture = TEXTURES[(int)QUADFACE];
    83.         else
    84.             //Vertex Colors testing
    85.             surface.GetComponent<Renderer>().material = planetMaterial;
    86.  
    87.         if (ROOT != null)
    88.             surface.transform.parent = ROOT.surface.transform;
    89.  
    90.         GenerateSurfaceCoordinates();
    91.     }
    92.  
    93.     public void GenerateSurfaceCoordinates()
    94.     {
    95.         //The base number of faces is 2, which means we need +1 vert to make that happen.
    96.         int vertsPerQuad = (int)Mathf.Pow(2, LEVELOFDETAIL) + 1;
    97.  
    98.         //Appropriately size (and create) the Vertex Array
    99.         Vector3[] vertices = new Vector3[vertsPerQuad * vertsPerQuad];
    100.         //Appropriately size (and create) the Normal Array
    101.         Vector3[] normals = new Vector3[vertices.Length];      
    102.         //Appropriately size (and create) the UV Array
    103.         Vector2[] uvs = new Vector2[vertices.Length];
    104.         //Vertex Colors Testing
    105.         Color[] colors = new Color[vertices.Length];
    106.         planetColors = colors;
    107.  
    108.         //Calculate the increment to keep the vertices constrained to a 1x1 cube.
    109.         float increment     = ((1.0f / Mathf.Pow(2, LEVELOFDETAIL)) / Mathf.Pow(2, SUBDIVISIONS));
    110.         float uvIncrement   = (1.0f / Mathf.Pow(2, LEVELOFDETAIL)) / Mathf.Pow(2, SUBDIVISIONS);
    111.  
    112.         for (int i = 0, index = 0; i < vertsPerQuad; i++)
    113.         {
    114.             for (int j = 0; j < vertsPerQuad; j++, index++)
    115.             {
    116.                 //Vertex Coordinates
    117.                 float xPos = (float)j * increment - 0.5f + ((float)XINDEX / Mathf.Pow(2, SUBDIVISIONS));
    118.                 float yPos = 0.5f;
    119.                 float zPos = (float)i * increment - 0.5f + ((float)ZINDEX / Mathf.Pow(2, SUBDIVISIONS));
    120.                 //UV Coordinates
    121.                 float xUV = (float)j * uvIncrement + ((float)XINDEX / Mathf.Pow(2, SUBDIVISIONS));
    122.                 float zUV = (float)i * uvIncrement + ((float)ZINDEX / Mathf.Pow(2, SUBDIVISIONS));
    123.  
    124.                 switch (QUADFACE)
    125.                 {
    126.                     case quadFace.TOP:
    127.                         //Assign Vertex Coordinates
    128.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    129.                         break;
    130.  
    131.                     case quadFace.BOTTOM:
    132.                         //Assign Vertex Coordinates
    133.                         vertices[index] = new Vector3(xPos, -yPos, -zPos);
    134.                         break;
    135.  
    136.                     case quadFace.LEFT:
    137.                         //Assign Vertex Coordinates
    138.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    139.                         break;
    140.  
    141.                     case quadFace.RIGHT:
    142.                         //Assign Vertex Coordinates
    143.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    144.                         break;
    145.  
    146.                     case quadFace.FRONT:
    147.                         //Assign Vertex Coordinates
    148.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    149.                         break;
    150.  
    151.                     case quadFace.BACK:
    152.                         //Assign Vertex Coordinates
    153.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    154.                         break;
    155.                 }
    156.                 //Assign UV Coordinates
    157.                 uvs[index] = new Vector2(xUV, zUV);
    158.  
    159.                 //Spherify the vertices!
    160.                 vertices[index] = PlanetUtilities.SmoothVertices(vertices[index]);
    161.  
    162.                 float noise = PlanetUtilities.GetNoise(vertices[index], NOISEOFFSET, OCTAVES, NOISESCALE);
    163.                 //Vertex Colors Testing
    164.                 colors[index] = planetGradient.Evaluate(noise);
    165.                
    166.                 //Keep the oceans flat! - thanks alucardj!
    167.                 if (noise > WATERLEVEL)
    168.                     noise = (noise - WATERLEVEL) * (1.0f / (1.0f - WATERLEVEL));
    169.                 else
    170.                     noise = 0.0f;
    171.  
    172.                 vertices[index] *= RADIUS + (noise * WORLDHEIGHT);
    173.  
    174.                 normals[index] = vertices[index].normalized;
    175.             }
    176.         }
    177.         //Create the Surface Object
    178.         CreateSurfaceMesh(vertsPerQuad, vertices, normals, uvs);
    179.     }
    180.  
    181.     void CreateSurfaceMesh(int vertsPerQuad, Vector3[] vertexArray, Vector3[] normalArray, Vector2[] uvArray)
    182.     {
    183.         //If this has already been generated, enable the renderer
    184.         if (generated)
    185.         {
    186.             renderer.enabled = true;
    187.             if (ROOT != null)
    188.                 ROOT.collider.enabled = false;
    189.             collider.enabled = true;
    190.             return;
    191.         }
    192.  
    193.         int vertBufferSize = vertsPerQuad - 1;
    194.         //Size the vertex buffer
    195.         int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    196.  
    197.         //Create a new Mesh using the Objects MeshFilter
    198.         surfaceMesh = surface.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    199.         surfaceMesh.name = surface.name;
    200.  
    201.         //Step through the Vertex Buffer
    202.         for (int triIndex = 0, vertIndex = 0, i = 0; i < vertBufferSize; i++, vertIndex++)
    203.         {
    204.             for (int j = 0; j < vertBufferSize; j++, triIndex += 6, vertIndex++)
    205.             {
    206.                 //  1
    207.                 //  | \
    208.                 //  |  \
    209.                 //  |   \
    210.                 //  0----2
    211.                 vertexBuffer[triIndex] = vertIndex;
    212.                 vertexBuffer[triIndex + 1] = vertIndex + vertsPerQuad;
    213.                 vertexBuffer[triIndex + 2] = vertIndex + 1;
    214.                 //  1----3
    215.                 //    \  |
    216.                 //     \ |
    217.                 //      \|
    218.                 //       2
    219.                 vertexBuffer[triIndex + 3] = vertIndex + vertsPerQuad;
    220.                 vertexBuffer[triIndex + 4] = vertIndex + vertsPerQuad + 1;
    221.                 vertexBuffer[triIndex + 5] = vertIndex + 1;
    222.             }
    223.         }
    224.         //Assign the Vertex Array from Generate Surface Coordinates to the Mesh Vertex Array
    225.         surfaceMesh.vertices = vertexArray;
    226.         //Assign the UV Coordinates
    227.         surfaceMesh.uv = uvArray;
    228.         //Assign the Vertex Buffer
    229.         surfaceMesh.triangles = vertexBuffer;
    230.         //Vertex Colors Testing
    231.         surfaceMesh.colors = planetColors;
    232.         //Recalculate the Normals
    233.         //surfaceMesh.RecalculateNormals();
    234.         surfaceMesh.normals = normalArray; //Done manually...
    235.         //Recalculate Bounds
    236.         surfaceMesh.RecalculateBounds();
    237.         //After the Mesh has been created, pass it back to the MeshCollider
    238.         surface.GetComponent<MeshCollider>().sharedMesh = surfaceMesh;
    239.  
    240.         //If root is ever null...Destroy!
    241.         if (ROOT != null)
    242.             ROOT.Destroy();
    243.  
    244.         generated = true;
    245.     }
    246.  
    247.     public void Destroy()
    248.     {
    249.         renderer.enabled = false;
    250.         generated = false;
    251.     }
    252.  
    253.     //Merges our quadtrees!
    254.     public void MergeQuadTree()
    255.     {
    256.         if (tree == null)
    257.             return;
    258.  
    259.         for (int i = 0; i < tree.Length; i++)
    260.         {
    261.             tree[i].MergeQuadTree();
    262.             tree[i].Destroy();
    263.         }
    264.  
    265.         tree = null;
    266.     }
    267.  
    268.     public void SubDivide()
    269.     {
    270.         tree = new QuadSurface[4];
    271.  
    272.         for (int i = 0, index = 0; i < 2; i++)
    273.         {
    274.             for (int j = 0; j < 2; j++, index++)
    275.             {
    276.                 //This is the line...makes perfect sense after I wrote it....
    277.                 tree[index] = new QuadSurface(this, LEVELOFDETAIL, this.SUBDIVISIONS + 1, i + (this.XINDEX * 2), j + (this.ZINDEX * 2), WATERLEVEL, RADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT, TEXTURES, QUADFACE);
    278.             }
    279.         }
    280.  
    281.         Destroy();
    282.     }
    283. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public enum quadFace { TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK }
    5.  
    6. public static class PlanetUtilities
    7. {
    8.     //From Catlike Coding!
    9.     public static Vector3 SmoothVertices(Vector3 vertex)
    10.     {
    11.         Vector3 v = vertex * 2f;
    12.         float x2 = v.x * v.x;
    13.         float y2 = v.y * v.y;
    14.         float z2 = v.z * v.z;
    15.  
    16.         Vector3 s;
    17.         s.x = v.x * Mathf.Sqrt(1f - y2 / 2f - z2 / 2f + y2 * z2 / 3f);
    18.         s.y = v.y * Mathf.Sqrt(1f - x2 / 2f - z2 / 2f + x2 * z2 / 3f);
    19.         s.z = v.z * Mathf.Sqrt(1f - x2 / 2f - y2 / 2f + x2 * y2 / 3f);
    20.  
    21.         return s;
    22.     }
    23.  
    24.     // using a Simplex Noise library :
    25.     //  http://cabbynode.net/2012/06/10/perlin-simplex-noise-for-c-and-xna/
    26.  
    27.     public static float GetNoise(Vector3 vertex, float noiseOffset, float octaves, float noiseScale)
    28.     {
    29.         // add offset
    30.         vertex.x += noiseOffset;
    31.         vertex.y += noiseOffset;
    32.         vertex.z += noiseOffset;
    33.  
    34.         // fractal noise
    35.         float amp = 1f;
    36.         float noise = 0f;
    37.         float gain = 1f;
    38.         float factor = 0f;
    39.         Vector3 calcVert;
    40.  
    41.         for (int i = 0; i < octaves; i++)
    42.         {
    43.             factor += 1f / gain;
    44.             calcVert = vertex * noiseScale * gain;
    45.             noise += Noise.Noise.GetNoise(calcVert.x, calcVert.y, calcVert.z) * (amp / gain);
    46.             gain *= 2f;
    47.         }
    48.  
    49.         // normalize noise by octave iteration factor
    50.         noise /= factor;
    51.  
    52.         return noise;
    53.     }
    54.  
    55.     public static Gradient CreateColourGradient(Gradient planetColors)
    56.     {
    57.         // using colour scale from LibNoise example : http://libnoise.sourceforge.net/tutorials/tutorial3.html
    58.         //renderer.AddGradientPoint (-1.0000, utils::Color (  0,  0, 128, 255)); // deeps
    59.         //renderer.AddGradientPoint (-0.2500, utils::Color (  0,  0, 255, 255)); // shallow
    60.         //renderer.AddGradientPoint ( 0.0000, utils::Color (  0, 128, 255, 255)); // shore
    61.         //renderer.AddGradientPoint ( 0.0625, utils::Color (240, 240,  64, 255)); // sand
    62.         //renderer.AddGradientPoint ( 0.1250, utils::Color ( 32, 160,  0, 255)); // grass
    63.         //renderer.AddGradientPoint ( 0.3750, utils::Color (224, 224,  0, 255)); // dirt
    64.         //renderer.AddGradientPoint ( 0.7500, utils::Color (128, 128, 128, 255)); // rock
    65.         //renderer.AddGradientPoint ( 1.0000, utils::Color (255, 255, 255, 255)); // snow
    66.  
    67.         planetColors = new Gradient();
    68.  
    69.         GradientColorKey[] gck = new GradientColorKey[8];
    70.         gck[0].color = CalculateGradientColour(0, 0, 128);
    71.         gck[0].time = CalculateGradientTime(-1.0000);
    72.         gck[1].color = CalculateGradientColour(0, 0, 255);
    73.         gck[1].time = CalculateGradientTime(-0.2500);
    74.         gck[2].color = CalculateGradientColour(0, 128, 255);
    75.         gck[2].time = CalculateGradientTime(0.0000);
    76.         gck[3].color = CalculateGradientColour(240, 240, 64);
    77.         gck[3].time = CalculateGradientTime(0.0625);
    78.         gck[4].color = CalculateGradientColour(32, 160, 0);
    79.         gck[4].time = CalculateGradientTime(0.1250);
    80.         gck[5].color = CalculateGradientColour(224, 224, 0);
    81.         gck[5].time = CalculateGradientTime(0.3750);
    82.         gck[6].color = CalculateGradientColour(128, 128, 128);
    83.         gck[6].time = CalculateGradientTime(0.7500);
    84.         gck[7].color = CalculateGradientColour(255, 255, 255);
    85.         gck[7].time = CalculateGradientTime(1.0000);
    86.  
    87.         GradientAlphaKey[] gak = new GradientAlphaKey[2];
    88.         gak[0].alpha = 1f;
    89.         gak[0].time = 0f;
    90.         gak[1].alpha = 1f;
    91.         gak[1].time = 1f;
    92.  
    93.         planetColors.SetKeys(gck, gak);
    94.  
    95.         return planetColors;
    96.     }
    97.  
    98.  
    99.     static Color CalculateGradientColour(int r, int g, int b)
    100.     {
    101.         return new Color((float)r / 255f, (float)g / 255f, (float)b / 255f);
    102.     }
    103.  
    104.  
    105.     static float CalculateGradientTime(double t)
    106.     {
    107.         return (float)((t + 1) * 0.5);
    108.     }
    109. }

    Code (CSharp):
    1. //SOURCE: alucardj
    2. //http://forum.unity3d.com/conversations/procedural-textures.465272/
    3.  
    4. using UnityEngine;
    5. using System.Collections;
    6. using System.Collections.Generic;
    7.  
    8. public class TextureGenerator
    9. {
    10.     public Texture2D[] surfaceTextures { get; private set; }
    11.  
    12.     private int   textureSize;
    13.     private float noiseOffset, octaves, noiseScale;
    14.     private Gradient planetColors;
    15.  
    16.     public TextureGenerator(int size, float offset, float oct, float scale, Gradient colors)
    17.     {
    18.         textureSize  = size;
    19.         noiseOffset  = offset;
    20.         octaves      = oct;
    21.         noiseScale   = scale;
    22.         planetColors = colors;
    23.  
    24.         surfaceTextures = new Texture2D[6];
    25.  
    26.         for (int i = 0; i < 6; i++ )
    27.             GenerateSurfaceTextures((quadFace)i);
    28.     }
    29.  
    30.     void GenerateSurfaceTextures(quadFace face)
    31.     {
    32.         Texture2D texture = new Texture2D(textureSize, textureSize);
    33.  
    34.         //Create the Color List that will be applied to the texture
    35.         List<Color> colorList = new List<Color>();
    36.         Color color;
    37.  
    38.         //Appropriately size (and create) the Vertex Array
    39.         Vector3[] vertices = new Vector3[textureSize * textureSize];
    40.  
    41.         // Calculate the increment to keep the vertices constrained to a 1x1 cube.
    42.         float increment = 1.0f / ((float)textureSize - 1);
    43.  
    44.         for (int i = 0, index = 0; i < textureSize; i++)
    45.         {
    46.             for (int j = 0; j < textureSize; j++, index++)
    47.             {
    48.                 // Vertex Coordinates
    49.                 float xPos = (float)j * increment - 0.5f;
    50.                 float yPos = 0.5f;
    51.                 float zPos = (float)i * increment - 0.5f;
    52.  
    53.                 // Assign Vertex Coordinates
    54.                 switch (face)
    55.                 {
    56.                     case quadFace.TOP:
    57.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    58.                         break;
    59.  
    60.                     case quadFace.BOTTOM:
    61.                         vertices[index] = new Vector3(xPos, -yPos, -zPos);
    62.                         break;
    63.  
    64.                     case quadFace.LEFT:
    65.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    66.                         break;
    67.  
    68.                     case quadFace.RIGHT:
    69.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    70.                         break;
    71.  
    72.                     case quadFace.FRONT:
    73.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    74.                         break;
    75.  
    76.                     case quadFace.BACK:
    77.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    78.                         break;
    79.                 }
    80.  
    81.                 //Spherify the Vertices!
    82.                 vertices[index] = PlanetUtilities.SmoothVertices(vertices[index]);
    83.  
    84.                 //Get noise!
    85.                 float noise = PlanetUtilities.GetNoise(vertices[index], noiseOffset, octaves, noiseScale);
    86.  
    87.                 // get Colour
    88.                 /*if (useGreyscale)
    89.                 {
    90.                     colour = new Color(noise, noise, noise, 1f);
    91.                 }
    92.                 else
    93.                 {
    94.                     colour = planetColours.Evaluate(noise);
    95.                 }*/
    96.  
    97.                 color = planetColors.Evaluate(noise);
    98.  
    99.                 // Add Colour to Texture Colour Array
    100.                 colorList.Add(color);
    101.             }
    102.         }
    103.  
    104.         // Apply Texture Colour Array to the Texture
    105.         texture.SetPixels(colorList.ToArray());
    106.         texture.wrapMode = TextureWrapMode.Clamp;
    107.         texture.Apply();
    108.  
    109.         surfaceTextures[(int)face] = texture;
    110.     }
    111. }
     
  7. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Okay, well...currently I'm stuck and I'm tired, so that means it's quittin' time!

    Issues and what not:

    1. Currently the planet sub-divides the way it's supposed to...sort of. The planet subdivides as you get closer and then collapses as you move farther away...the only issue is that it doesn't always work correctly on the collapse...Need help here. (turns out it's been doing this all along....just wasn't moving the player far enough away)

    2. The subdivisions process doesn't respect the 'neighbors can only ever be one level higher than the level before it (which is gonna be super critical for avoiding tears)...need help here too.

    I started a 'merge neighbors' function which is supposed to be used to poll the surrounding surfaces and control the sub division process a little bit...it's definitely not working as intended. This merge neighbors function will be the main function to handle avoiding tears, or at least the first step in that chain of events.

    So...yeah, need some help here for sure.

    Updated code!

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class GeneratePlanet : MonoBehaviour
    5. {
    6.     [Header("Planet Info")]
    7.     [Range(3.0f,  7.0f)] public int   LOD          = 3;
    8.     [Range(0.0f, 10.0f)] public int   MAXSUBDLEVEL = 3;
    9.     [Range(0.0f,  1.0f)] public float WATERLEVEL   = 0.5f;
    10.     public float PLANETRADIUS = 1; //m, will eventually be km
    11.     public float WORLDHEIGHT  = 0.1f;
    12.     [Header("Texture and Noise Variables")]
    13.     public int   TEXTURESIZE = 256;
    14.     public float NOISESCALE  = 1.45f;
    15.     public float NOISEOFFSET = 1.56f;
    16.     public int   OCTAVES     = 8;
    17.     [Header("Planet Colors")]
    18.     public Gradient PLANETCOLORS;
    19.     [Header("Test")]
    20.     public bool USEVERTEXCOLORS = false;
    21.     public Material vertexColors;
    22.  
    23.     //The planet!
    24.     private QuadSphere planet;
    25.     //Textures!
    26.     private TextureGenerator textureGenerator;
    27.     private Texture2D[]      textures;
    28.  
    29.     void Start()
    30.     {
    31.         //Generate textures!
    32.         textureGenerator = new TextureGenerator(TEXTURESIZE,NOISEOFFSET, OCTAVES, NOISESCALE, PLANETCOLORS);
    33.         //Assign the textures created above to the local textures to make passing them along easier...
    34.         textures = textureGenerator.surfaceTextures;
    35.  
    36.         planet = new QuadSphere("Planet1", LOD, WATERLEVEL, PLANETRADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT, textures);
    37.     }
    38.  
    39.     void Update()
    40.     {
    41.         for (int i = 0; i < 6; i++)
    42.             StartCoroutine(planet.Update(i));
    43.     }
    44. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class QuadSphere
    6. {
    7.     GameObject      planet;
    8.     GameObject      planetGenerator;
    9.     GeneratePlanet  generator;
    10.     QuadSurface[]   surfaces;
    11.  
    12.     public QuadSphere(string name, int lod, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures)
    13.     {      
    14.         //Initialize the array
    15.         surfaces = new QuadSurface[6];
    16.  
    17.         //Create a new new QuadSurface using the specified LOD.
    18.         for (int i = 0; i < 6; i++)
    19.         {
    20.             //Sub Divisions 0, Indices = 0...this is the default!
    21.             surfaces[i] = new QuadSurface(lod, 0, 0, 0, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, (QuadFace)i);
    22.         }
    23.  
    24.         planet          = new GameObject(name);
    25.         planetGenerator = GameObject.Find("PlanetGenerator");
    26.         generator       = planetGenerator.GetComponent<GeneratePlanet>();
    27.  
    28.         //Combine the planes under a single parent
    29.         for (int i = 0; i < surfaces.Length; i++)
    30.             surfaces[i].surface.transform.parent = planet.transform;
    31.     }
    32.  
    33.     private void MergeNeighbors(QuadSurface surface, Mesh neighbor, Direction neigborDirection)
    34.     {
    35.         int xIndex = surface.XINDEX, zIndex = surface.ZINDEX;
    36.  
    37.         Vector3[] vertices = surface.surfaceMesh.vertices;
    38.         Vector3[] normals  = surface.surfaceMesh.normals;
    39.  
    40.         if (normals.Length == 0)
    41.         {
    42.             surface.surfaceMesh.RecalculateNormals();
    43.             normals = surface.surfaceMesh.normals;
    44.         }
    45.  
    46.         Vector3[] neighborVertices = neighbor.vertices;
    47.         Vector3[] neighborNormals  = neighbor.normals;
    48.  
    49.         if (neighborNormals.Length == 0)
    50.         {
    51.             neighbor.RecalculateNormals();
    52.             neighborNormals = neighbor.normals;
    53.         }
    54.  
    55.         switch(neigborDirection)
    56.         {
    57.             case Direction.NORTH:
    58.                 zIndex += 1;
    59.                 break;
    60.  
    61.             case Direction.EAST:
    62.                 xIndex += 1;
    63.                 break;
    64.  
    65.             case Direction.SOUTH:
    66.                 zIndex -= 1;
    67.                 break;
    68.  
    69.             case Direction.WEST:
    70.                 xIndex -= 1;
    71.                 break;
    72.         }
    73.  
    74.         //Missing some other stuff until I can figure this out....
    75.  
    76.         surface.surfaceMesh.vertices = vertices;
    77.         surface.surfaceMesh.normals  = normals;
    78.  
    79.     }
    80.  
    81.     private void FindActiveQuadTreeSurfaces(QuadSurface surface, ref List<QuadSurface> activeSurfaces)
    82.     {
    83.         if (surface.tree == null)
    84.             activeSurfaces.Add(surface);
    85.         else
    86.             for (int i = 0; i < surface.tree.Length; i++)
    87.                 FindActiveQuadTreeSurfaces(surface.tree[i], ref activeSurfaces);
    88.     }
    89.  
    90.     public IEnumerator Update(int surface)
    91.     {
    92.         GameObject player = GameObject.FindGameObjectWithTag("Player");
    93.         if (player == null)
    94.             yield break;
    95.  
    96.         List<QuadSurface> activeTree = new List<QuadSurface>();
    97.         FindActiveQuadTreeSurfaces(surfaces[surface], ref activeTree);
    98.  
    99.         for (int i = 0; i < activeTree.Count; i++)
    100.         {
    101.             float dist = Vector3.Distance(player.transform.position, activeTree[i].surfaceMesh.bounds.ClosestPoint(player.transform.position));
    102.  
    103.             if (dist > activeTree[i].RADIUS)
    104.             {
    105.                 if (dist <= activeTree[i].RADIUS * 3.0f || activeTree[i].ROOT == null)
    106.                 {
    107.                     activeTree[i].MergeQuadTree();
    108.                     activeTree[i].GenerateSurfaceCoordinates();
    109.                 }
    110.                 else
    111.                 {
    112.                     activeTree[i].MergeQuadTree();
    113.                     activeTree[i].ROOT.MergeQuadTree();
    114.                     activeTree[i].ROOT.GenerateSurfaceCoordinates();
    115.                 }
    116.             }
    117.             else if(activeTree[i].SUBDIVISIONS < generator.MAXSUBDLEVEL)
    118.             {
    119.                 activeTree[i].SubDivide();
    120.             }
    121.             else
    122.             {
    123.                 if (activeTree[i].surface.tag != "planet")
    124.                     activeTree[i].surface.tag = "planet";
    125.             }
    126.         }
    127.     }
    128. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class QuadSurface
    5. {
    6.     public QuadSurface   ROOT;
    7.     public QuadSurface[] tree;  
    8.  
    9.     public GameObject   surface      { get; private set; }
    10.     public MeshRenderer renderer     { get; private set; }
    11.     public MeshCollider collider     { get; private set; }
    12.     public MeshFilter   filter       { get; private set; }
    13.     public Mesh         surfaceMesh  { get; private set; }
    14.  
    15.     public int          SUBDIVISIONS { get; private set; }
    16.     public int          XINDEX       { get; private set; }
    17.     public int          ZINDEX       { get; private set; }
    18.     public float        RADIUS       { get; private set; }
    19.  
    20.     private int         LEVELOFDETAIL;
    21.     private float       WATERLEVEL, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT;
    22.     private Texture2D[] TEXTURES;
    23.     private QuadFace    QUADFACE;
    24.  
    25.     public bool[] neighbors;
    26.     public bool   generated = false;
    27.     public bool   finalized = false;
    28.  
    29.     //Vertex Colors Testing
    30.     GameObject     planetGenerator;
    31.     GeneratePlanet generator;
    32.     Material       planetMaterial;
    33.     Color[]        planetColors;
    34.     Gradient       planetGradient;
    35.  
    36.     public QuadSurface(QuadSurface root, int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, QuadFace face)
    37.     {
    38.         InitializeSurfaceGenerator(root, lod, subDivisions, xIndex, zIndex, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, face);
    39.     }
    40.  
    41.     public QuadSurface(int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, QuadFace face)
    42.     {
    43.         InitializeSurfaceGenerator(null, lod, subDivisions, xIndex, zIndex, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, face);
    44.     }
    45.  
    46.     void InitializeSurfaceGenerator(QuadSurface root, int lod, int subDivisions, int xIndex, int zIndex, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, QuadFace face)
    47.     {
    48.         this.ROOT           = root;
    49.         this.LEVELOFDETAIL  = lod;
    50.         this.SUBDIVISIONS   = subDivisions;
    51.         this.XINDEX         = xIndex;
    52.         this.ZINDEX         = zIndex;
    53.         this.WATERLEVEL     = waterLevel;
    54.         this.RADIUS         = radius;
    55.         this.NOISEOFFSET    = noiseOffset;
    56.         this.OCTAVES        = octaves;
    57.         this.NOISESCALE     = noiseScale;
    58.         this.WORLDHEIGHT    = worldHeight;
    59.         this.TEXTURES       = textures;
    60.         this.QUADFACE       = face;
    61.  
    62.         //Vertex Colors Testing
    63.         planetGenerator = GameObject.Find("PlanetGenerator");
    64.         generator       = planetGenerator.GetComponent<GeneratePlanet>();
    65.         planetMaterial  = generator.vertexColors;
    66.         planetGradient  = generator.PLANETCOLORS;
    67.  
    68.         //Create the game object to which we'll attach everything to
    69.         surface  = new GameObject("SUB-D: " + SUBDIVISIONS + " Face: " + QUADFACE + " (" + XINDEX + ", " + ZINDEX + ")");
    70.         surface.isStatic = true;
    71.         surface.layer    = 8;
    72.         //Add the mesh Renderer
    73.         renderer = surface.AddComponent<MeshRenderer>();
    74.         //Add the MeshCollider
    75.         collider = surface.AddComponent<MeshCollider>();
    76.         //Add the MeshFilter
    77.         filter   = surface.AddComponent<MeshFilter>();
    78.  
    79.         //Initialize the renderer
    80.         renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    81.         renderer.receiveShadows = true;
    82.         renderer.enabled = true;
    83.  
    84.         if (generator.USEVERTEXCOLORS == false)
    85.             //Assign the generated textures to the quadSphere
    86.             surface.GetComponent<Renderer>().material.mainTexture = TEXTURES[(int)QUADFACE];
    87.         else
    88.             //Vertex Colors testing
    89.             surface.GetComponent<Renderer>().material = planetMaterial;
    90.  
    91.         if (ROOT != null)
    92.             surface.transform.parent = ROOT.surface.transform;
    93.  
    94.         GenerateSurfaceCoordinates();
    95.     }
    96.  
    97.     public void GenerateSurfaceCoordinates()
    98.     {
    99.         //The base number of faces is 2, which means we need +1 vert to make that happen.
    100.         int vertsPerQuad = (int)Mathf.Pow(2, LEVELOFDETAIL) + 1;
    101.  
    102.         //Appropriately size (and create) the Vertex Array
    103.         Vector3[] vertices = new Vector3[vertsPerQuad * vertsPerQuad];
    104.         //Appropriately size (and create) the Normal Array
    105.         Vector3[] normals = new Vector3[vertices.Length];      
    106.         //Appropriately size (and create) the UV Array
    107.         Vector2[] uvs = new Vector2[vertices.Length];
    108.         //Vertex Colors Testing
    109.         Color[] colors = new Color[vertices.Length];
    110.         planetColors = colors;
    111.  
    112.         //Calculate the increment to keep the vertices constrained to a 1x1 cube.
    113.         float increment     = ((1.0f / Mathf.Pow(2, LEVELOFDETAIL)) / Mathf.Pow(2, SUBDIVISIONS));
    114.         float uvIncrement   = (1.0f / Mathf.Pow(2, LEVELOFDETAIL)) / Mathf.Pow(2, SUBDIVISIONS);
    115.  
    116.         for (int i = 0, index = 0; i < vertsPerQuad; i++)
    117.         {
    118.             for (int j = 0; j < vertsPerQuad; j++, index++)
    119.             {
    120.                 //Vertex Coordinates
    121.                 float xPos = (float)j * increment - 0.5f + ((float)XINDEX / Mathf.Pow(2, SUBDIVISIONS));
    122.                 float yPos = 0.5f;
    123.                 float zPos = (float)i * increment - 0.5f + ((float)ZINDEX / Mathf.Pow(2, SUBDIVISIONS));
    124.                 //UV Coordinates
    125.                 float xUV = (float)j * uvIncrement + ((float)XINDEX / Mathf.Pow(2, SUBDIVISIONS));
    126.                 float zUV = (float)i * uvIncrement + ((float)ZINDEX / Mathf.Pow(2, SUBDIVISIONS));
    127.  
    128.                 switch (QUADFACE)
    129.                 {
    130.                     case QuadFace.TOP:
    131.                         //Assign Vertex Coordinates
    132.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    133.                         break;
    134.  
    135.                     case QuadFace.BOTTOM:
    136.                         //Assign Vertex Coordinates
    137.                         vertices[index] = new Vector3(xPos, -yPos, -zPos);
    138.                         break;
    139.  
    140.                     case QuadFace.LEFT:
    141.                         //Assign Vertex Coordinates
    142.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    143.                         break;
    144.  
    145.                     case QuadFace.RIGHT:
    146.                         //Assign Vertex Coordinates
    147.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    148.                         break;
    149.  
    150.                     case QuadFace.FRONT:
    151.                         //Assign Vertex Coordinates
    152.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    153.                         break;
    154.  
    155.                     case QuadFace.BACK:
    156.                         //Assign Vertex Coordinates
    157.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    158.                         break;
    159.                 }
    160.                 //Assign UV Coordinates
    161.                 uvs[index] = new Vector2(xUV, zUV);
    162.  
    163.                 //Spherify the vertices!
    164.                 vertices[index] = PlanetUtilities.SmoothVertices(vertices[index]);
    165.  
    166.                 float noise = PlanetUtilities.GetNoise(vertices[index], NOISEOFFSET, OCTAVES, NOISESCALE);
    167.                 //Vertex Colors Testing
    168.                 colors[index] = planetGradient.Evaluate(noise);
    169.                
    170.                 //Keep the oceans flat! - thanks alucardj!
    171.                 if (noise > WATERLEVEL)
    172.                     noise = (noise - WATERLEVEL) * (1.0f / (1.0f - WATERLEVEL));
    173.                 else
    174.                     noise = 0.0f;
    175.  
    176.                 vertices[index] *= RADIUS + (noise * WORLDHEIGHT);
    177.  
    178.                 //Manually compute Normals
    179.                 normals[index] = vertices[index].normalized;
    180.             }
    181.         }
    182.         //Create the Surface Object
    183.         CreateSurfaceMesh(vertsPerQuad, vertices, normals, uvs);
    184.     }
    185.  
    186.     void CreateSurfaceMesh(int vertsPerQuad, Vector3[] vertexArray, Vector3[] normalArray, Vector2[] uvArray)
    187.     {
    188.         //If this has already been generated, enable the renderer
    189.         if (generated)
    190.         {
    191.             renderer.enabled = true;
    192.             if (ROOT != null)
    193.                 ROOT.collider.enabled = false;
    194.             collider.enabled = true;
    195.             return;
    196.         }
    197.  
    198.         int vertBufferSize = vertsPerQuad - 1;
    199.         //Size the vertex buffer
    200.         int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    201.  
    202.         //Create a new Mesh using the Objects MeshFilter
    203.         surfaceMesh = surface.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    204.         surfaceMesh.name = surface.name;
    205.  
    206.         //Step through the Vertex Buffer
    207.         for (int triIndex = 0, vertIndex = 0, i = 0; i < vertBufferSize; i++, vertIndex++)
    208.         {
    209.             for (int j = 0; j < vertBufferSize; j++, triIndex += 6, vertIndex++)
    210.             {
    211.                 //  1
    212.                 //  | \
    213.                 //  |  \
    214.                 //  |   \
    215.                 //  0----2
    216.                 vertexBuffer[triIndex] = vertIndex;
    217.                 vertexBuffer[triIndex + 1] = vertIndex + vertsPerQuad;
    218.                 vertexBuffer[triIndex + 2] = vertIndex + 1;
    219.                 //  1----3
    220.                 //    \  |
    221.                 //     \ |
    222.                 //      \|
    223.                 //       2
    224.                 vertexBuffer[triIndex + 3] = vertIndex + vertsPerQuad;
    225.                 vertexBuffer[triIndex + 4] = vertIndex + vertsPerQuad + 1;
    226.                 vertexBuffer[triIndex + 5] = vertIndex + 1;
    227.             }
    228.         }
    229.         //Assign the Vertex Array from Generate Surface Coordinates to the Mesh Vertex Array
    230.         surfaceMesh.vertices = vertexArray;
    231.         //Assign the UV Coordinates
    232.         surfaceMesh.uv = uvArray;
    233.         //Assign the Vertex Buffer
    234.         surfaceMesh.triangles = vertexBuffer;
    235.         //Vertex Colors Testing
    236.         surfaceMesh.colors = planetColors;
    237.         //Recalculate the Normals
    238.         //surfaceMesh.RecalculateNormals();
    239.         surfaceMesh.normals = normalArray; //Done manually...
    240.         //Recalculate Bounds
    241.         surfaceMesh.RecalculateBounds();
    242.         //After the Mesh has been created, pass it back to the MeshCollider
    243.         surface.GetComponent<MeshCollider>().sharedMesh = surfaceMesh;
    244.  
    245.         //If root is ever null...Destroy!
    246.         if (ROOT != null)
    247.             ROOT.Destroy();
    248.  
    249.         generated = true;
    250.     }
    251.  
    252.     public void Destroy()
    253.     {
    254.         renderer.enabled = false;
    255.         generated = false;
    256.     }
    257.  
    258.     //Merges our quadtrees!
    259.     public void MergeQuadTree()
    260.     {
    261.         if (tree == null)
    262.             return;
    263.  
    264.         for (int i = 0; i < tree.Length; i++)
    265.         {
    266.             tree[i].MergeQuadTree();
    267.             tree[i].Destroy();
    268.         }
    269.  
    270.         tree = null;
    271.     }
    272.  
    273.     public void SubDivide()
    274.     {
    275.         tree = new QuadSurface[4];
    276.  
    277.         for (int i = 0, index = 0; i < 2; i++)
    278.         {
    279.             for (int j = 0; j < 2; j++, index++)
    280.             {
    281.                 //This is the line...makes perfect sense after I wrote it....
    282.                 tree[index] = new QuadSurface(this, LEVELOFDETAIL, this.SUBDIVISIONS + 1, i + (this.XINDEX * 2), j + (this.ZINDEX * 2), WATERLEVEL, RADIUS, NOISEOFFSET, OCTAVES, NOISESCALE, WORLDHEIGHT, TEXTURES, QUADFACE);
    283.             }
    284.         }
    285.  
    286.         Destroy();
    287.     }
    288. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. //public enum NeighborDirections { NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST }
    5. public enum Direction { NORTH, EAST, SOUTH, WEST }
    6.  
    7. public enum QuadFace { TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK }
    8.  
    9. public static class PlanetUtilities
    10. {
    11.     //From Catlike Coding!
    12.     public static Vector3 SmoothVertices(Vector3 vertex)
    13.     {
    14.         Vector3 v = vertex * 2f;
    15.         float x2 = v.x * v.x;
    16.         float y2 = v.y * v.y;
    17.         float z2 = v.z * v.z;
    18.  
    19.         Vector3 s;
    20.         s.x = v.x * Mathf.Sqrt(1f - y2 / 2f - z2 / 2f + y2 * z2 / 3f);
    21.         s.y = v.y * Mathf.Sqrt(1f - x2 / 2f - z2 / 2f + x2 * z2 / 3f);
    22.         s.z = v.z * Mathf.Sqrt(1f - x2 / 2f - y2 / 2f + x2 * y2 / 3f);
    23.  
    24.         return s;
    25.     }
    26.  
    27.     // using a Simplex Noise library :
    28.     //  http://cabbynode.net/2012/06/10/perlin-simplex-noise-for-c-and-xna/
    29.  
    30.     public static float GetNoise(Vector3 vertex, float noiseOffset, float octaves, float noiseScale)
    31.     {
    32.         // add offset
    33.         vertex.x += noiseOffset;
    34.         vertex.y += noiseOffset;
    35.         vertex.z += noiseOffset;
    36.  
    37.         // fractal noise
    38.         float amp = 1f;
    39.         float noise = 0f;
    40.         float gain = 1f;
    41.         float factor = 0f;
    42.         Vector3 calcVert;
    43.  
    44.         for (int i = 0; i < octaves; i++)
    45.         {
    46.             factor += 1f / gain;
    47.             calcVert = vertex * noiseScale * gain;
    48.             noise += Noise.Noise.GetNoise(calcVert.x, calcVert.y, calcVert.z) * (amp / gain);
    49.             gain *= 2f;
    50.         }
    51.  
    52.         // normalize noise by octave iteration factor
    53.         noise /= factor;
    54.  
    55.         return noise;
    56.     }
    57.  
    58.     public static Gradient CreateColourGradient(Gradient planetColors)
    59.     {
    60.         // using colour scale from LibNoise example : http://libnoise.sourceforge.net/tutorials/tutorial3.html
    61.         //renderer.AddGradientPoint (-1.0000, utils::Color (  0,  0, 128, 255)); // deeps
    62.         //renderer.AddGradientPoint (-0.2500, utils::Color (  0,  0, 255, 255)); // shallow
    63.         //renderer.AddGradientPoint ( 0.0000, utils::Color (  0, 128, 255, 255)); // shore
    64.         //renderer.AddGradientPoint ( 0.0625, utils::Color (240, 240,  64, 255)); // sand
    65.         //renderer.AddGradientPoint ( 0.1250, utils::Color ( 32, 160,  0, 255)); // grass
    66.         //renderer.AddGradientPoint ( 0.3750, utils::Color (224, 224,  0, 255)); // dirt
    67.         //renderer.AddGradientPoint ( 0.7500, utils::Color (128, 128, 128, 255)); // rock
    68.         //renderer.AddGradientPoint ( 1.0000, utils::Color (255, 255, 255, 255)); // snow
    69.  
    70.         planetColors = new Gradient();
    71.  
    72.         GradientColorKey[] gck = new GradientColorKey[8];
    73.         gck[0].color = CalculateGradientColour(0, 0, 128);
    74.         gck[0].time = CalculateGradientTime(-1.0000);
    75.         gck[1].color = CalculateGradientColour(0, 0, 255);
    76.         gck[1].time = CalculateGradientTime(-0.2500);
    77.         gck[2].color = CalculateGradientColour(0, 128, 255);
    78.         gck[2].time = CalculateGradientTime(0.0000);
    79.         gck[3].color = CalculateGradientColour(240, 240, 64);
    80.         gck[3].time = CalculateGradientTime(0.0625);
    81.         gck[4].color = CalculateGradientColour(32, 160, 0);
    82.         gck[4].time = CalculateGradientTime(0.1250);
    83.         gck[5].color = CalculateGradientColour(224, 224, 0);
    84.         gck[5].time = CalculateGradientTime(0.3750);
    85.         gck[6].color = CalculateGradientColour(128, 128, 128);
    86.         gck[6].time = CalculateGradientTime(0.7500);
    87.         gck[7].color = CalculateGradientColour(255, 255, 255);
    88.         gck[7].time = CalculateGradientTime(1.0000);
    89.  
    90.         GradientAlphaKey[] gak = new GradientAlphaKey[2];
    91.         gak[0].alpha = 1f;
    92.         gak[0].time = 0f;
    93.         gak[1].alpha = 1f;
    94.         gak[1].time = 1f;
    95.  
    96.         planetColors.SetKeys(gck, gak);
    97.  
    98.         return planetColors;
    99.     }
    100.  
    101.  
    102.     static Color CalculateGradientColour(int r, int g, int b)
    103.     {
    104.         return new Color((float)r / 255f, (float)g / 255f, (float)b / 255f);
    105.     }
    106.  
    107.  
    108.     static float CalculateGradientTime(double t)
    109.     {
    110.         return (float)((t + 1) * 0.5);
    111.     }
    112. }

    Code (CSharp):
    1. //SOURCE: alucardj
    2. //http://forum.unity3d.com/conversations/procedural-textures.465272/
    3.  
    4. using UnityEngine;
    5. using System.Collections;
    6. using System.Collections.Generic;
    7.  
    8. public class TextureGenerator
    9. {
    10.     public Texture2D[] surfaceTextures { get; private set; }
    11.  
    12.     private int   textureSize;
    13.     private float noiseOffset, octaves, noiseScale;
    14.     private Gradient planetColors;
    15.  
    16.     public TextureGenerator(int size, float offset, float oct, float scale, Gradient colors)
    17.     {
    18.         textureSize  = size;
    19.         noiseOffset  = offset;
    20.         octaves      = oct;
    21.         noiseScale   = scale;
    22.         planetColors = colors;
    23.  
    24.         surfaceTextures = new Texture2D[6];
    25.  
    26.         for (int i = 0; i < 6; i++ )
    27.             GenerateSurfaceTextures((QuadFace)i);
    28.     }
    29.  
    30.     void GenerateSurfaceTextures(QuadFace face)
    31.     {
    32.         Texture2D texture = new Texture2D(textureSize, textureSize);
    33.  
    34.         //Create the Color List that will be applied to the texture
    35.         List<Color> colorList = new List<Color>();
    36.         Color color;
    37.  
    38.         //Appropriately size (and create) the Vertex Array
    39.         Vector3[] vertices = new Vector3[textureSize * textureSize];
    40.  
    41.         // Calculate the increment to keep the vertices constrained to a 1x1 cube.
    42.         float increment = 1.0f / ((float)textureSize - 1);
    43.  
    44.         for (int i = 0, index = 0; i < textureSize; i++)
    45.         {
    46.             for (int j = 0; j < textureSize; j++, index++)
    47.             {
    48.                 // Vertex Coordinates
    49.                 float xPos = (float)j * increment - 0.5f;
    50.                 float yPos = 0.5f;
    51.                 float zPos = (float)i * increment - 0.5f;
    52.  
    53.                 // Assign Vertex Coordinates
    54.                 switch (face)
    55.                 {
    56.                     case QuadFace.TOP:
    57.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    58.                         break;
    59.  
    60.                     case QuadFace.BOTTOM:
    61.                         vertices[index] = new Vector3(xPos, -yPos, -zPos);
    62.                         break;
    63.  
    64.                     case QuadFace.LEFT:
    65.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    66.                         break;
    67.  
    68.                     case QuadFace.RIGHT:
    69.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    70.                         break;
    71.  
    72.                     case QuadFace.FRONT:
    73.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    74.                         break;
    75.  
    76.                     case QuadFace.BACK:
    77.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    78.                         break;
    79.                 }
    80.  
    81.                 //Spherify the Vertices!
    82.                 vertices[index] = PlanetUtilities.SmoothVertices(vertices[index]);
    83.  
    84.                 //Get noise!
    85.                 float noise = PlanetUtilities.GetNoise(vertices[index], noiseOffset, octaves, noiseScale);
    86.  
    87.                 // get Colour
    88.                 /*if (useGreyscale)
    89.                 {
    90.                     colour = new Color(noise, noise, noise, 1f);
    91.                 }
    92.                 else
    93.                 {
    94.                     colour = planetColours.Evaluate(noise);
    95.                 }*/
    96.  
    97.                 color = planetColors.Evaluate(noise);
    98.  
    99.                 // Add Colour to Texture Colour Array
    100.                 colorList.Add(color);
    101.             }
    102.         }
    103.  
    104.         // Apply Texture Colour Array to the Texture
    105.         texture.SetPixels(colorList.ToArray());
    106.         texture.wrapMode = TextureWrapMode.Clamp;
    107.         texture.Apply();
    108.  
    109.         surfaceTextures[(int)face] = texture;
    110.     }
    111. }
     
  8. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Anyone have any ideas on how to go about the above?
     
  9. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Beuller?
     
  10. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    Hey, been busy. Had a quick look trying to find out what happens in QuadSurface MergeQuadTree() ;
    adding this as the first line in that function :
    Code (csharp):
    1.  Debug.Log( "tree.Length: " + ( tree == null ? "NULL" : tree.Length.ToString() ) );
    only ever prints out NULL

    Maybe investigate why QuadSurface[] tree is not populated? If I have time I'll dig deeper too.
     
  11. MachoBrizzin

    MachoBrizzin

    Joined:
    Jul 10, 2013
    Posts:
    27
    Any updates on this stuff?
     
  12. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Sorry for the complete lack of updates. In the process of transitioning from my current job to a new one. Bought a house, moved and have been going flat out since. The last time I touched the code I was still having the issue of neighbors ignoring their neighbors subdivision level and not uniformly subdividing. Was also having issues with the trees not cleanly collapsing as you got farther away from the planet surface. The code up above is still the most current state of development as of it's date of publish. If anyone has any bright ideas on how to clean up the rough edges, please let me know! I hope to get back to work at some point in the near future, probably after the first of the year after everything kind of settles down for me.

    V/R

    Brad
     
  13. kolbosa

    kolbosa

    Joined:
    Nov 3, 2013
    Posts:
    2
    Hello BradMick, how are your doing? We still need your updates!) Keep it up, really good work.
     
  14. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    I'm actually about done with getting the Solar System Generator up and running...once that's done I'll be re-attacking this project as well. No real ETA...I kinda code a bit, write/record music, draw, play with the kids and dog, spend time with the wife and work. I was hoping that someday someone smarter than me would be like 'oh by the way, you're a super noob and this is how it should really be done...but alas, no such help has materialized....

    But yeah, once the framework for more or less realistically generating a solar system is complete, it'll be time to get into making the planets look good and also allow the player to flow from the surface to space and back again. Hooray for hobby game programmers!
     
  15. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    To prove I'm not completely dead, here's a screen shot showing the progress I've made on the Solar System Generator. This is 10 runs using Sigma Draconis as the test Star. This is a C# adaptation of source code from:

    http://www.eldacur.com/~brons/NerdCorner/StarGen/StarGen.html

    Once it's done I'll make it freely available (obviously) for any and everyone. This uses Accrete theory to generate 'earth like' solar systems around stars within certain parameters. I've got a lot of editor type work to do and the actual Atmosphere and planetary environment stuff to implement...I also need to put in place methods for ensuring habitable systems are generated (essentially it'll continue generating until that happens and tell you the seed that made it happen so you can then make that seed the base seed for the system). The StarGen source is able to give you only habitable, earth like and systems with twin habitable worlds. So the capability is there, I just have to implement it.

    Anyway, screenshot! Slow but steady progress...makes me happy! :D

    upload_2017-4-21_21-44-56.png
     
    MachoBrizzin likes this.
  16. AnyOtherProgrammer

    AnyOtherProgrammer

    Joined:
    May 29, 2016
    Posts:
    1
    Can you please upload your assets folder?
     
  17. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Hopefully I did this right...https://github.com/BradMick/StellarForge

    There's a GitHub link with the existing scripts I've created. I provide this code as is and with the understanding that it's nowhere near complete nor is it entirely functional. It's a hodge-podge of reverse engineering, tinkering and tutorials.

    References:

    TiggilyBoo's Quad Tree Planet - https://github.com/Tiggilyboo/UnityQuadTreePlanet
    Planetary Terrains v2
    Star Gen - http://www.eldacur.com/~brons/NerdCorner/StarGen/StarGen.html
    Cat Like Coding - http://catlikecoding.com/unity/tutorials/

    Hopefully this helps some folks. Also, if anyone figures out how to make the subDivision code on the planet generator work before I do and get it posted, please share. It subdivides just fine, but it doesn't un-subdivide. My goal is also to have it use specialty index buffers to create seamless terrains by polling the indices of the surrounding faces.

    This is actually a bit of step back because the texture stuff hasn't been rewritten (dont' even have UV's coded at the moment, so it really is bare bones) it's very much a work in progress but I figure i'd get it out to the wild rather than just keep sitting on it. Hopefully someone way smarter than me will be able to help out and get this thing lookin' and workin' nice!

    V/R

    Brad
     
    MachoBrizzin likes this.
  18. MachoBrizzin

    MachoBrizzin

    Joined:
    Jul 10, 2013
    Posts:
    27
    This is some great stuff! Thanks for sharing.
     
    AlucardJay likes this.
  19. EricL2

    EricL2

    Joined:
    Sep 12, 2017
    Posts:
    1
    @BradMick don't know if you're still here, but is there a way to move the planet to the position of the planet generator? Unity doesn't allow modifying the planet's transform directly.
     
  20. HansenDJ

    HansenDJ

    Joined:
    Aug 27, 2017
    Posts:
    2
    @BradMick I only have 7 months experience with Unity and with C# and practically programming in general, just the basics, so it's hard for me to follow along with this post. I really want to be able to randomly generate planets that you can walk on and this is just what I was looking for, except I wanted it to look more realistic than this but this good enough. I was just wondering if you could post how to apply all of this stuff to a new project to make it function properly using the final scripts you have provided, like what objects to create and attach these scripts too, etc. It would be very much appreciated, and good job on all your work with this.
     
  21. HansenDJ

    HansenDJ

    Joined:
    Aug 27, 2017
    Posts:
    2
    @alucardj If you know how to apply all of these scripts to a new project to make this system function properly I would really appreciate it. I need someone to help me out so I can implement this into a game I would like to make. Thanks for your input on this subject, I'm just really new and it's hard for me to understand what you guys are talking about.
     
  22. Hanowde

    Hanowde

    Joined:
    Feb 3, 2018
    Posts:
    1
    Hi, @alucardj, @BradMick and others

    Thank your for sharing und showing your project code & ideas, it helped me to understand more about how to generate surfaces by using vertices in the practical work!

    I have another question now...

    Would your like to give me idea about how to create its ocean / water for the generated planet procedurally (spherical water or ocean object within the planet)? Maybe i could use the same idea like creating the planet oder have your any better idea?


    Thank your very much!

    Carvin
     
  23. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Semi sorta back from the dead. It's about that time for me to be tinkering with this again. The focus this time around is on creating the 9 separate 'draw patterns' involved in allowing for seamless terrains.

    Attached is an image showing one of the draw patterns in action with manual sub-division for testing. (the whole thing is one giant test actually). But it shows how the terrains will be connected. Once I've finished up all of the patterns, I plan on creating a 'seamStitcher' which will...well, make things seamless.

    Once I've got that done, I've got to then figure out how to automate the LOD process, which is still the bane of my existence at this point. But, this is where this round of iteration is at! Someday I'll finish I think...but not today, lol, because it clearly never ends! :D

    Code!

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class NewTestTerrain : MonoBehaviour
    6. {
    7.     public int testSurfLOD = 2;    //Maximum surface LOD is 7 due to Unity limitations!
    8.     public int testSubDLvl = 1;
    9.  
    10.     public bool central,
    11.                 north,
    12.                 northEast,
    13.                 east,
    14.                 southEast,
    15.                 south,
    16.                 southWest,
    17.                 west,
    18.                 northWest;
    19.  
    20.     private int surfLOD,
    21.                 subDLvl,
    22.                 rowIndex,
    23.                 colIndex,
    24.                 vertsPerSurf,
    25.                 vertBufferSize;
    26.  
    27.     private Vector3[] vertexArray;
    28.  
    29.     private GameObject   surfaceObject;
    30.  
    31.     private Mesh         surfaceMesh;
    32.     private MeshCollider surfaceMeshCollider;
    33.     private MeshRenderer surfaceMeshRenderer;
    34.     private MeshFilter   surfaceMeshFilter;
    35.  
    36.     void Start()
    37.     {
    38.         Surface(testSurfLOD, testSubDLvl, 0, 0);
    39.     }
    40.  
    41.     //Initialize the surface
    42.     void Surface(int _surfLOD, int _subDLvl, int _rowIndex, int _colIndex)
    43.     {
    44.         surfLOD = _surfLOD;
    45.         subDLvl = _subDLvl;
    46.  
    47.         rowIndex = _rowIndex;
    48.         colIndex = _colIndex;
    49.  
    50.         //Verts per surface will always be a power of 2 + 1...due to Unity limits, the highest
    51.         vertsPerSurf   = (int)Mathf.Pow(2, surfLOD) + 1;
    52.         vertBufferSize = (int)Mathf.Pow(2, surfLOD);
    53.  
    54.         surfaceObject  = new GameObject("Surface Level: " + subDLvl);
    55.  
    56.         surfaceMeshFilter   = surfaceObject.AddComponent<MeshFilter>();
    57.         surfaceMeshCollider = surfaceObject.AddComponent<MeshCollider>();
    58.         surfaceMeshRenderer = surfaceObject.AddComponent<MeshRenderer>();
    59.  
    60.         //Let's create some coordinates!
    61.         GenerateSuraceCoordinates();
    62.     }
    63.  
    64.     private void GenerateSuraceCoordinates()
    65.     {    
    66.         //Create the vertex array, which will hold vertex position data
    67.         vertexArray        = new Vector3[vertsPerSurf * vertsPerSurf];
    68.  
    69.         //Coordinates are constrained to a 1x1 plane...
    70.         float increment = (1.0f / (vertsPerSurf - 1)) / Mathf.Pow(2, subDLvl);
    71.  
    72.         for (int row = 0, index = 0; row < vertsPerSurf; row++)
    73.         {
    74.             for (int col = 0; col < vertsPerSurf; col++, index++)
    75.             {
    76.                 float xPos = (float)col * increment - 0.5f + (colIndex / Mathf.Pow(2, subDLvl));
    77.                 float yPos = 0.0f;
    78.                 float zPos = (float)row * increment - 0.5f + (rowIndex / Mathf.Pow(2, subDLvl));
    79.  
    80.                 vertexArray[index] = new Vector3(xPos, yPos, zPos);
    81.             }
    82.         }
    83.  
    84.         //Create the surface!
    85.         GenerateSurface();
    86.     }
    87.  
    88.     private void GenerateSurface()
    89.     {
    90.         int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    91.  
    92.         surfaceMesh      = surfaceMeshFilter.sharedMesh = new Mesh();
    93.         surfaceMesh.name = surfaceObject.name;
    94.  
    95.         //This is the default pattern...
    96.         if (central == true)
    97.         {
    98.             for (int triIndex = 0, vertIndex = 0, row = 0; row < vertBufferSize; row++, vertIndex++)
    99.             {
    100.                 for (int col = 0; col < vertBufferSize; col++, triIndex += 6, vertIndex++)
    101.                 {
    102.                     //  +---+---+
    103.                     //  | \ | \ |
    104.                     //  1---+---+
    105.                     //  | \ | \ |
    106.                     //  0---2---+
    107.                     vertexBuffer[triIndex] = vertIndex;
    108.                     vertexBuffer[triIndex + 1] = vertIndex + vertsPerSurf;
    109.                     vertexBuffer[triIndex + 2] = vertIndex + 1;
    110.                     //  +---+---+
    111.                     //  | \ | \ |
    112.                     //  3---4---+
    113.                     //  | \ | \ |
    114.                     //  +---5---+
    115.                     vertexBuffer[triIndex + 3] = vertIndex + vertsPerSurf;
    116.                     vertexBuffer[triIndex + 4] = vertIndex + vertsPerSurf + 1;
    117.                     vertexBuffer[triIndex + 5] = vertIndex + 1;
    118.                 }
    119.             }
    120.         }
    121.  
    122.         //Eventually we need to test if this tile is north of the central tile. We do that by checking
    123.         //the row and col indices. So, if the central tile is row 3, col 3 and north is row 4, col 3,
    124.         //then north would be rowDif = 4 - 3 = 1, colDif = 3 - 3 = 0. South would be row 2, col 3, or
    125.         //rowDif = 2 - 3 = -1, colDif = 3 - 3 = 0. But, that is the gist of the concept. Once that's
    126.         //worked out, then the SeamStitcher can be created. SeamStitcher will take find the average of
    127.         //two tiles heights and set BOTH tiles heights to this value.
    128.         if (north == true)
    129.         {
    130.             int triIndex, vertIndex, row, col;
    131.  
    132.             for (triIndex = 0, vertIndex = 0, row = 0; row < vertBufferSize - 1; row++, vertIndex++)
    133.             {
    134.                 for (col = 0; col < vertBufferSize; col++, triIndex += 6, vertIndex++)
    135.                 {
    136.                     //  +---+---+
    137.                     //  | \ | \ |
    138.                     //  1---+---+
    139.                     //  | \ | \ |
    140.                     //  0---2---+
    141.                     vertexBuffer[triIndex] = vertIndex;
    142.                     vertexBuffer[triIndex + 1] = vertIndex + vertsPerSurf;
    143.                     vertexBuffer[triIndex + 2] = vertIndex + 1;
    144.                     //  +---+---+
    145.                     //  | \ | \ |
    146.                     //  3---4---+
    147.                     //  | \ | \ |
    148.                     //  +---5---+
    149.                     vertexBuffer[triIndex + 3] = vertIndex + vertsPerSurf;
    150.                     vertexBuffer[triIndex + 4] = vertIndex + vertsPerSurf + 1;
    151.                     vertexBuffer[triIndex + 5] = vertIndex + 1;
    152.                     //Debug.Log("----break----");
    153.                 }
    154.                 //Debug.Log("---break 2---");
    155.             }
    156.  
    157.             //Debug.Log("vertIndex: " + vertIndex + ", triIndex: " + triIndex);
    158.  
    159.             for (row = vertBufferSize - 1; row < vertBufferSize; row++, vertIndex += 2)
    160.             {
    161.                 for (col = 0; col < vertBufferSize; col += 2, triIndex += 9, vertIndex += 2)
    162.                 {
    163.                     //  1---+---+
    164.                     //  | \   / |
    165.                     //  0---2---+
    166.                     //  | \ | \ |
    167.                     //  +---+---+
    168.                     vertexBuffer[triIndex]     = vertIndex;
    169.                     //Debug.Log(vertexBuffer[triIndex]);
    170.                     vertexBuffer[triIndex + 1] = vertIndex + vertsPerSurf;
    171.                     //Debug.Log(vertexBuffer[triIndex + 1]);
    172.                     vertexBuffer[triIndex + 2] = vertIndex + 1;
    173.                     //Debug.Log(vertexBuffer[triIndex + 2]);
    174.                     //  3---+---4
    175.                     //  | \   / |
    176.                     //  +---5---+
    177.                     //  | \ | \ |
    178.                     //  +---+---+
    179.                     vertexBuffer[triIndex + 3] = vertIndex + vertsPerSurf;
    180.                     //Debug.Log(vertexBuffer[triIndex + 3]);
    181.                     vertexBuffer[triIndex + 4] = vertIndex + vertsPerSurf + 2;
    182.                     //Debug.Log(vertexBuffer[triIndex + 4]);
    183.                     vertexBuffer[triIndex + 5] = vertIndex + 1;
    184.                     //Debug.Log(vertexBuffer[triIndex + 5]);
    185.                     //  +---+---7
    186.                     //  | \   / |
    187.                     //  +---6---8
    188.                     //  | \ | \ |
    189.                     //  +---+---+
    190.                     vertexBuffer[triIndex + 6] = vertIndex + 1;
    191.                     //Debug.Log(vertexBuffer[triIndex + 6]);
    192.                     vertexBuffer[triIndex + 7] = vertIndex + vertsPerSurf + 2;
    193.                     //Debug.Log(vertexBuffer[triIndex + 7]);
    194.                     vertexBuffer[triIndex + 8] = vertIndex + 2;
    195.                     //Debug.Log(vertexBuffer[triIndex + 8]);
    196.                     //Debug.Log("---break 3---");
    197.                 }
    198.                 //Debug.Log("---break 4---");
    199.             }
    200.             //Debug.Log("vertIndex: " + vertIndex + ", triIndex: " + triIndex);
    201.         }
    202.  
    203.         if (south == true)
    204.         {
    205.             int triIndex,
    206.                 vertIndex,
    207.                 row, col;
    208.  
    209.             //Rather than use vertBufferSize for the row check, we know we ONLY want to run the
    210.             //following on the first row. So we can hard code 1...
    211.             for (triIndex = 0, vertIndex = 0, row = 0; row < 1; row++, vertIndex += 1)
    212.             {
    213.                 for (col = 0; col < vertBufferSize; col += 2, triIndex += 9, vertIndex += 2)
    214.                 {
    215.                     //  +---+---+
    216.                     //  | \ | \ |
    217.                     //  1---2---+
    218.                     //  | /   \ |
    219.                     //  0---+---+
    220.                     vertexBuffer[triIndex] = vertIndex;
    221.                     //Debug.Log(vertexBuffer[triIndex]);
    222.                     vertexBuffer[triIndex + 1] = vertIndex + vertsPerSurf;
    223.                     //Debug.Log(vertexBuffer[triIndex + 1]);
    224.                     vertexBuffer[triIndex + 2] = vertIndex + vertsPerSurf + 1;
    225.                     //Debug.Log(vertexBuffer[triIndex + 2]);
    226.                     //  +---+---+
    227.                     //  | \ | \ |
    228.                     //  +---4---+
    229.                     //  | /   \ |
    230.                     //  3---+---5
    231.                     vertexBuffer[triIndex + 3] = vertIndex;
    232.                     vertexBuffer[triIndex + 4] = vertIndex + vertsPerSurf + 1;
    233.                     vertexBuffer[triIndex + 5] = vertIndex + 2;
    234.                     //  +---+---+
    235.                     //  | \ | \ |
    236.                     //  +---6---7
    237.                     //  | /   \ |
    238.                     //  +---+---8
    239.                     vertexBuffer[triIndex + 6] = vertIndex + vertsPerSurf + 1;
    240.                     vertexBuffer[triIndex + 7] = vertIndex + vertsPerSurf + 2;
    241.                     vertexBuffer[triIndex + 8] = vertIndex + 2;
    242.                     Debug.Log("----break----");
    243.                 }
    244.                 Debug.Log("---break 2---");
    245.             }
    246.  
    247.             Debug.Log("vertBufferSize: " + vertBufferSize + ", vertIndex: " + vertIndex + ", triIndex: " + triIndex);
    248.  
    249.             //Same idea as above, if we stopped at 1, then we can start at 1.
    250.             for (row = 1; row < vertBufferSize; row++, vertIndex++)
    251.             {
    252.                 for (col = 0; col < vertBufferSize; col++, triIndex += 6, vertIndex++)
    253.                 {
    254.                     //  1---+---+
    255.                     //  | \ | \ |
    256.                     //  0---2---+
    257.                     //  | /   \ |
    258.                     //  +---+---+
    259.                     vertexBuffer[triIndex] = vertIndex;
    260.                     //Debug.Log(vertexBuffer[triIndex]);
    261.                     vertexBuffer[triIndex + 1] = vertIndex + vertsPerSurf;
    262.                     //Debug.Log(vertexBuffer[triIndex + 1]);
    263.                     vertexBuffer[triIndex + 2] = vertIndex + 1;
    264.                     //Debug.Log(vertexBuffer[triIndex + 2]);
    265.                     //  3---4---+
    266.                     //  | \ | \ |
    267.                     //  +---5---+
    268.                     //  | /   \ |
    269.                     //  +---+---+
    270.                     vertexBuffer[triIndex + 3] = vertIndex + vertsPerSurf;
    271.                     //Debug.Log(vertexBuffer[triIndex + 3]);
    272.                     vertexBuffer[triIndex + 4] = vertIndex + vertsPerSurf + 1;
    273.                     //Debug.Log(vertexBuffer[triIndex + 4]);
    274.                     vertexBuffer[triIndex + 5] = vertIndex + 1;
    275.                     //Debug.Log(vertexBuffer[triIndex + 5]);
    276.                     //Debug.Log("---break 3---");
    277.                 }
    278.                 //Debug.Log("---break 4---");
    279.             }
    280.             //Debug.Log("vertIndex: " + vertIndex + ", triIndex: " + triIndex);
    281.         }
    282.  
    283.  
    284.         //Assign the Vertex Array from the surface to the mesh vertex array
    285.         surfaceMesh.vertices = vertexArray;
    286.         //Assign the Vertex Buffer
    287.         surfaceMesh.triangles = vertexBuffer;
    288.         //Recalculate the Normals
    289.         //terrainMesh.normals = RecalculateNormals(vertexArray);
    290.         surfaceMesh.RecalculateNormals();
    291.         //Recalculate bounds
    292.         surfaceMesh.RecalculateBounds();
    293.         //After the Mesh has been created, pass it back to the MeshCollider
    294.         //surfaceMeshCollider.sharedMesh = surfaceMesh;
    295.     }
    296. }

    upload_2018-3-18_14-51-9.png upload_2018-3-18_14-59-16.png
     
    Last edited: Mar 18, 2018
  24. bicarbon8

    bicarbon8

    Joined:
    Sep 2, 2018
    Posts:
    32
    this is quite amazing work! I've been trying to find a somewhat working (in Unity) example to start from for QuadSphere with LoD so thanks so much for posting all your code. All the examples I could find online were incomplete, ancient and in C or for XNA and with the maths of Quad trees already wrecking my head it was really crushing to try to convert them to C# and get them working. This gives me a much more achievable starting point. I've forked your GitHub repo and if I manage to get things working I'll issue a Pull Request back to you. Thanks again!
     
  25. bicarbon8

    bicarbon8

    Joined:
    Sep 2, 2018
    Posts:
    32
    ok, so in the spirit of giving (and because this thread was such a HUGE help for me in getting this far with QuadTrees), I'm sharing the following implementation that I've developed. To use it, add an empty to your scene, add the QuadTree.cs script to the empty and set your starting size, distance for first subdivision (all subsequent subdivisions are at half the previous distance) and add the GameObject you'd like to use to measure distance (I used the Main Camera)

    QuadTree.cs
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using UnityEngine;
    5.  
    6. public class QuadTree : MonoBehaviour
    7. {
    8.     public float Size;
    9.     public GameObject Player;
    10.     public float SubdivisionDistance;
    11.  
    12.     private MeshFilter _meshFilter;
    13.     private MeshCollider _meshCollider;
    14.     private MeshRenderer _meshRenderer;
    15.  
    16.     private Quad _quad;
    17.     private Dictionary<string, QuadVert> _vertStore;
    18.  
    19.     private void Start()
    20.     {
    21.         _meshFilter = gameObject.AddComponent<MeshFilter>();
    22.         _meshCollider = gameObject.AddComponent<MeshCollider>();
    23.         _meshRenderer = gameObject.AddComponent<MeshRenderer>();
    24.         _vertStore = new Dictionary<string, QuadVert>();
    25.  
    26.         _quad = new Quad(null, QuadType.Root, 0, Size, Player, SubdivisionDistance, ref _vertStore);
    27.  
    28.         Render();
    29.     }
    30.  
    31.     private void Update()
    32.     {
    33.         if (_quad != null && _quad.Update())
    34.         {
    35.             Render();
    36.         }
    37.     }
    38.  
    39.     private void Render()
    40.     {
    41.         List<Vector3> vertices = new List<Vector3>();
    42.         _quad.GetVertices(ref vertices);
    43.         int[] triangles = _quad.GetTriangles();
    44.  
    45.         _meshFilter.mesh.Clear();
    46.         _meshFilter.mesh.vertices = vertices.ToArray();
    47.         _meshFilter.mesh.triangles = triangles;
    48.  
    49.         _meshFilter.mesh.RecalculateNormals();
    50.         _meshFilter.mesh.RecalculateBounds();
    51.     }
    52. }
    Quad.cs
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using UnityEngine;
    5.  
    6. public class Quad
    7. {
    8.     // our immediate parent node
    9.     public Quad Parent;
    10.     // Level 0 is top level node, Level 1 is a child of the top level node
    11.     public int Level;
    12.     // Size of one edge of quad in game units
    13.     public float Size;
    14.  
    15.     // Type indicates quadrant this Quad represents: TopLeft, TopRight, BottomRight, BottomLeft
    16.     public QuadType Type;
    17.     public Quad[] Children; // max of 4
    18.     public Quad[] Neighbors; // max of 4
    19.     public QuadVert[] Vertices;
    20.     public GameObject Player;
    21.     public float SubdivisionDist;
    22.  
    23.     // Are we subdivided
    24.     public bool HasChildren { get { return Children != null && Children.Any(c => c != null); } }
    25.  
    26.     private Dictionary<string, QuadVert> _vertLookupTable;
    27.  
    28.     public Quad(Quad parent, QuadType type, int level, float size, GameObject player, float subdivisionDistance, ref Dictionary<string, QuadVert> vertLookupTable)
    29.     {
    30.         Parent = parent;
    31.         Type = type;
    32.         Level = level;
    33.         Size = size;
    34.         Player = player;
    35.         SubdivisionDist = subdivisionDistance;
    36.  
    37.         _vertLookupTable = vertLookupTable;
    38.  
    39.         Children = new Quad[4];
    40.         Neighbors = new Quad[4];
    41.  
    42.         AddVertices();
    43.         AddNeighbors();
    44.     }
    45.  
    46.     public bool Update()
    47.     {
    48.         bool updated = false;
    49.  
    50.         if (Vertices.Any(v => v != null && Vector3.Distance(v.Point, Player.transform.position) <= SubdivisionDist))
    51.         {
    52.             if (!HasChildren)
    53.             {
    54.                 Subdivide();
    55.                 updated = true;
    56.             }
    57.         }
    58.         else
    59.         {
    60.             if (HasChildren)
    61.             {
    62.                 Unify();
    63.                 updated = true;
    64.             }
    65.         }
    66.  
    67.         if (HasChildren)
    68.         {
    69.             foreach(Quad child in Children)
    70.             {
    71.                 if (child != null)
    72.                 {
    73.                     updated = updated || child.Update();
    74.                 }
    75.             }
    76.         }
    77.  
    78.         return updated;
    79.     }
    80.  
    81.     public void GetVertices(ref List<Vector3> verts)
    82.     {
    83.         if (HasChildren)
    84.         {
    85.             for (var i=0; i<Children.Length; i++)
    86.             {
    87.                 var child = Children[i];
    88.                 if (child != null)
    89.                 {
    90.                     child.GetVertices(ref verts);
    91.                 }
    92.             }
    93.         }
    94.         else
    95.         {
    96.             foreach (QuadVert qp in Vertices)
    97.             {
    98.                 if (qp != null)
    99.                 {
    100.                     verts.Add(qp.Point);
    101.                     qp.Index = verts.Count - 1;
    102.                 }
    103.             }
    104.         }
    105.     }
    106.  
    107.     public int[] GetTriangles()
    108.     {
    109.         List<int> tris = new List<int>();
    110.         if (HasChildren)
    111.         {
    112.             for (var i=0; i<Children.Length; i++)
    113.             {
    114.                 var child = Children[i];
    115.                 if (child != null)
    116.                 {
    117.                     tris.AddRange(child.GetTriangles());
    118.                 }
    119.             }
    120.         }
    121.         else
    122.         {
    123.             if (Vertices[(int)VertPos.Centre].Active)
    124.             {
    125.                 // fan triangles from centre
    126.                 // 6-7-8    6-7-8    6-7-8    6-7-8    6-7-8
    127.                 // |\ /|    |\ /|    |\ /|    |\ /|    |\|/|
    128.                 // 3 4 5    3-4 5    3 4 5    3 4-5    3 4 5
    129.                 // |/ \|    |/ \|    |/|\|    |/ \|    |/ \|
    130.                 // 0-1-2 or 0-1-2 or 0-1-2 or 0-1-2 or 0-1-2 or combination of...
    131.  
    132.                 // start with Bottom or BottomLeft Triangle
    133.                 tris.Add(Vertices[(int)VertPos.Centre].Index);
    134.                 tris.Add(Vertices[(int)VertPos.BottomLeft].Index);
    135.                 if (Vertices[(int)VertPos.Bottom].Active)
    136.                 {
    137.                     tris.Add(Vertices[(int)VertPos.Bottom].Index);
    138.                     // next triangle
    139.                     tris.Add(Vertices[(int)VertPos.Centre].Index);
    140.                     tris.Add(Vertices[(int)VertPos.Bottom].Index);
    141.                 }
    142.                 tris.Add(Vertices[(int)VertPos.BottomRight].Index);
    143.  
    144.                 // next do Right or RightBottom Triangle
    145.                 tris.Add(Vertices[(int)VertPos.Centre].Index);
    146.                 tris.Add(Vertices[(int)VertPos.BottomRight].Index);
    147.                 if (Vertices[(int)VertPos.Right].Active)
    148.                 {
    149.                     tris.Add(Vertices[(int)VertPos.Right].Index);
    150.                     // next triangle
    151.                     tris.Add(Vertices[(int)VertPos.Centre].Index);
    152.                     tris.Add(Vertices[(int)VertPos.Right].Index);
    153.                 }
    154.                 tris.Add(Vertices[(int)VertPos.TopRight].Index);
    155.  
    156.                 // next do Top or TopRight Triangle
    157.                 tris.Add(Vertices[(int)VertPos.Centre].Index);
    158.                 tris.Add(Vertices[(int)VertPos.TopRight].Index);
    159.                 if (Vertices[(int)VertPos.Top].Active)
    160.                 {
    161.                     tris.Add(Vertices[(int)VertPos.Top].Index);
    162.                     // next triangle
    163.                     tris.Add(Vertices[(int)VertPos.Centre].Index);
    164.                     tris.Add(Vertices[(int)VertPos.Top].Index);
    165.                 }
    166.                 tris.Add(Vertices[(int)VertPos.TopLeft].Index);
    167.  
    168.                 // finally do Left or LeftTop Triangle
    169.                 tris.Add(Vertices[(int)VertPos.Centre].Index);
    170.                 tris.Add(Vertices[(int)VertPos.TopLeft].Index);
    171.                 if (Vertices[(int)VertPos.Left].Active)
    172.                 {
    173.                     tris.Add(Vertices[(int)VertPos.Left].Index);
    174.                     // next triangle
    175.                     tris.Add(Vertices[(int)VertPos.Centre].Index);
    176.                     tris.Add(Vertices[(int)VertPos.Left].Index);
    177.                 }
    178.                 tris.Add(Vertices[(int)VertPos.BottomLeft].Index);
    179.             }
    180.             else
    181.             {
    182.                 // draw diagonal triangles from corners
    183.                 // 6 7 8     6-7-8
    184.                 // |\         \  |
    185.                 // 3 4 5     3 4 5
    186.                 // |  \         \|
    187.                 // 0-1-2 and 0 1 2
    188.  
    189.                 // start with BottomLeft Triangle
    190.                 tris.Add(Vertices[(int)VertPos.BottomLeft].Index);
    191.                 tris.Add(Vertices[(int)VertPos.BottomRight].Index);
    192.                 tris.Add(Vertices[(int)VertPos.TopLeft].Index);
    193.                 // then add TopRight Triangle
    194.                 tris.Add(Vertices[(int)VertPos.BottomRight].Index);
    195.                 tris.Add(Vertices[(int)VertPos.TopRight].Index);
    196.                 tris.Add(Vertices[(int)VertPos.TopLeft].Index);
    197.             }
    198.         }
    199.         return tris.ToArray();
    200.     }
    201.  
    202.     /// <summary>
    203.     /// called when adding LoD to mesh
    204.     /// </summary>
    205.     public void Subdivide()
    206.     {
    207.         if (!HasChildren)
    208.         {
    209.             AddChildren();
    210.             ActivateVerts();
    211.         }
    212.     }
    213.  
    214.     /// <summary>
    215.     /// called when removing LoD from mesh
    216.     /// </summary>
    217.     public void Unify()
    218.     {
    219.         if (HasChildren)
    220.         {
    221.             RemoveChildren();
    222.         }
    223.     }
    224.  
    225.     private void AddVertices()
    226.     {
    227.         Vertices = new QuadVert[9];
    228.         switch (Type)
    229.         {
    230.             case QuadType.TopLeft:
    231.                 Vertices[(int)VertPos.TopLeft] = Parent.Vertices[(int)VertPos.TopLeft];
    232.                 Vertices[(int)VertPos.TopRight] = Parent.Vertices[(int)VertPos.Top];
    233.                 Vertices[(int)VertPos.BottomLeft] = Parent.Vertices[(int)VertPos.Left];
    234.                 Vertices[(int)VertPos.BottomRight] = Parent.Vertices[(int)VertPos.Centre];
    235.                 break;
    236.             case QuadType.TopRight:
    237.                 Vertices[(int)VertPos.TopLeft] = Parent.Vertices[(int)VertPos.Top];
    238.                 Vertices[(int)VertPos.TopRight] = Parent.Vertices[(int)VertPos.TopRight];
    239.                 Vertices[(int)VertPos.BottomLeft] = Parent.Vertices[(int)VertPos.Centre];
    240.                 Vertices[(int)VertPos.BottomRight] = Parent.Vertices[(int)VertPos.Right];
    241.                 break;
    242.             case QuadType.BottomLeft:
    243.                 Vertices[(int)VertPos.TopLeft] = Parent.Vertices[(int)VertPos.Left];
    244.                 Vertices[(int)VertPos.TopRight] = Parent.Vertices[(int)VertPos.Centre];
    245.                 Vertices[(int)VertPos.BottomLeft] = Parent.Vertices[(int)VertPos.BottomLeft];
    246.                 Vertices[(int)VertPos.BottomRight] = Parent.Vertices[(int)VertPos.Bottom];
    247.                 break;
    248.             case QuadType.BottomRight:
    249.                 Vertices[(int)VertPos.TopLeft] = Parent.Vertices[(int)VertPos.Centre];
    250.                 Vertices[(int)VertPos.TopRight] = Parent.Vertices[(int)VertPos.Right];
    251.                 Vertices[(int)VertPos.BottomLeft] = Parent.Vertices[(int)VertPos.Bottom];
    252.                 Vertices[(int)VertPos.BottomRight] = Parent.Vertices[(int)VertPos.BottomRight];
    253.                 break;
    254.             default:
    255.                 // add BottomLeft QuadVert
    256.                 var bottomLeft = new Vector3(-Size / 2, -Size / 2, 0);
    257.                 var bottomLeftKey = JsonUtility.ToJson(bottomLeft);
    258.                 if (_vertLookupTable.ContainsKey(bottomLeftKey))
    259.                 {
    260.                     Vertices[(int)VertPos.BottomLeft] = _vertLookupTable[bottomLeftKey];
    261.                 }
    262.                 else
    263.                 {
    264.                     Vertices[(int)VertPos.BottomLeft] = new QuadVert
    265.                     {
    266.                         Active = true,
    267.                         Point = bottomLeft
    268.                     };
    269.                     _vertLookupTable.Add(bottomLeftKey, Vertices[(int)VertPos.BottomLeft]);
    270.                 }
    271.                 Vertices[(int)VertPos.BottomLeft].Increment(); // tracks usage for cleanup
    272.  
    273.                 // add BottomRight QuadVert
    274.                 var bottomRight = new Vector3(Size / 2, -Size / 2, 0);
    275.                 var bottomRightKey = JsonUtility.ToJson(bottomRight);
    276.                 if (_vertLookupTable.ContainsKey(bottomRightKey))
    277.                 {
    278.                     Vertices[(int)VertPos.BottomRight] = _vertLookupTable[bottomRightKey];
    279.                 }
    280.                 else
    281.                 {
    282.                     Vertices[(int)VertPos.BottomRight] = new QuadVert
    283.                     {
    284.                         Active = true,
    285.                         Point = bottomRight
    286.                     };
    287.                     _vertLookupTable.Add(bottomRightKey, Vertices[(int)VertPos.BottomRight]);
    288.                 }
    289.                 Vertices[(int)VertPos.BottomRight].Increment(); // tracks usage for cleanup
    290.  
    291.                 // add TopLeft QuadVert
    292.                 var topLeft = new Vector3(-Size / 2, Size / 2, 0);
    293.                 var topLeftKey = JsonUtility.ToJson(topLeft);
    294.                 if (_vertLookupTable.ContainsKey(topLeftKey))
    295.                 {
    296.                     Vertices[(int)VertPos.TopLeft] = _vertLookupTable[topLeftKey];
    297.                 }
    298.                 else
    299.                 {
    300.                     Vertices[(int)VertPos.TopLeft] = new QuadVert
    301.                     {
    302.                         Active = true,
    303.                         Point = topLeft
    304.                     };
    305.                     _vertLookupTable.Add(topLeftKey, Vertices[(int)VertPos.TopLeft]);
    306.                 }
    307.                 Vertices[(int)VertPos.TopLeft].Increment(); // tracks usage for cleanup
    308.  
    309.                 // add TopRight QuadVert
    310.                 var topRight = new Vector3(Size / 2, Size / 2, 0);
    311.                 var topRightKey = JsonUtility.ToJson(topRight);
    312.                 if (_vertLookupTable.ContainsKey(topRightKey))
    313.                 {
    314.                     Vertices[(int)VertPos.TopRight] = _vertLookupTable[topRightKey];
    315.                 }
    316.                 else
    317.                 {
    318.                     Vertices[(int)VertPos.TopRight] = new QuadVert
    319.                     {
    320.                         Active = true,
    321.                         Point = topRight
    322.                     };
    323.                     _vertLookupTable.Add(topRightKey, Vertices[(int)VertPos.TopRight]);
    324.                 }
    325.                 Vertices[(int)VertPos.TopRight].Increment(); // tracks usage for cleanup
    326.                 break;
    327.         }
    328.  
    329.         // add Top QuadVert
    330.         var top = new Vector3((Vertices[(int)VertPos.TopLeft].Point.x + Vertices[(int)VertPos.TopRight].Point.x) / 2, // average gives midpoint on x
    331.                                 Vertices[(int)VertPos.TopLeft].Point.y,
    332.                                 0F);
    333.         var topKey = JsonUtility.ToJson(top);
    334.         if (_vertLookupTable.ContainsKey(topKey))
    335.         {
    336.             Vertices[(int)VertPos.Top] = _vertLookupTable[topKey];
    337.         }
    338.         else
    339.         {
    340.             Vertices[(int)VertPos.Top] = new QuadVert
    341.             {
    342.                 Active = false,
    343.                 Point = top
    344.             };
    345.             _vertLookupTable.Add(topKey, Vertices[(int)VertPos.Top]);
    346.         }
    347.         Vertices[(int)VertPos.Top].Increment(); // tracks usage for cleanup
    348.  
    349.         // add Left QuadVert
    350.         var left = new Vector3(Vertices[(int)VertPos.TopLeft].Point.x,
    351.                                 (Vertices[(int)VertPos.TopLeft].Point.y + Vertices[(int)VertPos.BottomLeft].Point.y) / 2, // average gives midpoint on y
    352.                                 0F);
    353.         var leftKey = JsonUtility.ToJson(left);
    354.         if (_vertLookupTable.ContainsKey(leftKey))
    355.         {
    356.             Vertices[(int)VertPos.Left] = _vertLookupTable[leftKey];
    357.         }
    358.         else
    359.         {
    360.             Vertices[(int)VertPos.Left] = new QuadVert
    361.             {
    362.                 Active = false,
    363.                 Point = left
    364.             };
    365.             _vertLookupTable.Add(leftKey, Vertices[(int)VertPos.Left]);
    366.         }
    367.         Vertices[(int)VertPos.Left].Increment(); // tracks usage for cleanup
    368.  
    369.         // add Centre QuadVert
    370.         var centre = new Vector3((Vertices[(int)VertPos.TopLeft].Point.x + Vertices[(int)VertPos.TopRight].Point.x) / 2, // average gives midpoint on x
    371.                                 (Vertices[(int)VertPos.TopLeft].Point.y + Vertices[(int)VertPos.BottomLeft].Point.y) / 2, // average gives midpoint on y
    372.                                 0F);
    373.         var centreKey = JsonUtility.ToJson(centre);
    374.         if (_vertLookupTable.ContainsKey(centreKey))
    375.         {
    376.             Vertices[(int)VertPos.Centre] = _vertLookupTable[centreKey];
    377.         }
    378.         else
    379.         {
    380.             Vertices[(int)VertPos.Centre] = new QuadVert
    381.             {
    382.                 Active = true,
    383.                 Point = centre
    384.             };
    385.             _vertLookupTable.Add(centreKey, Vertices[(int)VertPos.Centre]);
    386.         }
    387.         Vertices[(int)VertPos.Centre].Increment(); // tracks usage for cleanup
    388.  
    389.         // add Right QuadVert
    390.         var right = new Vector3(Vertices[(int)VertPos.TopRight].Point.x,
    391.                                 (Vertices[(int)VertPos.TopRight].Point.y + Vertices[(int)VertPos.BottomRight].Point.y) / 2, // average gives midpoint on y
    392.                                 0F);
    393.         var rightKey = JsonUtility.ToJson(right);
    394.         if (_vertLookupTable.ContainsKey(rightKey))
    395.         {
    396.             Vertices[(int)VertPos.Right] = _vertLookupTable[rightKey];
    397.         }
    398.         else
    399.         {
    400.             Vertices[(int)VertPos.Right] = new QuadVert
    401.             {
    402.                 Active = false,
    403.                 Point = right
    404.             };
    405.             _vertLookupTable.Add(rightKey, Vertices[(int)VertPos.Right]);
    406.         }
    407.         Vertices[(int)VertPos.Right].Increment(); // tracks usage for cleanup
    408.  
    409.         // add Bottom QuadVert
    410.         var bottom = new Vector3((Vertices[(int)VertPos.BottomLeft].Point.x + Vertices[(int)VertPos.BottomRight].Point.x) / 2,
    411.                                 Vertices[(int)VertPos.BottomLeft].Point.y,
    412.                                 0F);
    413.         var bottomKey = JsonUtility.ToJson(bottom);
    414.         if (_vertLookupTable.ContainsKey(bottomKey))
    415.         {
    416.             Vertices[(int)VertPos.Bottom] = _vertLookupTable[bottomKey];
    417.         }
    418.         else
    419.         {
    420.             Vertices[(int)VertPos.Bottom] = new QuadVert
    421.             {
    422.                 Active = false,
    423.                 Point = bottom
    424.             };
    425.             _vertLookupTable.Add(bottomKey, Vertices[(int)VertPos.Bottom]);
    426.         }
    427.         Vertices[(int)VertPos.Bottom].Increment(); // tracks usage for cleanup
    428.     }
    429.  
    430.     private void AddNeighbors()
    431.     {
    432.         if (Level > 0)
    433.         {
    434.             if (Parent != null)
    435.             {
    436.                 switch (Type)
    437.                 {
    438.                     case QuadType.TopLeft: //Top Left Corner
    439.                         // Right Neighbor (sibling)
    440.                         Neighbors[(int)NeighborType.Right] = Parent.Children[(int)QuadType.TopRight];
    441.                         // Bottom Neighbor (sibling)
    442.                         Neighbors[(int)NeighborType.Bottom] = Parent.Children[(int)QuadType.BottomLeft];
    443.                         break;
    444.                     case QuadType.TopRight: //Top Right Corner
    445.                         // Bottom neighbor (sibling)
    446.                         Neighbors[(int)NeighborType.Bottom] = Parent.Children[(int)QuadType.BottomRight];
    447.                         // Left neighbor (sibling)
    448.                         Neighbors[(int)NeighborType.Left] = Parent.Children[(int)QuadType.TopLeft];
    449.                         break;
    450.                     case QuadType.BottomLeft: //Bottom Left Corner
    451.                         // Top neighbor (sibling)
    452.                         Neighbors[(int)NeighborType.Top] = Parent.Children[(int)QuadType.TopLeft];
    453.                         // Right neighbor (sibling)
    454.                         Neighbors[(int)NeighborType.Right] = Parent.Children[(int)QuadType.BottomRight];
    455.                         break;
    456.                     case QuadType.BottomRight: //Bottom Right Corner
    457.                         // Top neighbor (sibling)
    458.                         Neighbors[(int)NeighborType.Top] = Parent.Children[(int)QuadType.TopRight];
    459.                         // Left neighbor (sibling)
    460.                         Neighbors[(int)NeighborType.Left] = Parent.Children[(int)QuadType.BottomLeft];
    461.                         break;
    462.                 }
    463.             }
    464.         }
    465.     }
    466.  
    467.     private void AddChildren()
    468.     {
    469.         if (Level < 7)
    470.         {
    471.             //Add bottom right (southeast) child
    472.             Children[(int)QuadType.BottomRight] = new Quad(this, QuadType.BottomRight, Level + 1, (Size / 2), Player, SubdivisionDist / 2, ref _vertLookupTable);
    473.  
    474.             //Add bottom left (southwest) child
    475.             Children[(int)QuadType.BottomLeft] = new Quad(this, QuadType.BottomLeft, Level + 1, (Size / 2), Player, SubdivisionDist / 2, ref _vertLookupTable);
    476.  
    477.             //Add top left (northwest) child
    478.             Children[(int)QuadType.TopLeft] = new Quad(this, QuadType.TopLeft, Level + 1, (Size / 2), Player, SubdivisionDist / 2, ref _vertLookupTable);
    479.          
    480.             //Add top right (northeast) child
    481.             Children[(int)QuadType.TopRight] = new Quad(this, QuadType.TopRight, Level + 1, (Size / 2), Player, SubdivisionDist / 2, ref _vertLookupTable);
    482.         }
    483.     }
    484.  
    485.     private void RemoveChildren()
    486.     {
    487.         foreach (Quad child in Children)
    488.         {
    489.             foreach (QuadVert vertex in child.Vertices)
    490.             {
    491.                 vertex.Decrement();
    492.             }
    493.         }
    494.         Children = new Quad[4];
    495.  
    496.         // clean up unused QuadVerts from lookup table
    497.         List<string> toBeRemovedFromTable = new List<string>();
    498.         foreach (string key in _vertLookupTable.Keys)
    499.         {
    500.             if (!_vertLookupTable[key].InUse)
    501.             {
    502.                 toBeRemovedFromTable.Add(key);
    503.             }
    504.         }
    505.         foreach (string key in toBeRemovedFromTable)
    506.         {
    507.             _vertLookupTable.Remove(key);
    508.         }
    509.     }
    510.  
    511.     private void ActivateVerts()
    512.     {
    513.         // on Subdivision, all edge Vertices are always enabled
    514.         Vertices[(int)VertPos.Left].Active = true;
    515.         Vertices[(int)VertPos.Top].Active = true;
    516.         Vertices[(int)VertPos.Right].Active = true;
    517.         Vertices[(int)VertPos.Bottom].Active = true;
    518.     }
    519.  
    520.     private void DeactivateVerts()
    521.     {
    522.         // on Unification, all edge Vertices are always disabled
    523.         Vertices[(int)VertPos.Left].Active = false;
    524.         Vertices[(int)VertPos.Top].Active = false;
    525.         Vertices[(int)VertPos.Right].Active = false;
    526.         Vertices[(int)VertPos.Bottom].Active = false;
    527.     }
    528. }
    529.  
    530. public class QuadVert
    531. {
    532.     private int _usedBy;
    533.  
    534.     public bool Active {get; set;}
    535.     public int Index {get; set;}
    536.     public Vector3 Point { get; set; }
    537.     public bool InUse { get { return _usedBy > 0; } }
    538.     public int Usages { get { return _usedBy; } }
    539.     public void Increment()
    540.     {
    541.         _usedBy++;
    542.     }
    543.     public void Decrement()
    544.     {
    545.         _usedBy--;
    546.     }
    547. }
    548.  
    549. public enum QuadType
    550. {
    551.     Root = -1, // no neighbors
    552.     BottomLeft = 0,
    553.     BottomRight = 1,
    554.     TopRight = 2,
    555.     TopLeft = 3
    556. }
    557.  
    558. public enum NeighborType
    559. {
    560.     Top,
    561.     Right,
    562.     Bottom,
    563.     Left
    564. }
    565.  
    566. public enum VertPos
    567. {
    568.     BottomLeft = 0,
    569.     Bottom = 1,
    570.     BottomRight = 2,
    571.     Left = 3,
    572.     Centre = 4,
    573.     Right = 5,
    574.     TopLeft = 6,
    575.     Top = 7,
    576.     TopRight = 8
    577. }
     
  26. bicarbon8

    bicarbon8

    Joined:
    Sep 2, 2018
    Posts:
    32
    updated for anyone following along. Hopefully this will be of use to someone in the future:
    QuadTree.cs
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using UnityEngine;
    5.  
    6. public class QuadTree : MonoBehaviour
    7. {
    8.     public float Size;
    9.     public GameObject Player;
    10.     public float SubdivisionDistance;
    11.     public Material SurfaceMat;
    12.     public Texture2D SurfaceTex;
    13.  
    14.     private MeshFilter _meshFilter;
    15.     private MeshCollider _meshCollider;
    16.     private MeshRenderer _meshRenderer;
    17.  
    18.     private Quad _quad;
    19.     private Dictionary<string, QuadVert> _vertStore;
    20.  
    21.     private void Start()
    22.     {
    23.         _meshFilter = gameObject.AddComponent<MeshFilter>();
    24.         _meshCollider = gameObject.AddComponent<MeshCollider>();
    25.         _meshRenderer = gameObject.AddComponent<MeshRenderer>();
    26.         _meshRenderer.material = new Material(SurfaceMat);
    27.  
    28.         _vertStore = new Dictionary<string, QuadVert>();
    29.  
    30.         _quad = new Quad(null, QuadType.Root, 0, Size, Player, SubdivisionDistance, ref _vertStore);
    31.  
    32.         Render();
    33.     }
    34.  
    35.     private void Update()
    36.     {
    37.         if (_quad != null && _quad.Update())
    38.         {
    39.             Render();
    40.         }
    41.     }
    42.  
    43.     private void Render()
    44.     {
    45.         List<Vector3> vertices = new List<Vector3>();
    46.         _quad.GetVertices(ref vertices);
    47.         int[] triangles = _quad.GetTriangles();
    48.  
    49.         _meshFilter.mesh.Clear();
    50.         _meshFilter.mesh.vertices = vertices.ToArray();
    51.         _meshFilter.mesh.triangles = triangles;
    52.  
    53.         ApplyElevation(_meshFilter.mesh);
    54.  
    55.         _meshFilter.mesh.RecalculateNormals();
    56.         _meshFilter.mesh.RecalculateBounds();
    57.     }
    58.  
    59.     public void ApplyElevation(Mesh mesh)
    60.     {
    61.         // Iterate over the vertex positions and UVs of the mesh.
    62.         var vertices = mesh.vertices;
    63.  
    64.         Debug.Log(string.Join(", ", SurfaceMat.GetTexturePropertyNames()));
    65.         //SurfaceMat.GetTexture(Shader.PropertyToID("_BumpMap")) as Texture2D;
    66.         for (int i = 0; i < vertices.Length; i++)
    67.         {
    68.             var heightMap = SurfaceTex;
    69.             int x = Mathf.FloorToInt(((vertices[i].x + (Size / 2)) / Size) * heightMap.width);
    70.             int y = Mathf.FloorToInt(((vertices[i].y + (Size / 2)) / Size) * heightMap.height);
    71.            
    72.             Color color = heightMap.GetPixel(x, y);
    73.  
    74.             // Convert the resulting color value to an elevation in meters.
    75.             float elevation = ColorToElevation(color);
    76.  
    77.             vertices[i].z = elevation;
    78.         }
    79.         // Assign the new vertex positions to the mesh.
    80.         mesh.vertices = vertices;
    81.     }
    82.  
    83.     public static float ColorToElevation(Color color)
    84.     {
    85.         // Convert from color channel values in 0.0-1.0 range to elevation in meters:
    86.         // https://mapzen.com/documentation/terrain-tiles/formats/#terrarium
    87.         // return (color.r * 256.0f * 256.0f + color.g * 256.0f + color.b) - 32768.0f;
    88.         return ((color.r * color.g * color.b) / 3) * 100;
    89.     }
    90. }
    Quad.cs
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using UnityEngine;
    5.  
    6. public class Quad
    7. {
    8.     // our immediate parent node
    9.     public Quad Parent;
    10.     // Level 0 is top level node, Level 1 is a child of the top level node
    11.     public int Level;
    12.     // Size of one edge of quad in game units
    13.     public float Size;
    14.  
    15.     // Type indicates quadrant this Quad represents: TopLeft, TopRight, BottomRight, BottomLeft
    16.     public QuadType Type;
    17.     public Quad[] Children; // max of 4
    18.     public Quad[] Neighbors; // max of 4
    19.     public QuadVert[] Vertices;
    20.     public GameObject Player;
    21.     public float SubdivisionDist;
    22.  
    23.     // Are we subdivided
    24.     public bool HasChildren { get { return Children != null && Children.Any(c => c != null); } }
    25.  
    26.     private Dictionary<string, QuadVert> _vertLookupTable;
    27.  
    28.     public Quad(Quad parent, QuadType type, int level, float size, GameObject player, float subdivisionDistance, ref Dictionary<string, QuadVert> vertLookupTable)
    29.     {
    30.         Parent = parent;
    31.         Type = type;
    32.         Level = level;
    33.         Size = size;
    34.         Player = player;
    35.         SubdivisionDist = subdivisionDistance;
    36.  
    37.         _vertLookupTable = vertLookupTable;
    38.  
    39.         Children = new Quad[4];
    40.         Neighbors = new Quad[4];
    41.  
    42.         AddVertices();
    43.     }
    44.  
    45.     public bool Update()
    46.     {
    47.         bool updated = false;
    48.  
    49.         if (Vertices.Any(v => v != null && Vector3.Distance(v.Point, Player.transform.position) <= SubdivisionDist))
    50.         {
    51.             if (!HasChildren)
    52.             {
    53.                 Subdivide();
    54.                 updated = true;
    55.             }
    56.         }
    57.         else
    58.         {
    59.             if (HasChildren)
    60.             {
    61.                 Unify();
    62.                 updated = true;
    63.             }
    64.         }
    65.  
    66.         if (HasChildren)
    67.         {
    68.             foreach(Quad child in Children)
    69.             {
    70.                 if (child != null)
    71.                 {
    72.                     updated = updated || child.Update();
    73.                 }
    74.             }
    75.         }
    76.  
    77.         return updated;
    78.     }
    79.  
    80.     public void GetVertices(ref List<Vector3> verts)
    81.     {
    82.         if (HasChildren)
    83.         {
    84.             for (var i=0; i<Children.Length; i++)
    85.             {
    86.                 var child = Children[i];
    87.                 if (child != null)
    88.                 {
    89.                     child.GetVertices(ref verts);
    90.                 }
    91.             }
    92.         }
    93.         else
    94.         {
    95.             foreach (QuadVert qp in Vertices)
    96.             {
    97.                 if (qp != null)
    98.                 {
    99.                     verts.Add(qp.Point);
    100.                     qp.Index = verts.Count - 1;
    101.                 }
    102.             }
    103.         }
    104.     }
    105.  
    106.     public int[] GetTriangles()
    107.     {
    108.         List<int> tris = new List<int>();
    109.         if (HasChildren)
    110.         {
    111.             for (var i=0; i<Children.Length; i++)
    112.             {
    113.                 var child = Children[i];
    114.                 if (child != null)
    115.                 {
    116.                     tris.AddRange(child.GetTriangles());
    117.                 }
    118.             }
    119.         }
    120.         else
    121.         {
    122.             if (Vertices[(int)VertPos.Centre].Active)
    123.             {
    124.                 // fan triangles from centre
    125.                 // 6-7-8    6-7-8    6-7-8    6-7-8    6-7-8
    126.                 // |\ /|    |\ /|    |\ /|    |\ /|    |\|/|
    127.                 // 3 4 5    3-4 5    3 4 5    3 4-5    3 4 5
    128.                 // |/ \|    |/ \|    |/|\|    |/ \|    |/ \|
    129.                 // 0-1-2 or 0-1-2 or 0-1-2 or 0-1-2 or 0-1-2 or combination of...
    130.  
    131.                 // start with Bottom or BottomLeft Triangle
    132.                 tris.Add(Vertices[(int)VertPos.Centre].Index);
    133.                 tris.Add(Vertices[(int)VertPos.BottomLeft].Index);
    134.                 if (Vertices[(int)VertPos.Bottom].Active)
    135.                 {
    136.                     tris.Add(Vertices[(int)VertPos.Bottom].Index);
    137.                     // next triangle
    138.                     tris.Add(Vertices[(int)VertPos.Centre].Index);
    139.                     tris.Add(Vertices[(int)VertPos.Bottom].Index);
    140.                 }
    141.                 tris.Add(Vertices[(int)VertPos.BottomRight].Index);
    142.  
    143.                 // next do Right or RightBottom Triangle
    144.                 tris.Add(Vertices[(int)VertPos.Centre].Index);
    145.                 tris.Add(Vertices[(int)VertPos.BottomRight].Index);
    146.                 if (Vertices[(int)VertPos.Right].Active)
    147.                 {
    148.                     tris.Add(Vertices[(int)VertPos.Right].Index);
    149.                     // next triangle
    150.                     tris.Add(Vertices[(int)VertPos.Centre].Index);
    151.                     tris.Add(Vertices[(int)VertPos.Right].Index);
    152.                 }
    153.                 tris.Add(Vertices[(int)VertPos.TopRight].Index);
    154.  
    155.                 // next do Top or TopRight Triangle
    156.                 tris.Add(Vertices[(int)VertPos.Centre].Index);
    157.                 tris.Add(Vertices[(int)VertPos.TopRight].Index);
    158.                 if (Vertices[(int)VertPos.Top].Active)
    159.                 {
    160.                     tris.Add(Vertices[(int)VertPos.Top].Index);
    161.                     // next triangle
    162.                     tris.Add(Vertices[(int)VertPos.Centre].Index);
    163.                     tris.Add(Vertices[(int)VertPos.Top].Index);
    164.                 }
    165.                 tris.Add(Vertices[(int)VertPos.TopLeft].Index);
    166.  
    167.                 // finally do Left or LeftTop Triangle
    168.                 tris.Add(Vertices[(int)VertPos.Centre].Index);
    169.                 tris.Add(Vertices[(int)VertPos.TopLeft].Index);
    170.                 if (Vertices[(int)VertPos.Left].Active)
    171.                 {
    172.                     tris.Add(Vertices[(int)VertPos.Left].Index);
    173.                     // next triangle
    174.                     tris.Add(Vertices[(int)VertPos.Centre].Index);
    175.                     tris.Add(Vertices[(int)VertPos.Left].Index);
    176.                 }
    177.                 tris.Add(Vertices[(int)VertPos.BottomLeft].Index);
    178.             }
    179.             else
    180.             {
    181.                 // draw diagonal triangles from corners
    182.                 // 6 7 8     6-7-8
    183.                 // |\         \  |
    184.                 // 3 4 5     3 4 5
    185.                 // |  \         \|
    186.                 // 0-1-2 and 0 1 2
    187.  
    188.                 // start with BottomLeft Triangle
    189.                 tris.Add(Vertices[(int)VertPos.BottomLeft].Index);
    190.                 tris.Add(Vertices[(int)VertPos.BottomRight].Index);
    191.                 tris.Add(Vertices[(int)VertPos.TopLeft].Index);
    192.                 // then add TopRight Triangle
    193.                 tris.Add(Vertices[(int)VertPos.BottomRight].Index);
    194.                 tris.Add(Vertices[(int)VertPos.TopRight].Index);
    195.                 tris.Add(Vertices[(int)VertPos.TopLeft].Index);
    196.             }
    197.         }
    198.         return tris.ToArray();
    199.     }
    200.  
    201.     /// <summary>
    202.     /// called when adding LoD to mesh
    203.     /// </summary>
    204.     public void Subdivide()
    205.     {
    206.         if (!HasChildren)
    207.         {
    208.             ActivateVerts();
    209.             AddChildren();
    210.         }
    211.     }
    212.  
    213.     /// <summary>
    214.     /// called when removing LoD from mesh
    215.     /// </summary>
    216.     public void Unify()
    217.     {
    218.         if (HasChildren)
    219.         {
    220.             RemoveChildren();
    221.             DeactivateVerts();
    222.         }
    223.     }
    224.  
    225.     private void AddVertices()
    226.     {
    227.         Vertices = new QuadVert[9];
    228.         switch (Type)
    229.         {
    230.             case QuadType.TopLeft:
    231.                 Vertices[(int)VertPos.TopLeft] = Parent.Vertices[(int)VertPos.TopLeft];
    232.                 Vertices[(int)VertPos.TopRight] = Parent.Vertices[(int)VertPos.Top];
    233.                 Vertices[(int)VertPos.BottomLeft] = Parent.Vertices[(int)VertPos.Left];
    234.                 Vertices[(int)VertPos.BottomRight] = Parent.Vertices[(int)VertPos.Centre];
    235.                 break;
    236.             case QuadType.TopRight:
    237.                 Vertices[(int)VertPos.TopLeft] = Parent.Vertices[(int)VertPos.Top];
    238.                 Vertices[(int)VertPos.TopRight] = Parent.Vertices[(int)VertPos.TopRight];
    239.                 Vertices[(int)VertPos.BottomLeft] = Parent.Vertices[(int)VertPos.Centre];
    240.                 Vertices[(int)VertPos.BottomRight] = Parent.Vertices[(int)VertPos.Right];
    241.                 break;
    242.             case QuadType.BottomLeft:
    243.                 Vertices[(int)VertPos.TopLeft] = Parent.Vertices[(int)VertPos.Left];
    244.                 Vertices[(int)VertPos.TopRight] = Parent.Vertices[(int)VertPos.Centre];
    245.                 Vertices[(int)VertPos.BottomLeft] = Parent.Vertices[(int)VertPos.BottomLeft];
    246.                 Vertices[(int)VertPos.BottomRight] = Parent.Vertices[(int)VertPos.Bottom];
    247.                 break;
    248.             case QuadType.BottomRight:
    249.                 Vertices[(int)VertPos.TopLeft] = Parent.Vertices[(int)VertPos.Centre];
    250.                 Vertices[(int)VertPos.TopRight] = Parent.Vertices[(int)VertPos.Right];
    251.                 Vertices[(int)VertPos.BottomLeft] = Parent.Vertices[(int)VertPos.Bottom];
    252.                 Vertices[(int)VertPos.BottomRight] = Parent.Vertices[(int)VertPos.BottomRight];
    253.                 break;
    254.             default:
    255.                 // add BottomLeft QuadVert
    256.                 var bottomLeft = new Vector3(-Size / 2, -Size / 2, 0);
    257.                 var bottomLeftKey = JsonUtility.ToJson(bottomLeft);
    258.                 if (_vertLookupTable.ContainsKey(bottomLeftKey))
    259.                 {
    260.                     Vertices[(int)VertPos.BottomLeft] = _vertLookupTable[bottomLeftKey];
    261.                 }
    262.                 else
    263.                 {
    264.                     Vertices[(int)VertPos.BottomLeft] = new QuadVert
    265.                     {
    266.                         Active = true,
    267.                         Point = bottomLeft
    268.                     };
    269.                     _vertLookupTable.Add(bottomLeftKey, Vertices[(int)VertPos.BottomLeft]);
    270.                 }
    271.  
    272.                 // add BottomRight QuadVert
    273.                 var bottomRight = new Vector3(Size / 2, -Size / 2, 0);
    274.                 var bottomRightKey = JsonUtility.ToJson(bottomRight);
    275.                 if (_vertLookupTable.ContainsKey(bottomRightKey))
    276.                 {
    277.                     Vertices[(int)VertPos.BottomRight] = _vertLookupTable[bottomRightKey];
    278.                 }
    279.                 else
    280.                 {
    281.                     Vertices[(int)VertPos.BottomRight] = new QuadVert
    282.                     {
    283.                         Active = true,
    284.                         Point = bottomRight
    285.                     };
    286.                     _vertLookupTable.Add(bottomRightKey, Vertices[(int)VertPos.BottomRight]);
    287.                 }
    288.  
    289.                 // add TopLeft QuadVert
    290.                 var topLeft = new Vector3(-Size / 2, Size / 2, 0);
    291.                 var topLeftKey = JsonUtility.ToJson(topLeft);
    292.                 if (_vertLookupTable.ContainsKey(topLeftKey))
    293.                 {
    294.                     Vertices[(int)VertPos.TopLeft] = _vertLookupTable[topLeftKey];
    295.                 }
    296.                 else
    297.                 {
    298.                     Vertices[(int)VertPos.TopLeft] = new QuadVert
    299.                     {
    300.                         Active = true,
    301.                         Point = topLeft
    302.                     };
    303.                     _vertLookupTable.Add(topLeftKey, Vertices[(int)VertPos.TopLeft]);
    304.                 }
    305.  
    306.                 // add TopRight QuadVert
    307.                 var topRight = new Vector3(Size / 2, Size / 2, 0);
    308.                 var topRightKey = JsonUtility.ToJson(topRight);
    309.                 if (_vertLookupTable.ContainsKey(topRightKey))
    310.                 {
    311.                     Vertices[(int)VertPos.TopRight] = _vertLookupTable[topRightKey];
    312.                 }
    313.                 else
    314.                 {
    315.                     Vertices[(int)VertPos.TopRight] = new QuadVert
    316.                     {
    317.                         Active = true,
    318.                         Point = topRight
    319.                     };
    320.                     _vertLookupTable.Add(topRightKey, Vertices[(int)VertPos.TopRight]);
    321.                 }
    322.                 break;
    323.         }
    324.  
    325.         // add Top QuadVert
    326.         var top = new Vector3((Vertices[(int)VertPos.TopLeft].Point.x + Vertices[(int)VertPos.TopRight].Point.x) / 2, // average gives midpoint on x
    327.                                 Vertices[(int)VertPos.TopLeft].Point.y,
    328.                                 0F);
    329.         var topKey = JsonUtility.ToJson(top);
    330.         if (_vertLookupTable.ContainsKey(topKey))
    331.         {
    332.             Vertices[(int)VertPos.Top] = _vertLookupTable[topKey];
    333.         }
    334.         else
    335.         {
    336.             Vertices[(int)VertPos.Top] = new QuadVert
    337.             {
    338.                 Active = false,
    339.                 Point = top
    340.             };
    341.             _vertLookupTable.Add(topKey, Vertices[(int)VertPos.Top]);
    342.         }
    343.  
    344.         // add Left QuadVert
    345.         var left = new Vector3(Vertices[(int)VertPos.TopLeft].Point.x,
    346.                                 (Vertices[(int)VertPos.TopLeft].Point.y + Vertices[(int)VertPos.BottomLeft].Point.y) / 2, // average gives midpoint on y
    347.                                 0F);
    348.         var leftKey = JsonUtility.ToJson(left);
    349.         if (_vertLookupTable.ContainsKey(leftKey))
    350.         {
    351.             Vertices[(int)VertPos.Left] = _vertLookupTable[leftKey];
    352.         }
    353.         else
    354.         {
    355.             Vertices[(int)VertPos.Left] = new QuadVert
    356.             {
    357.                 Active = false,
    358.                 Point = left
    359.             };
    360.             _vertLookupTable.Add(leftKey, Vertices[(int)VertPos.Left]);
    361.         }
    362.        
    363.         // add Centre QuadVert
    364.         var centre = new Vector3((Vertices[(int)VertPos.TopLeft].Point.x + Vertices[(int)VertPos.TopRight].Point.x) / 2, // average gives midpoint on x
    365.                                 (Vertices[(int)VertPos.TopLeft].Point.y + Vertices[(int)VertPos.BottomLeft].Point.y) / 2, // average gives midpoint on y
    366.                                 0F);
    367.         var centreKey = JsonUtility.ToJson(centre);
    368.         if (_vertLookupTable.ContainsKey(centreKey))
    369.         {
    370.             Vertices[(int)VertPos.Centre] = _vertLookupTable[centreKey];
    371.         }
    372.         else
    373.         {
    374.             Vertices[(int)VertPos.Centre] = new QuadVert
    375.             {
    376.                 Active = true,
    377.                 Point = centre
    378.             };
    379.             _vertLookupTable.Add(centreKey, Vertices[(int)VertPos.Centre]);
    380.         }
    381.  
    382.         // add Right QuadVert
    383.         var right = new Vector3(Vertices[(int)VertPos.TopRight].Point.x,
    384.                                 (Vertices[(int)VertPos.TopRight].Point.y + Vertices[(int)VertPos.BottomRight].Point.y) / 2, // average gives midpoint on y
    385.                                 0F);
    386.         var rightKey = JsonUtility.ToJson(right);
    387.         if (_vertLookupTable.ContainsKey(rightKey))
    388.         {
    389.             Vertices[(int)VertPos.Right] = _vertLookupTable[rightKey];
    390.         }
    391.         else
    392.         {
    393.             Vertices[(int)VertPos.Right] = new QuadVert
    394.             {
    395.                 Active = false,
    396.                 Point = right
    397.             };
    398.             _vertLookupTable.Add(rightKey, Vertices[(int)VertPos.Right]);
    399.         }
    400.  
    401.         // add Bottom QuadVert
    402.         var bottom = new Vector3((Vertices[(int)VertPos.BottomLeft].Point.x + Vertices[(int)VertPos.BottomRight].Point.x) / 2,
    403.                                 Vertices[(int)VertPos.BottomLeft].Point.y,
    404.                                 0F);
    405.         var bottomKey = JsonUtility.ToJson(bottom);
    406.         if (_vertLookupTable.ContainsKey(bottomKey))
    407.         {
    408.             Vertices[(int)VertPos.Bottom] = _vertLookupTable[bottomKey];
    409.         }
    410.         else
    411.         {
    412.             Vertices[(int)VertPos.Bottom] = new QuadVert
    413.             {
    414.                 Active = false,
    415.                 Point = bottom
    416.             };
    417.             _vertLookupTable.Add(bottomKey, Vertices[(int)VertPos.Bottom]);
    418.         }
    419.        
    420.         // increment usages of QuadVerts so we know when we can deactivate
    421.         Vertices[(int)VertPos.BottomLeft].Increment();
    422.         Vertices[(int)VertPos.BottomRight].Increment();
    423.         Vertices[(int)VertPos.TopLeft].Increment();
    424.         Vertices[(int)VertPos.TopRight].Increment();
    425.  
    426.         Vertices[(int)VertPos.Top].Increment();
    427.         Vertices[(int)VertPos.Left].Increment();
    428.         Vertices[(int)VertPos.Centre].Increment();
    429.         Vertices[(int)VertPos.Right].Increment();
    430.         Vertices[(int)VertPos.Bottom].Increment();
    431.     }
    432.  
    433.     private void AddChildren()
    434.     {
    435.         if (Level < 7)
    436.         {
    437.             //Add bottom right (southeast) child
    438.             Children[(int)QuadType.BottomRight] = new Quad(this, QuadType.BottomRight, Level + 1, (Size / 2), Player, SubdivisionDist / 2, ref _vertLookupTable);
    439.  
    440.             //Add bottom left (southwest) child
    441.             Children[(int)QuadType.BottomLeft] = new Quad(this, QuadType.BottomLeft, Level + 1, (Size / 2), Player, SubdivisionDist / 2, ref _vertLookupTable);
    442.  
    443.             //Add top left (northwest) child
    444.             Children[(int)QuadType.TopLeft] = new Quad(this, QuadType.TopLeft, Level + 1, (Size / 2), Player, SubdivisionDist / 2, ref _vertLookupTable);
    445.            
    446.             //Add top right (northeast) child
    447.             Children[(int)QuadType.TopRight] = new Quad(this, QuadType.TopRight, Level + 1, (Size / 2), Player, SubdivisionDist / 2, ref _vertLookupTable);
    448.         }
    449.     }
    450.  
    451.     private void RemoveChildren()
    452.     {
    453.         foreach (Quad child in Children)
    454.         {
    455.             foreach (QuadVert vertex in child.Vertices)
    456.             {
    457.                 vertex.Decrement();
    458.             }
    459.         }
    460.         Children = new Quad[4];
    461.  
    462.         // clean up unused QuadVerts from lookup table
    463.         List<string> toBeRemovedFromTable = new List<string>();
    464.         foreach (string key in _vertLookupTable.Keys)
    465.         {
    466.             if (!_vertLookupTable[key].InUse)
    467.             {
    468.                 toBeRemovedFromTable.Add(key);
    469.             }
    470.         }
    471.         foreach (string key in toBeRemovedFromTable)
    472.         {
    473.             _vertLookupTable.Remove(key);
    474.         }
    475.     }
    476.  
    477.     private void ActivateVerts()
    478.     {
    479.         // on Subdivision, all edge Vertices are always enabled
    480.         Vertices[(int)VertPos.Left].Active = true;
    481.         Vertices[(int)VertPos.Top].Active = true;
    482.         Vertices[(int)VertPos.Right].Active = true;
    483.         Vertices[(int)VertPos.Bottom].Active = true;
    484.     }
    485.  
    486.     private void DeactivateVerts()
    487.     {
    488.         // on Unification, all edge Vertices are always disabled
    489.         if (Vertices[(int)VertPos.Left].Usages < 2)
    490.         {
    491.             // we are the only one using it so it can safely be deactivated
    492.             Vertices[(int)VertPos.Left].Active = false;
    493.         }
    494.         if (Vertices[(int)VertPos.Top].Usages < 2)
    495.         {
    496.             // we are the only one using it so it can safely be deactivated
    497.             Vertices[(int)VertPos.Top].Active = false;
    498.         }
    499.         if (Vertices[(int)VertPos.Right].Usages < 2)
    500.         {
    501.             // we are the only one using it so it can safely be deactivated
    502.             Vertices[(int)VertPos.Right].Active = false;
    503.         }
    504.         if (Vertices[(int)VertPos.Bottom].Usages < 2)
    505.         {
    506.             // we are the only one using it so it can safely be deactivated
    507.             Vertices[(int)VertPos.Bottom].Active = false;
    508.         }
    509.     }
    510. }
    511.  
    512. public class QuadVert
    513. {
    514.     private int _usedBy;
    515.  
    516.     public bool Active {get; set;}
    517.     public int Index {get; set;}
    518.     public Vector3 Point { get; set; }
    519.     public bool InUse { get { return _usedBy > 0; } }
    520.     public int Usages { get { return _usedBy; } }
    521.     public void Increment()
    522.     {
    523.         _usedBy++;
    524.     }
    525.     public void Decrement()
    526.     {
    527.         _usedBy--;
    528.     }
    529. }
    530.  
    531. public enum QuadType
    532. {
    533.     Root = -1, // no neighbors
    534.     BottomLeft = 0,
    535.     BottomRight = 1,
    536.     TopRight = 2,
    537.     TopLeft = 3
    538. }
    539.  
    540. public enum VertPos
    541. {
    542.     BottomLeft = 0,
    543.     Bottom = 1,
    544.     BottomRight = 2,
    545.     Left = 3,
    546.     Centre = 4,
    547.     Right = 5,
    548.     TopLeft = 6,
    549.     Top = 7,
    550.     TopRight = 8
    551. }
     
  27. bicarbon8

    bicarbon8

    Joined:
    Sep 2, 2018
    Posts:
    32
  28. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Oh wow! This is fantastic! I'll have to sit down and play with this. The LOD bit is what has been kicking my butt royally! Conceptually, it seems so simple...but the implementation has been a pain in the butt! The code is rather out of date, and I've not really played with it in a long while, so I probably need to update it and reupload.

    Fantastic work! and I'm glad this spawned the spirit of sharing!

    Brad
     
    KelsoMRK likes this.
  29. bicarbon8

    bicarbon8

    Joined:
    Sep 2, 2018
    Posts:
    32
    I've just updated the code as I realised that the entire unification process wasn't needed and the distance calculation should have only been subdividing the closest Quad and not all quads within range. This improves the performance overall. I also added UV mappings to the mesh so you can apply a sort of Cube map texture (example textures and materials uploaded too) and I've made it work wherever you place the empty object that the _QuadSphere_ script attaches to and however that empty is rotated (also works with scaling the empty). Hope this proves helpful!
     
  30. bicarbon8

    bicarbon8

    Joined:
    Sep 2, 2018
    Posts:
    32
    Just wanted to let you know I've updated the QuadSphere yet again. When I added it to my Space Game I found that the scaled space script wasn't able to scale the planet properly to allow for landing on it when the sizes being used were around that of the actual Earth. Because of this I went back to the drawing board and fully redesigned my QuadSphere to use separate objects for each Quad. Because I was doing a full re-write, I also took the opportunity to create a cache of the possible triangles and to simplify the vertices generation process. I've pushed all the changes to github at: https://github.com/bicarbon8/QuadSphere and you can see a demo video at:


    With all the Quads being separate objects and having their centres be at the centre of their vertices (instead of the centre of the Sphere itself), you can now independently scale each Quad. Hope this proves useful for someone!
     
    HunterNacho338 and supercow21 like this.
  31. HunterNacho338

    HunterNacho338

    Joined:
    Aug 30, 2015
    Posts:
    17
    This looks great, man! You've obviously put in some work. Thanks for sharing it.
     
  32. bb8_1

    bb8_1

    Joined:
    Jan 20, 2019
    Posts:
    100
    Great work!
     
  33. Soulice

    Soulice

    Joined:
    Aug 18, 2014
    Posts:
    69
    @bicarbon8 Gorgeous! So you were able to land on your planet? What did you do to make that work? Should the capsule in your demo "land" on the planet?