Search Unity

Procedural Planet Terrain Engine

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

  1. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Howdy!

    Back again, this time I'm working on a Procedural planet generator. So far I've been able to get a Subdivided Plane converted into a sphere (victory!) but am hitting some snags with the UV side of the house. I more or less understand the concept...I think. I essentially need the four corners of a subdivided square, but I'm not sure how that then gets applied to the pieces composing the quad face....

    Attached is a screen grab of what I've got and the code as it sits right now. The textures are manually applied because I've been focusing on getting the geometry right. So the UV coordinates per patch object are working great, so I know I at least haven't screwed those up to bad. Now I just need each face to be one continuous UV space, which will then be mapped with a Cube Map...and procedural textures.

    Yeah, so...basically: I need help turning 4 (or however many sub-divisions the user has) into 1 large UV space instead of 4 or 6 or 8 or 3 or whatever individual UV spaces.

    Code!

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3. using System;
    4.  
    5. public class ProceduralPlanet : MonoBehaviour
    6. {
    7.     //Define the number of Subdivions for patch side.
    8.     public int patchSubdivisions = 1;
    9.     //Define the number of vertices per of each patch segment.
    10.     public int patchResolution = 2;
    11.     //Define the planets radius
    12.     public int planetRadius = 10;
    13.  
    14.     public Vector3[] patchVertices;
    15.  
    16.     private int patchID = 0;
    17.     private int vertIndex = 0;
    18.  
    19.     public enum CubeIndex { TOP, BOTTOM, FRONT, BACK, LEFT, RIGHT }
    20.  
    21.     public List<SurfacePatch> surfacePatches = new List<SurfacePatch>();
    22.  
    23.     void Start()
    24.     {
    25.         patchVertices = new Vector3[((patchSubdivisions * patchSubdivisions) * 4) * 6];
    26.         // First we have define our dimensions:
    27.         //          +Z
    28.         //   -1,1    |    1,1
    29.         //      +----|----+
    30.         //      |    |    |
    31.         //-X --------0-------- +X
    32.         //      |    |    |
    33.         //      +----|----+
    34.         //  -1,-1    |    1,-1
    35.         //          -Z
    36.         //
    37.         // First we have to figure out what our distance increment is going to be. Since we
    38.         // know that our total width is 2 we can figure that out using the following:
    39.         float increment = 2.0f / patchSubdivisions;
    40.         // We can then find the patchHalfWidth using the following:
    41.         float patchHalfWidth = (patchSubdivisions / 2.0f) * increment;
    42.         //Next we plot the vertices for all of the sub-patches:
    43.         if (patchSubdivisions > 0)
    44.         {
    45.             //Loop through the 'z' positions
    46.             for (int z = 0; z < patchSubdivisions; z++)
    47.             {
    48.                 //Each time through we define the starting 'z' position for each sub-patch and the
    49.                 //next 'z' position of the sub-patch.
    50.                 float _z  = z * increment;
    51.                 float _z1 = (z + 1) * increment;
    52.            
    53.                 for (int x = 0; x < patchSubdivisions; x++)
    54.                 {
    55.                     //Each time through we define the starting 'z' position for each sub-patch and the
    56.                     //next 'z' position of the sub-patch.
    57.                     float _x  = x * increment;
    58.                     float _x1 = (x + 1) * increment;
    59.  
    60.                     //Pass our generated data to CreateSubPatch which then defines the coordinates for
    61.                     //each side of the Cube.
    62.                     for (int i = 0; i < 6; i++)
    63.                     {
    64.                         CreatePatch(patchHalfWidth, _x, _x1, _z, _z1, (CubeIndex)i, x, z);
    65.                     }                  
    66.                 }            
    67.             }
    68.         }
    69.     }
    70.  
    71.     void CreatePatch(float half, float x, float x1, float z, float z1,  CubeIndex index, int sX, int sZ)
    72.     {
    73.         //A Vector Array to hold our Patch Vertices...
    74.         Vector3 lowLeft  = Vector3.zero;
    75.         Vector3 topLeft  = Vector3.zero;
    76.         Vector3 topRight = Vector3.zero;
    77.         Vector3 lowRight = Vector3.zero;
    78.  
    79.         switch (index)
    80.         {
    81.             case CubeIndex.TOP:
    82.                 lowLeft = new Vector3(x - half, half, z - half);
    83.                 topLeft = new Vector3(x - half, half, z1 - half);
    84.                 topRight = new Vector3(x1 - half, half, z1 - half);
    85.                 lowRight = new Vector3(x1 - half, half, z - half);
    86.  
    87.                 //patchVertices[vertIndex] = lowLeft;
    88.                 //patchVertices[vertIndex + 1] = topLeft;
    89.                 //patchVertices[vertIndex + 2] = topRight;
    90.                 //patchVertices[vertIndex + 3] = lowRight;
    91.                 break;
    92.  
    93.             case CubeIndex.BOTTOM:
    94.                 lowLeft = new Vector3(x - half, -half, z - half);
    95.                 topLeft = new Vector3(x - half, -half, z1 - half);
    96.                 topRight = new Vector3(x1 - half, -half, z1 - half);
    97.                 lowRight = new Vector3(x1 - half, -half, z - half);
    98.            
    99.                 //patchVertices[vertIndex] = lowLeft;
    100.                 //patchVertices[vertIndex + 1] = topLeft;
    101.                 //patchVertices[vertIndex + 2] = topRight;
    102.                 //patchVertices[vertIndex + 3] = lowRight;
    103.                 break;
    104.  
    105.             case CubeIndex.FRONT:
    106.                 lowLeft = new Vector3(x - half, half - x1, half);
    107.                 topLeft = new Vector3(x - half, half - z, half);
    108.                 topRight = new Vector3(x1 - half, half - z, half);
    109.                 lowRight = new Vector3(x1 - half, half - z1, half);
    110.                            
    111.                 //patchVertices[vertIndex] = lowLeft;
    112.                 //patchVertices[vertIndex + 1] = topLeft;
    113.                 //patchVertices[vertIndex + 2] = topRight;
    114.                 //patchVertices[vertIndex + 3] = lowRight;
    115.                 break;
    116.  
    117.             case CubeIndex.BACK:
    118.                 lowLeft = new Vector3(x - half, half - x1, -half);
    119.                 topLeft = new Vector3(x - half, half - z, -half);
    120.                 topRight = new Vector3(x1 - half, half - z, -half);
    121.                 lowRight = new Vector3(x1 - half, half - z1, -half);
    122.                            
    123.                 //patchVertices[vertIndex] = lowLeft;
    124.                 //patchVertices[vertIndex + 1] = topLeft;
    125.                 //patchVertices[vertIndex + 2] = topRight;
    126.                 //patchVertices[vertIndex + 3] = lowRight;
    127.                 break;
    128.  
    129.             case CubeIndex.LEFT:
    130.                 lowLeft = new Vector3(-half, half - x1, -half + x);
    131.                 topLeft = new Vector3(-half, half - z, -half + x);
    132.                 topRight = new Vector3(-half, half - z, -half + x1);
    133.                 lowRight = new Vector3(-half, half - z1, -half + x1);
    134.                            
    135.                 patchVertices[vertIndex] = lowLeft;
    136.                 patchVertices[vertIndex + 1] = topLeft;
    137.                 patchVertices[vertIndex + 2] = topRight;
    138.                 patchVertices[vertIndex + 3] = lowRight;
    139.                 break;
    140.  
    141.             case CubeIndex.RIGHT:
    142.                 lowLeft = new Vector3(half, half - x1, -half + x);
    143.                 topLeft = new Vector3(half, half - z, -half + x);
    144.                 topRight = new Vector3(half, half - z, -half + x1);
    145.                 lowRight = new Vector3(half, half - z1, -half + x1);
    146.                            
    147.                 //patchVertices[vertIndex] = lowLeft;
    148.                 //patchVertices[vertIndex + 1] = topLeft;
    149.                 //patchVertices[vertIndex + 2] = topRight;
    150.                 //patchVertices[vertIndex + 3] = lowRight;
    151.                 break;
    152.         }
    153.  
    154.         CreatePatchObject(patchID, lowLeft, topLeft, topRight, lowRight, index, sX, sZ);
    155.         //Increment the patchID number after the sub-patch has been created.
    156.         patchID++;
    157.         vertIndex += 4;
    158.     }
    159.  
    160.     void CreatePatchObject(int id, Vector3 lowLeft, Vector3 topLeft, Vector3 topRight, Vector3 lowRight,  CubeIndex index, int sX, int sZ)
    161.     {
    162.         GameObject patchObject = new GameObject(id + " " + index + " PatchObject (" + sX + " , " + sZ + ")");
    163.         patchObject.layer = gameObject.layer;
    164.         patchObject.tag = gameObject.tag;
    165.         patchObject.transform.parent = transform;
    166.         patchObject.transform.position = transform.position;
    167.    
    168.         SurfacePatch patch = patchObject.AddComponent<SurfacePatch>();
    169.         patch.InitPatchGenerator(lowLeft, topLeft, topRight, lowRight, this, index, null, sX, sZ);
    170.         patch.GeneratePatchCoordinates(patchSubdivisions, patchResolution);
    171.         patch.Add(patch);
    172.     }
    173.  
    174.  
    175.  
    176.     /*private void OnDrawGizmos ()
    177.     {
    178.         if (patchVertices == null)
    179.         {
    180.             return;
    181.         }
    182.  
    183.         for (int i = 0; i < patchVertices.Length; i++)
    184.         {
    185.             Gizmos.color = Color.black;
    186.             Gizmos.DrawWireSphere(patchVertices[i], 0.0625f);
    187.         }
    188.         //Gizmos.color = Color.yellow;
    189.         //Gizmos.DrawRay(vertices[i], normals[i]);
    190.     }*/
    191. }
    192.  
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class SurfacePatch : MonoBehaviour
    5. {
    6.     public Vector3 _lowLeft;
    7.     public Vector3 _topLeft;
    8.     public Vector3 _topRight;
    9.     public Vector3 _lowRight;
    10.  
    11.     public Mesh patch;
    12.  
    13.     public ProceduralPlanet _planet;
    14.     public SurfacePatch _parent;
    15.     public ProceduralPlanet.CubeIndex _index;
    16.     public int _sX, _sZ;
    17.  
    18.     float patchWidth;
    19.     private Vector3[] patchVertices;
    20.  
    21.     private int vertIndex = 0;
    22.     private int[] vertBuffer;
    23.     private Vector2[] patchUV;
    24.     private Vector3[] patchNormals;
    25.  
    26.     public void InitPatchGenerator(Vector3 lowLeft, Vector3 topLeft, Vector3 topRight, Vector3 lowRight, ProceduralPlanet planet, ProceduralPlanet.CubeIndex index, SurfacePatch parent, int sX, int sZ)
    27.     {
    28.         _planet = planet;
    29.         _index = index;
    30.         _parent = parent;
    31.         _sX = sX;
    32.         _sZ = sZ;
    33.  
    34.         _lowLeft = lowLeft;
    35.         _topLeft = topLeft;
    36.         _topRight = topRight;
    37.         _lowRight = lowRight;
    38.  
    39.         if (patch == null)
    40.         {
    41.             MeshFilter meshFilter = gameObject.AddComponent<MeshFilter>();
    42.             patch = meshFilter.sharedMesh = new Mesh();
    43.  
    44.             gameObject.AddComponent<MeshRenderer>();
    45.             GetComponent<Renderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    46.             GetComponent<Renderer>().receiveShadows = true;
    47.             GetComponent<Renderer>().enabled = true;
    48.         }
    49.     }
    50.  
    51.     public void GeneratePatchCoordinates(int patchSubdivisions, int patchResolution)
    52.     {
    53.         patchVertices = new Vector3[patchResolution * patchResolution];
    54.         vertBuffer = new int[(patchResolution - 1) * (patchResolution - 1) * 6];
    55.         patchUV = new Vector2[patchVertices.Length];
    56.         patchNormals = new Vector3[patchVertices.Length];
    57.  
    58.         if (_index == ProceduralPlanet.CubeIndex.FRONT || _index == ProceduralPlanet.CubeIndex.BACK || _index == ProceduralPlanet.CubeIndex.TOP || _index == ProceduralPlanet.CubeIndex.BOTTOM)
    59.         {
    60.             patchWidth = _topRight.x - _lowLeft.x;
    61.         }
    62.  
    63.         if (_index == ProceduralPlanet.CubeIndex.LEFT || _index == ProceduralPlanet.CubeIndex.RIGHT)
    64.         {
    65.             patchWidth = _topRight.z - _lowLeft.z;
    66.         }
    67.  
    68.         float increment = patchWidth / (patchResolution - 1);
    69.         float patchHalfWidth = ((patchResolution - 1) / patchWidth) * increment;
    70.  
    71.         for (int z = 0; z < patchResolution; z++)
    72.         {
    73.             float _z = z * increment;
    74.             float _z1 = (z + 1) * increment;
    75.  
    76.             for (int x = 0; x < patchResolution; x++)
    77.             {
    78.                 float _x = x * increment;
    79.                 float _x1 = (x + 1) * increment;
    80.  
    81.                 CreatePatchMesh(patchHalfWidth, _x, _x1, _z, _z1, patchSubdivisions, patchResolution);
    82.             }
    83.         }
    84.     }
    85.  
    86.     private void CreatePatchMesh(float half, float x, float x1, float z, float z1, int subDiv, int resolution)
    87.     {
    88.         float xIncrement = ((float)_sX / (float)subDiv) * 2;
    89.         float zIncrement = ((float)_sZ / (float)subDiv) * 2;
    90.  
    91.         switch(_index)
    92.         {
    93.             case ProceduralPlanet.CubeIndex.TOP:
    94.                 patchVertices[vertIndex] = new Vector3(x - half + xIncrement, _lowLeft.y, z - half + zIncrement);
    95.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    96.                 patchVertices[vertIndex] *= _planet.planetRadius;
    97.                 //Assign UV coordinates
    98.                 patchUV[vertIndex] = new Vector2(x, z);
    99.                 //Draw the triangles
    100.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    101.                 {
    102.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    103.                     {
    104.                         vertBuffer[ti] = vi;
    105.                         vertBuffer[ti + 1] = vi + resolution;
    106.                         vertBuffer[ti + 2] = vi + 1;
    107.  
    108.                         vertBuffer[ti + 3] = vi + resolution;
    109.                         vertBuffer[ti + 4] = vi + resolution + 1;
    110.                         vertBuffer[ti + 5] = vi + 1;
    111.                     }
    112.                 }
    113.                 break;
    114.  
    115.             case ProceduralPlanet.CubeIndex.BOTTOM:
    116.                 patchVertices[vertIndex] = new Vector3(x - half + xIncrement, _lowLeft.y, z - half + zIncrement);
    117.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    118.                 patchVertices[vertIndex] *= _planet.planetRadius;
    119.                 //Assign UV coordinates
    120.                 patchUV[vertIndex] = new Vector2(x, z);
    121.                 //Draw the triangles
    122.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    123.                 {
    124.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    125.                     {
    126.                         vertBuffer[ti] = vi + 1;
    127.                         vertBuffer[ti + 1] = vi + resolution;
    128.                         vertBuffer[ti + 2] = vi;
    129.  
    130.                         vertBuffer[ti + 3] = vi + 1;
    131.                         vertBuffer[ti + 4] = vi + resolution + 1;
    132.                         vertBuffer[ti + 5] = vi + resolution;
    133.                     }
    134.                 }
    135.                 break;
    136.  
    137.             case ProceduralPlanet.CubeIndex.FRONT:
    138.                 patchVertices[vertIndex] = new Vector3(x - half + xIncrement, z - half + zIncrement, _lowLeft.z);
    139.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    140.                 patchVertices[vertIndex] *= _planet.planetRadius;
    141.                 //Assign UV coordinates
    142.                 patchUV[vertIndex] = new Vector2(x, z);
    143.                 //Draw the triangles
    144.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    145.                 {
    146.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    147.                     {
    148.                         vertBuffer[ti] = vi + 1;
    149.                         vertBuffer[ti + 1] = vi + resolution;
    150.                         vertBuffer[ti + 2] = vi;
    151.  
    152.                         vertBuffer[ti + 3] = vi + 1;
    153.                         vertBuffer[ti + 4] = vi + resolution + 1;
    154.                         vertBuffer[ti + 5] = vi + resolution;
    155.                     }
    156.                 }
    157.                 break;
    158.        
    159.             case ProceduralPlanet.CubeIndex.BACK:
    160.                 patchVertices[vertIndex] = new Vector3(x - half + xIncrement, z - half + zIncrement, _lowLeft.z);
    161.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    162.                 patchVertices[vertIndex] *= _planet.planetRadius;
    163.                 //Assign UV coordinates
    164.                 patchUV[vertIndex] = new Vector2(x, z);
    165.                 //Draw the triangles
    166.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    167.                 {
    168.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    169.                     {
    170.                         vertBuffer[ti] = vi;
    171.                         vertBuffer[ti + 1] = vi + resolution;
    172.                         vertBuffer[ti + 2] = vi + 1;
    173.  
    174.                         vertBuffer[ti + 3] = vi + resolution;
    175.                         vertBuffer[ti + 4] = vi + resolution + 1;
    176.                         vertBuffer[ti + 5] = vi + 1;
    177.                     }
    178.                 }
    179.                 break;
    180.  
    181.             case ProceduralPlanet.CubeIndex.LEFT:
    182.                 patchVertices[vertIndex] = new Vector3(_lowLeft.x, x - half + zIncrement, -half + z + xIncrement);
    183.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    184.                 patchVertices[vertIndex] *= _planet.planetRadius;
    185.                 //Assign UV coordinates
    186.                 patchUV[vertIndex] = new Vector2(x, z);
    187.                 //Draw the triangles
    188.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    189.                 {
    190.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    191.                     {
    192.                         vertBuffer[ti] = vi;
    193.                         vertBuffer[ti + 1] = vi + resolution;
    194.                         vertBuffer[ti + 2] = vi + 1;
    195.  
    196.                         vertBuffer[ti + 3] = vi + resolution;
    197.                         vertBuffer[ti + 4] = vi + resolution + 1;
    198.                         vertBuffer[ti + 5] = vi + 1;
    199.                     }
    200.                 }
    201.                 break;
    202.  
    203.             case ProceduralPlanet.CubeIndex.RIGHT:
    204.                 patchVertices[vertIndex] = new Vector3(_lowLeft.x, x - half + zIncrement, -half + z + xIncrement);
    205.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    206.                 patchVertices[vertIndex] *= _planet.planetRadius;
    207.                 //Assign UV coordinates
    208.                 patchUV[vertIndex] = new Vector2(x, z);
    209.                 //Draw the triangles
    210.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    211.                 {
    212.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    213.                     {
    214.                         vertBuffer[ti] = vi + 1;
    215.                         vertBuffer[ti + 1] = vi + resolution;
    216.                         vertBuffer[ti + 2] = vi;
    217.  
    218.                         vertBuffer[ti + 3] = vi + 1;
    219.                         vertBuffer[ti + 4] = vi + resolution + 1;
    220.                         vertBuffer[ti + 5] = vi + resolution;
    221.                     }
    222.                 }
    223.                 break;
    224.         }
    225.  
    226.         patch.vertices = patchVertices;
    227.         patch.uv = patchUV;
    228.         patch.triangles = vertBuffer;
    229.         patch.RecalculateNormals();
    230.  
    231.         vertIndex++;
    232.     }
    233.  
    234.     public void Add(SurfacePatch patch)
    235.     {
    236.  
    237.     }
    238.  
    239.     public static Vector3 MapToSphere(Vector3 point)
    240.     {
    241.         float dX2, dY2, dZ2;
    242.         float dX2Half, dY2Half, dZ2Half;
    243.  
    244.         dX2 = point.x * point.x;
    245.         dY2 = point.y * point.y;
    246.         dZ2 = point.z * point.z;
    247.  
    248.         dX2Half = dX2 * 0.5f;
    249.         dY2Half = dY2 * 0.5f;
    250.         dZ2Half = dZ2 * 0.5f;
    251.  
    252.         point.x = point.x * Mathf.Sqrt(1f - dY2Half - dZ2Half + (dY2 * dZ2) * (1f / 3f));
    253.         point.y = point.y * Mathf.Sqrt(1f - dZ2Half - dX2Half + (dZ2 * dX2) * (1f / 3f));
    254.         point.z = point.z * Mathf.Sqrt(1f - dX2Half - dY2Half + (dX2 * dY2) * (1f / 3f));
    255.  
    256.         return point;
    257.     }
    258.  
    259.     /*private void OnDrawGizmos()
    260.     {
    261.         if (patchVertices == null)
    262.         {
    263.             return;
    264.         }
    265.  
    266.         for (int i = 0; i < patchVertices.Length; i++)
    267.         {
    268.             if (_index == ProceduralPlanet.CubeIndex.TOP)
    269.             {
    270.                 Gizmos.color = Color.red;
    271.             }
    272.  
    273.             if (_index == ProceduralPlanet.CubeIndex.BOTTOM)
    274.             {
    275.                 Gizmos.color = Color.magenta;
    276.             }
    277.  
    278.             if (_index == ProceduralPlanet.CubeIndex.FRONT)
    279.             {
    280.                 Gizmos.color = Color.green;
    281.             }
    282.  
    283.             if (_index == ProceduralPlanet.CubeIndex.BACK)
    284.             {
    285.                 Gizmos.color = Color.blue;
    286.             }
    287.  
    288.             Gizmos.DrawSphere(patchVertices[i], 0.0125f);
    289.         }
    290.         //Gizmos.color = Color.yellow;
    291.         //Gizmos.DrawRay(vertices[i], normals[i]);
    292.     }*/
    293. }
    294.  
    V/R

    Brad
     

    Attached Files:

    Last edited: Sep 2, 2016
    LeftyRighty likes this.
  2. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Forgot...the relevant code is located in the CreatePatchMesh function in the case statements.
     
  3. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Well, good news! I solved the UV issue I was having, but now I'm having issues applying the perlin noise as a height modifier to my vertices...Here is a screen shot of what's going on and also the most current code. Any help would be awesome! I suspect the issue lies in how I'm handling stepping through the texture to find the height info...The first tile on the top part of the sphere has zero deformation and then the tile to the right of it looks kind of right...but not really. You can see the craziness of the other tiles as well. Any help sorting this out would be great!

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3. using System;
    4. using LibNoise;
    5. using LibNoise.Generator;
    6. using LibNoise.Operator;
    7.  
    8.  
    9.  
    10. public class ProceduralPlanet : MonoBehaviour
    11. {
    12.     //Define the number of Subdivions for patch side.
    13.     public int patchSubdivisions = 1;
    14.     //Define the number of vertices per of each patch segment.
    15.     public int patchResolution = 2;
    16.     //Define the planets radius
    17.     public int planetRadius = 10;
    18.  
    19.     public float xOrg = 10.0f;
    20.     public float yOrg = 10.0f;
    21.     public float scale = 1.0f;
    22.     public int textureSize = 128;
    23.  
    24.     //public Vector3[] patchVertices;
    25.  
    26.     private int patchID = 0;
    27.     //private int vertIndex = 0;
    28.  
    29.     public enum CubeIndex { TOP, BOTTOM, FRONT, BACK, LEFT, RIGHT }
    30.  
    31.     private Material baseMaterial;
    32.     public Texture2D[] textures = new Texture2D[6];
    33.     Color[] pixelColor;
    34.  
    35.     public List<SurfacePatch> surfacePatches = new List<SurfacePatch>();
    36.  
    37.     void Start()
    38.     {
    39.         pixelColor = new Color[textureSize * textureSize];
    40.         //patchVertices = new Vector3[((patchSubdivisions * patchSubdivisions) * 4) * 6];
    41.         // First we have define our dimensions:
    42.         //          +Z
    43.         //   -1,1    |    1,1
    44.         //      +----|----+
    45.         //      |    |    |
    46.         //-X --------0-------- +X
    47.         //      |    |    |
    48.         //      +----|----+
    49.         //  -1,-1    |    1,-1
    50.         //          -Z
    51.         //
    52.         // First we have to figure out what our distance increment is going to be. Since we
    53.         // know that our total width is 2 we can figure that out using the following:
    54.         float increment = 2.0f / patchSubdivisions;
    55.         // We can then find the patchHalfWidth using the following:
    56.         float patchHalfWidth = (patchSubdivisions / 2.0f) * increment;
    57.         //Next we plot the vertices for all of the sub-patches:
    58.         if (patchSubdivisions > 0)
    59.         {
    60.             //Loop through the 'z' positions
    61.             for (int z = 0; z < patchSubdivisions; z++)
    62.             {
    63.                 //Each time through we define the starting 'z' position for each sub-patch and the
    64.                 //next 'z' position of the sub-patch.
    65.                 float _z  = z * increment;
    66.                 float _z1 = (z + 1) * increment;
    67.                
    68.                 for (int x = 0; x < patchSubdivisions; x++)
    69.                 {
    70.                     //Each time through we define the starting 'z' position for each sub-patch and the
    71.                     //next 'z' position of the sub-patch.
    72.                     float _x  = x * increment;
    73.                     float _x1 = (x + 1) * increment;
    74.  
    75.                     //Pass our generated data to CreateSubPatch which then defines the coordinates for
    76.                     //each side of the Cube.
    77.                     for (int i = 0; i < 6; i++)
    78.                     {
    79.                         CreatePatch(patchHalfWidth, _x, _x1, _z, _z1, (CubeIndex)i, x, z);
    80.                     }                      
    81.                 }              
    82.             }
    83.         }
    84.     }
    85.  
    86.     void CreatePatch(float half, float x, float x1, float z, float z1,  CubeIndex index, int sX, int sZ)
    87.     {
    88.         //A Vector Array to hold our Patch Vertices...
    89.         Vector3 lowLeft  = Vector3.zero;
    90.         Vector3 topLeft  = Vector3.zero;
    91.         Vector3 topRight = Vector3.zero;
    92.         Vector3 lowRight = Vector3.zero;
    93.  
    94.         switch (index)
    95.         {
    96.             case CubeIndex.TOP:
    97.                 lowLeft = new Vector3(x - half, half, z - half);
    98.                 topLeft = new Vector3(x - half, half, z1 - half);
    99.                 topRight = new Vector3(x1 - half, half, z1 - half);
    100.                 lowRight = new Vector3(x1 - half, half, z - half);
    101.  
    102.                 //patchVertices[vertIndex] = lowLeft;
    103.                 //patchVertices[vertIndex + 1] = topLeft;
    104.                 //patchVertices[vertIndex + 2] = topRight;
    105.                 //patchVertices[vertIndex + 3] = lowRight;
    106.                 break;
    107.  
    108.             case CubeIndex.BOTTOM:
    109.                 lowLeft = new Vector3(x - half, -half, z - half);
    110.                 topLeft = new Vector3(x - half, -half, z1 - half);
    111.                 topRight = new Vector3(x1 - half, -half, z1 - half);
    112.                 lowRight = new Vector3(x1 - half, -half, z - half);
    113.                
    114.                 //patchVertices[vertIndex] = lowLeft;
    115.                 //patchVertices[vertIndex + 1] = topLeft;
    116.                 //patchVertices[vertIndex + 2] = topRight;
    117.                 //patchVertices[vertIndex + 3] = lowRight;
    118.                 break;
    119.  
    120.             case CubeIndex.FRONT:
    121.                 lowLeft = new Vector3(x - half, half - x1, half);
    122.                 topLeft = new Vector3(x - half, half - z, half);
    123.                 topRight = new Vector3(x1 - half, half - z, half);
    124.                 lowRight = new Vector3(x1 - half, half - z1, half);
    125.                                
    126.                 //patchVertices[vertIndex] = lowLeft;
    127.                 //patchVertices[vertIndex + 1] = topLeft;
    128.                 //patchVertices[vertIndex + 2] = topRight;
    129.                 //patchVertices[vertIndex + 3] = lowRight;
    130.                 break;
    131.  
    132.             case CubeIndex.BACK:
    133.                 lowLeft = new Vector3(x - half, half - x1, -half);
    134.                 topLeft = new Vector3(x - half, half - z, -half);
    135.                 topRight = new Vector3(x1 - half, half - z, -half);
    136.                 lowRight = new Vector3(x1 - half, half - z1, -half);
    137.                                
    138.                 //patchVertices[vertIndex] = lowLeft;
    139.                 //patchVertices[vertIndex + 1] = topLeft;
    140.                 //patchVertices[vertIndex + 2] = topRight;
    141.                 //patchVertices[vertIndex + 3] = lowRight;
    142.                 break;
    143.  
    144.             case CubeIndex.LEFT:
    145.                 lowLeft = new Vector3(-half, half - x1, -half + x);
    146.                 topLeft = new Vector3(-half, half - z, -half + x);
    147.                 topRight = new Vector3(-half, half - z, -half + x1);
    148.                 lowRight = new Vector3(-half, half - z1, -half + x1);
    149.                                
    150.                 //patchVertices[vertIndex] = lowLeft;
    151.                 //patchVertices[vertIndex + 1] = topLeft;
    152.                 //patchVertices[vertIndex + 2] = topRight;
    153.                 //patchVertices[vertIndex + 3] = lowRight;
    154.                 break;
    155.  
    156.             case CubeIndex.RIGHT:
    157.                 lowLeft = new Vector3(half, half - x1, -half + x);
    158.                 topLeft = new Vector3(half, half - z, -half + x);
    159.                 topRight = new Vector3(half, half - z, -half + x1);
    160.                 lowRight = new Vector3(half, half - z1, -half + x1);
    161.                                
    162.                 //patchVertices[vertIndex] = lowLeft;
    163.                 //patchVertices[vertIndex + 1] = topLeft;
    164.                 //patchVertices[vertIndex + 2] = topRight;
    165.                 //patchVertices[vertIndex + 3] = lowRight;
    166.                 break;
    167.         }
    168.  
    169.         CreatePatchObject(patchID, lowLeft, topLeft, topRight, lowRight, index, sX, sZ);
    170.         //Increment the patchID number after the sub-patch has been created.
    171.         patchID++;
    172.         //vertIndex += 4;
    173.     }
    174.  
    175.     void CreatePatchObject(int id, Vector3 lowLeft, Vector3 topLeft, Vector3 topRight, Vector3 lowRight,  CubeIndex index, int sX, int sZ)
    176.     {
    177.         GameObject patchObject = new GameObject(id + " " + index + " PatchObject (" + sX + " , " + sZ + ")");
    178.         patchObject.layer = gameObject.layer;
    179.         patchObject.tag = gameObject.tag;
    180.         patchObject.transform.parent = transform;
    181.         patchObject.transform.position = transform.position;
    182.         //Add a MeshRenderer to access Renderer functions
    183.         patchObject.gameObject.AddComponent<MeshRenderer>();
    184.         //patchObject.GetComponent<Renderer>().material = base.GetComponent<Renderer>().material;
    185.         //Add a MeshCollider for Collision purposes
    186.         patchObject.AddComponent<MeshCollider>();
    187.  
    188.         for (int i = 0; i < 6; i++)
    189.         {
    190.             textures[i] = new Texture2D(textureSize, textureSize);
    191.  
    192.             float y = 0.0f;
    193.             while (y < textureSize)
    194.             {
    195.                 float x = 0.0f;
    196.                 while (x < textureSize)
    197.                 {
    198.                     float xCoord = xOrg + x / textureSize * scale;
    199.                     float yCoord = xOrg + y / textureSize * scale;
    200.                     float sample = Mathf.PerlinNoise(xCoord, yCoord);
    201.                     pixelColor[(int)y * textureSize + (int)x] = new Color(sample, sample, sample);
    202.                     x++;
    203.                 }
    204.                 y++;
    205.             }
    206.             textures[i].SetPixels(pixelColor);
    207.             textures[i].Apply();
    208.         }
    209.  
    210.         switch (index)
    211.         {
    212.             case CubeIndex.TOP:
    213.                 patchObject.gameObject.GetComponent<Renderer>().material.mainTexture = textures[0];
    214.                 break;
    215.  
    216.             case CubeIndex.BOTTOM:
    217.                 patchObject.gameObject.GetComponent<Renderer>().material.mainTexture = textures[1];
    218.                 break;
    219.  
    220.             case CubeIndex.FRONT:
    221.                 patchObject.gameObject.GetComponent<Renderer>().material.mainTexture = textures[2];
    222.                 break;
    223.  
    224.             case CubeIndex.BACK:
    225.                 patchObject.gameObject.GetComponent<Renderer>().material.mainTexture = textures[3];
    226.                 break;
    227.  
    228.             case CubeIndex.LEFT:
    229.                 patchObject.gameObject.GetComponent<Renderer>().material.mainTexture = textures[4];
    230.                 break;
    231.  
    232.             case CubeIndex.RIGHT:
    233.                 patchObject.gameObject.GetComponent<Renderer>().material.mainTexture = textures[5];
    234.                 break;
    235.         }
    236.  
    237.         SurfacePatch patch = patchObject.AddComponent<SurfacePatch>();
    238.         //Initialize the patch generator with the coordinates generated in Start
    239.         patch.InitPatchGenerator(lowLeft, topLeft, topRight, lowRight, this, index, null, sX, sZ);
    240.         //Generate the coordinates for the patch based on the sub-division level and resolution
    241.         patch.GeneratePatchCoordinates(patchSubdivisions, patchResolution);
    242.         //patch.Add(patch);
    243.         //After the Patch has been created, assign that patch to this objects MeshCollider
    244.         patchObject.GetComponent<MeshCollider>().sharedMesh = patch.ReturnMesh();
    245.     }
    246.  
    247.     /*private void OnDrawGizmos ()
    248.     {
    249.         if (patchVertices == null)
    250.         {
    251.             return;
    252.         }
    253.  
    254.         for (int i = 0; i < patchVertices.Length; i++)
    255.         {
    256.             Gizmos.color = Color.black;
    257.             Gizmos.DrawWireSphere(patchVertices[i], 0.0625f);
    258.         }
    259.         //Gizmos.color = Color.yellow;
    260.         //Gizmos.DrawRay(vertices[i], normals[i]);
    261.     }*/
    262. }
    263.  
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class SurfacePatch : MonoBehaviour
    5. {
    6.     private Vector3 _lowLeft;
    7.     private Vector3 _topLeft;
    8.     private Vector3 _topRight;
    9.     private Vector3 _lowRight;
    10.  
    11.     private Mesh patch;
    12.  
    13.     private ProceduralPlanet _planet;
    14.     private SurfacePatch _parent;
    15.     private ProceduralPlanet.CubeIndex _index;
    16.     private int _sX, _sZ;
    17.  
    18.     private float patchWidth;
    19.     private Vector3[] patchVertices;
    20.  
    21.     private int vertIndex = 0;
    22.     private int[] vertBuffer;
    23.     private Vector2[] patchUV;
    24.     private Vector3[] patchNormals;
    25.  
    26.     private float uvResolution;
    27.     private float uvXIncrement;
    28.     private float uvZIncrement;
    29.  
    30.     int k = 0;
    31.     int l = 0;
    32.  
    33.     public void InitPatchGenerator(Vector3 lowLeft, Vector3 topLeft, Vector3 topRight, Vector3 lowRight, ProceduralPlanet planet, ProceduralPlanet.CubeIndex index, SurfacePatch parent, int sX, int sZ)
    34.     {
    35.         _planet = planet;
    36.         _index = index;
    37.         _parent = parent;
    38.         _sX = sX;
    39.         _sZ = sZ;
    40.  
    41.         _lowLeft = lowLeft;
    42.         _topLeft = topLeft;
    43.         _topRight = topRight;
    44.         _lowRight = lowRight;
    45.  
    46.         if (patch == null)
    47.         {
    48.             MeshFilter meshFilter = gameObject.AddComponent<MeshFilter>();
    49.             patch = meshFilter.sharedMesh = new Mesh();
    50.  
    51.             GetComponent<Renderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    52.             GetComponent<Renderer>().receiveShadows = true;
    53.             GetComponent<Renderer>().enabled = true;
    54.         }
    55.     }
    56.  
    57.     public void GeneratePatchCoordinates(int patchSubdivisions, int patchResolution)
    58.     {
    59.         patchVertices = new Vector3[patchResolution * patchResolution];
    60.         vertBuffer = new int[(patchResolution - 1) * (patchResolution - 1) * 6];
    61.         patchUV = new Vector2[patchVertices.Length];
    62.         patchNormals = new Vector3[patchVertices.Length];
    63.  
    64.         if (_index == ProceduralPlanet.CubeIndex.FRONT || _index == ProceduralPlanet.CubeIndex.BACK || _index == ProceduralPlanet.CubeIndex.TOP || _index == ProceduralPlanet.CubeIndex.BOTTOM)
    65.         {
    66.             patchWidth = _topRight.x - _lowLeft.x;
    67.         }
    68.  
    69.         if (_index == ProceduralPlanet.CubeIndex.LEFT || _index == ProceduralPlanet.CubeIndex.RIGHT)
    70.         {
    71.             patchWidth = _topRight.z - _lowLeft.z;
    72.         }
    73.  
    74.         float increment = patchWidth / (patchResolution - 1);
    75.         float patchHalfWidth = ((patchResolution - 1) / patchWidth) * increment;
    76.  
    77.         for (int z = 0; z < patchResolution; z++)
    78.         {
    79.             float _z = z * increment;
    80.             float _z1 = (z + 1) * increment;
    81.  
    82.             for (int x = 0; x < patchResolution; x++)
    83.             {
    84.                 float _x = x * increment;
    85.                 float _x1 = (x + 1) * increment;
    86.  
    87.                 CreatePatchMesh(patchHalfWidth, _x, _x1, _z, _z1, patchSubdivisions, patchResolution);
    88.             }
    89.         }
    90.     }
    91.  
    92.     private void CreatePatchMesh(float half, float x, float x1, float z, float z1, int subDiv, int resolution)
    93.     {
    94.         float xIncrement = ((float)_sX / (float)subDiv) * 2;
    95.         float zIncrement = ((float)_sZ / (float)subDiv) * 2;
    96.  
    97.         uvXIncrement = _sX * (1.0f / subDiv);
    98.         uvZIncrement = _sZ * (1.0f / subDiv);
    99.  
    100.         int texXIncrement = _sX * ((_planet.textureSize / subDiv) / resolution);
    101.         int texZIncrement = _sZ * ((_planet.textureSize / subDiv) / resolution);
    102.         float noise = 0;
    103.         float noiseBase = _planet.planetRadius;
    104.  
    105.         switch(_index)
    106.         {
    107.             case ProceduralPlanet.CubeIndex.TOP:
    108.                 patchVertices[vertIndex] = new Vector3(x - half + xIncrement, _lowLeft.y, z - half + zIncrement);
    109.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    110.  
    111.                 patchUV[vertIndex] = new Vector2((x / 2f) + uvXIncrement , (z / 2f) + uvZIncrement);
    112.                 //Draw the triangles
    113.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    114.                 {
    115.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    116.                     {
    117.                         vertBuffer[ti] = vi;
    118.                         vertBuffer[ti + 1] = vi + resolution;
    119.                         vertBuffer[ti + 2] = vi + 1;
    120.  
    121.                         vertBuffer[ti + 3] = vi + resolution;
    122.                         vertBuffer[ti + 4] = vi + resolution + 1;
    123.                         vertBuffer[ti + 5] = vi + 1;
    124.                     }
    125.                 }
    126.                 noise = _planet.textures[0].GetPixel(l * texXIncrement, k * texZIncrement).grayscale * 0.5f;
    127.                 //noise = Mathf.Clamp(noise, (_planet.planetRadius + noise + 1.0f), _planet.planetRadius + noise + 1.0f);
    128.  
    129.                 patchVertices[vertIndex] *= _planet.planetRadius + noise;
    130.  
    131.                 patch.name = "TOP (" + _sX + "," + _sZ + ")";
    132.                 break;
    133.  
    134.             case ProceduralPlanet.CubeIndex.BOTTOM:
    135.                 patchVertices[vertIndex] = new Vector3(x - half + xIncrement, _lowLeft.y, z - half + zIncrement);
    136.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    137.              
    138.                 //Assign UV coordinates
    139.                 patchUV[vertIndex] = new Vector2((x / 2f) + uvXIncrement, (z / 2f) + uvZIncrement);
    140.                 //Draw the triangles
    141.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    142.                 {
    143.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    144.                     {
    145.                         vertBuffer[ti] = vi + 1;
    146.                         vertBuffer[ti + 1] = vi + resolution;
    147.                         vertBuffer[ti + 2] = vi;
    148.  
    149.                         vertBuffer[ti + 3] = vi + 1;
    150.                         vertBuffer[ti + 4] = vi + resolution + 1;
    151.                         vertBuffer[ti + 5] = vi + resolution;
    152.                     }
    153.                 }
    154.                 noise = _planet.textures[0].GetPixel(k * texXIncrement, l * texZIncrement).grayscale * 0.5f;
    155.                 //noise = Mathf.Clamp(noise, _planet.planetWaterLevel * (_planet.planetRadius + noise + 1.0f), _planet.planetRadius + noise + 1.0f);
    156.  
    157.                 patchVertices[vertIndex] *= _planet.planetRadius + noise;
    158.  
    159.                 patch.name = "BOTTOM (" + _sX + "," + _sZ + ")";
    160.                 break;
    161.  
    162.             case ProceduralPlanet.CubeIndex.FRONT:
    163.                 patchVertices[vertIndex] = new Vector3(x - half + xIncrement, z - half + zIncrement, _lowLeft.z);
    164.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    165.  
    166.                 //Assign UV coordinates
    167.                 patchUV[vertIndex] = new Vector2((x / 2f) + uvXIncrement, (z / 2f) + uvZIncrement);
    168.                 //Draw the triangles
    169.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    170.                 {
    171.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    172.                     {
    173.                         vertBuffer[ti] = vi + 1;
    174.                         vertBuffer[ti + 1] = vi + resolution;
    175.                         vertBuffer[ti + 2] = vi;
    176.  
    177.                         vertBuffer[ti + 3] = vi + 1;
    178.                         vertBuffer[ti + 4] = vi + resolution + 1;
    179.                         vertBuffer[ti + 5] = vi + resolution;
    180.                     }
    181.                 }
    182.                 noise = _planet.textures[0].GetPixel(k * texXIncrement, l * texZIncrement).grayscale * 0.5f;
    183.                 //noise = Mathf.Clamp(noise, _planet.planetWaterLevel * (_planet.planetRadius + noise + 1.0f), _planet.planetRadius + noise + 1.0f);
    184.  
    185.                 patchVertices[vertIndex] *= _planet.planetRadius + noise;
    186.  
    187.                 patch.name = "FRONT (" + _sX + "," + _sZ + ")";
    188.                 break;
    189.            
    190.             case ProceduralPlanet.CubeIndex.BACK:
    191.                 patchVertices[vertIndex] = new Vector3(x - half + xIncrement, z - half + zIncrement, _lowLeft.z);
    192.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    193.  
    194.                 //Assign UV coordinates
    195.                 patchUV[vertIndex] = new Vector2((x / 2f) + uvXIncrement, (z / 2f) + uvZIncrement);
    196.                 //Draw the triangles
    197.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    198.                 {
    199.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    200.                     {
    201.                         vertBuffer[ti] = vi;
    202.                         vertBuffer[ti + 1] = vi + resolution;
    203.                         vertBuffer[ti + 2] = vi + 1;
    204.  
    205.                         vertBuffer[ti + 3] = vi + resolution;
    206.                         vertBuffer[ti + 4] = vi + resolution + 1;
    207.                         vertBuffer[ti + 5] = vi + 1;
    208.                     }
    209.                 }
    210.                 noise = _planet.textures[0].GetPixel(k * texXIncrement, l * texZIncrement).grayscale;
    211.                 //noise = Mathf.Clamp(noise, _planet.planetRadius + noise, _planet.planetRadius + noise + 1.0f);
    212.    
    213.                 patchVertices[vertIndex] *= _planet.planetRadius + noise;
    214.  
    215.                 patch.name = "BACK (" + _sX + "," + _sZ + ")";
    216.                 break;
    217.  
    218.             case ProceduralPlanet.CubeIndex.LEFT:
    219.                 patchVertices[vertIndex] = new Vector3(_lowLeft.x, x - half + zIncrement, -half + z + xIncrement);
    220.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    221.  
    222.                 //Assign UV coordinates
    223.                 patchUV[vertIndex] = new Vector2((x / 2f) + uvZIncrement, (z / 2f) + uvXIncrement);
    224.                 //Draw the triangles
    225.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    226.                 {
    227.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    228.                     {
    229.                         vertBuffer[ti] = vi;
    230.                         vertBuffer[ti + 1] = vi + resolution;
    231.                         vertBuffer[ti + 2] = vi + 1;
    232.  
    233.                         vertBuffer[ti + 3] = vi + resolution;
    234.                         vertBuffer[ti + 4] = vi + resolution + 1;
    235.                         vertBuffer[ti + 5] = vi + 1;
    236.                     }
    237.                 }
    238.                 noise = _planet.textures[0].GetPixel(k * texXIncrement, l * texZIncrement).grayscale * 0.5f;
    239.                 //noise = Mathf.Clamp(noise, _planet.planetWaterLevel * (_planet.planetRadius + noise + 1.0f), _planet.planetRadius + noise + 1.0f);
    240.  
    241.                 patchVertices[vertIndex] *= _planet.planetRadius + noise;
    242.  
    243.                 patch.name = "LEFT (" + _sX + "," + _sZ + ")";
    244.                 break;
    245.  
    246.             case ProceduralPlanet.CubeIndex.RIGHT:
    247.                 patchVertices[vertIndex] = new Vector3(_lowLeft.x, x - half + zIncrement, -half + z + xIncrement);
    248.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    249.                
    250.                 //Assign UV coordinates
    251.                 patchUV[vertIndex] = new Vector2((x / 2f) + uvZIncrement, (z / 2f) + uvXIncrement);
    252.                 //Draw the triangles
    253.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    254.                 {
    255.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    256.                     {
    257.                         vertBuffer[ti] = vi + 1;
    258.                         vertBuffer[ti + 1] = vi + resolution;
    259.                         vertBuffer[ti + 2] = vi;
    260.  
    261.                         vertBuffer[ti + 3] = vi + 1;
    262.                         vertBuffer[ti + 4] = vi + resolution + 1;
    263.                         vertBuffer[ti + 5] = vi + resolution;
    264.                     }
    265.                 }
    266.                 noise = _planet.textures[0].GetPixel(k * texXIncrement, l * texZIncrement).grayscale * 2.5f;
    267.                 //noise = Mathf.Clamp(noise, _planet.planetWaterLevel * (_planet.planetRadius + noise + 1.0f), _planet.planetRadius + noise + 1.0f);
    268.  
    269.                 patchVertices[vertIndex] *= _planet.planetRadius + noise;
    270.  
    271.                 patch.name = "RIGHT (" + _sX + "," + _sZ + ")";
    272.                 break;
    273.         }
    274.  
    275.         patch.vertices = patchVertices;
    276.         patch.uv = patchUV;
    277.         patch.triangles = vertBuffer;
    278.         patch.RecalculateNormals();
    279.  
    280.         vertIndex++;
    281.         k++;
    282.         l++;
    283.     }
    284.  
    285.     public void Add(SurfacePatch patch)
    286.     {
    287.  
    288.     }
    289.  
    290.     public Mesh ReturnMesh()
    291.     {
    292.         return patch;
    293.     }
    294.  
    295.     public static Vector3 MapToSphere(Vector3 point)
    296.     {
    297.         float dX2, dY2, dZ2;
    298.         float dX2Half, dY2Half, dZ2Half;
    299.  
    300.         dX2 = point.x * point.x;
    301.         dY2 = point.y * point.y;
    302.         dZ2 = point.z * point.z;
    303.  
    304.         dX2Half = dX2 * 0.5f;
    305.         dY2Half = dY2 * 0.5f;
    306.         dZ2Half = dZ2 * 0.5f;
    307.  
    308.         point.x = point.x * Mathf.Sqrt(1f - dY2Half - dZ2Half + (dY2 * dZ2) * (1f / 3f));
    309.         point.y = point.y * Mathf.Sqrt(1f - dZ2Half - dX2Half + (dZ2 * dX2) * (1f / 3f));
    310.         point.z = point.z * Mathf.Sqrt(1f - dX2Half - dY2Half + (dX2 * dY2) * (1f / 3f));
    311.  
    312.         return point;
    313.     }
    314.  
    315.     /*private void OnDrawGizmos()
    316.     {
    317.         if (patchVertices == null)
    318.         {
    319.             return;
    320.         }
    321.  
    322.         for (int i = 0; i < patchVertices.Length; i++)
    323.         {
    324.             if (_index == ProceduralPlanet.CubeIndex.TOP)
    325.             {
    326.                 Gizmos.color = Color.red;
    327.             }
    328.  
    329.             if (_index == ProceduralPlanet.CubeIndex.BOTTOM)
    330.             {
    331.                 Gizmos.color = Color.magenta;
    332.             }
    333.  
    334.             if (_index == ProceduralPlanet.CubeIndex.FRONT)
    335.             {
    336.                 Gizmos.color = Color.green;
    337.             }
    338.  
    339.             if (_index == ProceduralPlanet.CubeIndex.BACK)
    340.             {
    341.                 Gizmos.color = Color.blue;
    342.             }
    343.  
    344.             Gizmos.DrawSphere(patchVertices[i], 0.0125f);
    345.         }
    346.         //Gizmos.color = Color.yellow;
    347.         //Gizmos.DrawRay(vertices[i], normals[i]);
    348.     }*/
    349. }
    350.  
     

    Attached Files:

    LeftyRighty likes this.
  4. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Bump...

    So this is what I've got implemented so far:

    Code (CSharp):
    1.         int texXIncrement = _sX * ((_planet.textureSize / subDiv) / resolution);
    2.         int texZIncrement = _sZ * ((_planet.textureSize / subDiv) / resolution);
    3.         float noise = 0;
    4.         float noiseBase = _planet.planetRadius;
    Code (CSharp):
    1.  
    2.                 noise = _planet.textures[0].GetPixel(l * texXIncrement, k * texZIncrement).grayscale * 0.5f;
    3.                 //noise = Mathf.Clamp(noise, (_planet.planetRadius + noise + 1.0f), _planet.planetRadius + noise + 1.0f);
    4.                 patchVertices[vertIndex] *= _planet.planetRadius + noise;
    Code (CSharp):
    1.         patch.vertices = patchVertices;
    2.         patch.uv = patchUV;
    3.         patch.triangles = vertBuffer;
    4.         patch.RecalculateNormals();
    5.         vertIndex++;
    6.         k++;
    7.         l++;
    The idea is that the texture covers one full side of the cube that makes up the sphere. It's resolution is defined by the user. Since the texture is a fixed size, I figure out how many pixels each vertex needs to move and sample. So that takes into account first the number of subdivisions currently implemented and then the number of vertices per subdivision surface. This is then put into GetPixel. Each time through the switch it then increments the current vertex....But from looking at the results it's definitely not working as intended. Anyone see anything glaringly obvious in the code?
     
    LeftyRighty likes this.
  5. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    I made the following adjustments to my code and got the following:

    upload_2016-5-9_10-30-24.png

    So...i'm a little bit closer, now I just need to get the sides of each patch to line up...at least the depth corresponds to the height map now....here's the relevant lines of code updated...if anyone has any ideas what I'm still doing wrong, please chime in!

    Code (CSharp):
    1.     private void CreatePatchMesh(float half, float x, float x1, float z, float z1, int subDiv, int resolution)
    2.     {
    3.         float xIncrement = ((float)_sX / (float)subDiv) * 2;
    4.         float zIncrement = ((float)_sZ / (float)subDiv) * 2;
    5.  
    6.         uvXIncrement = _sX * (1.0f / subDiv);
    7.         uvZIncrement = _sZ * (1.0f / subDiv);
    8.  
    9.         //int texXIncrement = _sX * ((_planet.textureSize / subDiv) / resolution);
    10.         //int texZIncrement = _sZ * ((_planet.textureSize / subDiv) / resolution);
    11.         int texXIncrement = ((_planet.textureSize / subDiv) / resolution);
    12.         int texZIncrement = ((_planet.textureSize / subDiv) / resolution);
    13.         float[] noise = new float[patchVertices.Length];
    14.         float noiseBase = _planet.planetRadius;
    15.  
    16.         switch(_index)
    17.         {
    18.             case ProceduralPlanet.CubeIndex.TOP:
    19.                 patchVertices[vertIndex] = new Vector3(x - half + xIncrement, _lowLeft.y, z - half + zIncrement);
    20.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    21.                 //patchVertices[vertIndex].Normalize();
    22.  
    23.                 patchUV[vertIndex] = new Vector2((x / 2f) + uvXIncrement , (z / 2f) + uvZIncrement);
    24.                 //Draw the triangles
    25.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    26.                 {
    27.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    28.                     {
    29.                         vertBuffer[ti] = vi;
    30.                         vertBuffer[ti + 1] = vi + resolution;
    31.                         vertBuffer[ti + 2] = vi + 1;
    32.  
    33.                         vertBuffer[ti + 3] = vi + resolution;
    34.                         vertBuffer[ti + 4] = vi + resolution + 1;
    35.                         vertBuffer[ti + 5] = vi + 1;
    36.                     }
    37.                 }
    38.                 for (int a = 0, b = 0; a < resolution; a++)
    39.                 {
    40.                     for (int c = 0; c < resolution; c++, b++)
    41.                     {
    42.                         noise[b] = _planet.textures[0].GetPixel(c * texXIncrement, a * texZIncrement).grayscale;
    43.                         //noise[b] = Mathf.Clamp(noise[b], _planet.planetRadius + noise[b] + 1.0f, _planet.planetRadius + noise[b] + 1.0f);
    44.                     }
    45.                 }
    46.                
    47.  
    48.                 patchVertices[vertIndex] *= _planet.planetRadius + noise[vertIndex];
    49.                 //patchVertices[vertIndex] *= _planet.planetRadius;
    50.  
    51.                 patch.name = "TOP (" + _sX + "," + _sZ + ")";
    52.                 break;
     
    LeftyRighty likes this.
  6. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    So no one has any insight into why this isn't working as it's supposed to? I've double checked my math using excel to ensure everything is correct implementation wise...but I'm still getting disjointed patches. Here's the most up to date code:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class SurfacePatch : MonoBehaviour
    5. {
    6.     private Vector3 _lowLeft;
    7.     private Vector3 _topLeft;
    8.     private Vector3 _topRight;
    9.     private Vector3 _lowRight;
    10.  
    11.     private Mesh patch;
    12.  
    13.     private ProceduralPlanet _planet;
    14.     private SurfacePatch _parent;
    15.     private ProceduralPlanet.CubeIndex _index;
    16.     private int _sX, _sZ;
    17.  
    18.     private float patchWidth;
    19.     private Vector3[] patchVertices;
    20.  
    21.     private int vertIndex = 0;
    22.     private int[] vertBuffer;
    23.     private Vector2[] patchUV;
    24.     private Vector3[] patchNormals;
    25.  
    26.     private float uvResolution;
    27.     private float uvXIncrement;
    28.     private float uvZIncrement;
    29.     public float[] noise;
    30.  
    31.  
    32.     public void InitPatchGenerator(Vector3 lowLeft, Vector3 topLeft, Vector3 topRight, Vector3 lowRight, ProceduralPlanet planet, ProceduralPlanet.CubeIndex index, SurfacePatch parent, int sX, int sZ)
    33.     {
    34.         _planet = planet;
    35.         _index = index;
    36.         _parent = parent;
    37.         _sX = sX;
    38.         _sZ = sZ;
    39.  
    40.         _lowLeft = lowLeft;
    41.         _topLeft = topLeft;
    42.         _topRight = topRight;
    43.         _lowRight = lowRight;
    44.  
    45.         if (patch == null)
    46.         {
    47.             MeshFilter meshFilter = gameObject.AddComponent<MeshFilter>();
    48.             patch = meshFilter.sharedMesh = new Mesh();
    49.  
    50.             GetComponent<Renderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    51.             GetComponent<Renderer>().receiveShadows = true;
    52.             GetComponent<Renderer>().enabled = true;
    53.         }
    54.     }
    55.  
    56.     public void GeneratePatchCoordinates(int patchSubdivisions, int patchResolution)
    57.     {
    58.         patchVertices = new Vector3[patchResolution * patchResolution];
    59.         vertBuffer = new int[(patchResolution - 1) * (patchResolution - 1) * 6];
    60.         patchUV = new Vector2[patchVertices.Length];
    61.         patchNormals = new Vector3[patchVertices.Length];
    62.         noise = new float[(patchResolution + 1) * (patchResolution + 1)];
    63.  
    64.         if (_index == ProceduralPlanet.CubeIndex.FRONT || _index == ProceduralPlanet.CubeIndex.BACK || _index == ProceduralPlanet.CubeIndex.TOP || _index == ProceduralPlanet.CubeIndex.BOTTOM)
    65.         {
    66.             patchWidth = _topRight.x - _lowLeft.x;
    67.         }
    68.  
    69.         if (_index == ProceduralPlanet.CubeIndex.LEFT || _index == ProceduralPlanet.CubeIndex.RIGHT)
    70.         {
    71.             patchWidth = _topRight.z - _lowLeft.z;
    72.         }
    73.  
    74.         float increment = patchWidth / (patchResolution - 1);
    75.         float patchHalfWidth = ((patchResolution - 1) / patchWidth) * increment;
    76.  
    77.         for (int z = 0; z < patchResolution; z++)
    78.         {
    79.             float _z = z * increment;
    80.             float _z1 = (z + 1) * increment;
    81.  
    82.             for (int x = 0; x < patchResolution; x++)
    83.             {
    84.                 float _x = x * increment;
    85.                 float _x1 = (x + 1) * increment;
    86.  
    87.                 CreatePatchMesh(patchHalfWidth, _x, _x1, _z, _z1, patchSubdivisions, patchResolution);
    88.             }
    89.         }
    90.     }
    91.  
    92.     private void CreatePatchMesh(float half, float x, float x1, float z, float z1, int subDiv, int resolution)
    93.     {
    94.         float xIncrement = ((float)_sX / (float)subDiv) * 2;
    95.         float zIncrement = ((float)_sZ / (float)subDiv) * 2;
    96.  
    97.         uvXIncrement = _sX * (1.0f / subDiv);
    98.         uvZIncrement = _sZ * (1.0f / subDiv);
    99.  
    100.         //int texXIncrement = _sX * ((_planet.textureSize / subDiv) / resolution);
    101.         //int texZIncrement = _sZ * ((_planet.textureSize / subDiv) / resolution);
    102.         int texXIncrement = ((_planet.textureSize / subDiv) / resolution);
    103.         int texZIncrement = ((_planet.textureSize / subDiv) / resolution);
    104.         float noiseBase = _planet.planetRadius;
    105.  
    106.         switch(_index)
    107.         {
    108.             case ProceduralPlanet.CubeIndex.TOP:
    109.                 patchVertices[vertIndex] = new Vector3(x - half + xIncrement, _lowLeft.y, z - half + zIncrement);
    110.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    111.                 //patchVertices[vertIndex].Normalize();
    112.  
    113.                 patchUV[vertIndex] = new Vector2((x / 2f) + uvXIncrement , (z / 2f) + uvZIncrement);
    114.                 //Draw the triangles
    115.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    116.                 {
    117.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    118.                     {
    119.                         vertBuffer[ti] = vi;
    120.                         vertBuffer[ti + 1] = vi + resolution;
    121.                         vertBuffer[ti + 2] = vi + 1;
    122.  
    123.                         vertBuffer[ti + 3] = vi + resolution;
    124.                         vertBuffer[ti + 4] = vi + resolution + 1;
    125.                         vertBuffer[ti + 5] = vi + 1;
    126.                     }
    127.                 }
    128.  
    129.                 for (int a = 0, b = 0; a <= resolution; a++)
    130.                 {
    131.                     for (int c = 0; c <= resolution; c++, b++)
    132.                     {
    133.                         noise[b] = _planet.textures[0].GetPixel(c * texXIncrement + ((_sX * resolution) * texXIncrement), a * texZIncrement + ((_sZ * resolution) * texZIncrement)).grayscale;
    134.                         //noise[b] = Mathf.Clamp(noise[b], _planet.planetRadius + noise[b] + 1.0f, _planet.planetRadius + noise[b] + 1.0f);
    135.                     }
    136.                 }
    137.                
    138.  
    139.                 patchVertices[vertIndex] *= _planet.planetRadius + noise[vertIndex];
    140.                 //patchVertices[vertIndex] *= _planet.planetRadius;
    141.  
    142.                 patch.name = "TOP (" + _sX + "," + _sZ + ")";
    143.                 break;
    144.  
    145.             case ProceduralPlanet.CubeIndex.BOTTOM:
    146.                 patchVertices[vertIndex] = new Vector3(x - half + xIncrement, _lowLeft.y, z - half + zIncrement);
    147.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    148.              
    149.                 //Assign UV coordinates
    150.                 patchUV[vertIndex] = new Vector2((x / 2f) + uvXIncrement, (z / 2f) + uvZIncrement);
    151.                 //Draw the triangles
    152.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    153.                 {
    154.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    155.                     {
    156.                         vertBuffer[ti] = vi + 1;
    157.                         vertBuffer[ti + 1] = vi + resolution;
    158.                         vertBuffer[ti + 2] = vi;
    159.  
    160.                         vertBuffer[ti + 3] = vi + 1;
    161.                         vertBuffer[ti + 4] = vi + resolution + 1;
    162.                         vertBuffer[ti + 5] = vi + resolution;
    163.                     }
    164.                 }
    165.  
    166.                 for (int a = 0, b = 0; a < resolution; a++)
    167.                 {
    168.                     for (int c = 0; c < resolution; c++, b++)
    169.                     {
    170.                         noise[b] = _planet.textures[0].GetPixel(c * texXIncrement, a * texZIncrement).grayscale;
    171.                         //noise[b] = Mathf.Clamp(noise[b], _planet.planetRadius + noise[b] + 1.0f, _planet.planetRadius + noise[b] + 1.0f);
    172.                     }
    173.                 }
    174.                
    175.                 patchVertices[vertIndex] *= _planet.planetRadius + noise[vertIndex];
    176.                 //patchVertices[vertIndex] *= _planet.planetRadius;
    177.  
    178.                 patch.name = "BOTTOM (" + _sX + "," + _sZ + ")";
    179.                 break;
    180.  
    181.             case ProceduralPlanet.CubeIndex.FRONT:
    182.                 patchVertices[vertIndex] = new Vector3(x - half + xIncrement, z - half + zIncrement, _lowLeft.z);
    183.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    184.  
    185.                 //Assign UV coordinates
    186.                 patchUV[vertIndex] = new Vector2((x / 2f) + uvXIncrement, (z / 2f) + uvZIncrement);
    187.                 //Draw the triangles
    188.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    189.                 {
    190.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    191.                     {
    192.                         vertBuffer[ti] = vi + 1;
    193.                         vertBuffer[ti + 1] = vi + resolution;
    194.                         vertBuffer[ti + 2] = vi;
    195.  
    196.                         vertBuffer[ti + 3] = vi + 1;
    197.                         vertBuffer[ti + 4] = vi + resolution + 1;
    198.                         vertBuffer[ti + 5] = vi + resolution;
    199.                     }
    200.                 }
    201.  
    202.                 for (int a = 0, b = 0; a < resolution; a++)
    203.                 {
    204.                     for (int c = 0; c < resolution; c++, b++)
    205.                     {
    206.                         noise[b] = _planet.textures[0].GetPixel(c * texXIncrement, a * texZIncrement).grayscale;
    207.                         //noise[b] = Mathf.Clamp(noise[b], _planet.planetRadius + noise[b] + 1.0f, _planet.planetRadius + noise[b] + 1.0f);
    208.                     }
    209.                 }
    210.                
    211.                 patchVertices[vertIndex] *= _planet.planetRadius + noise[vertIndex];
    212.                 //patchVertices[vertIndex] *= _planet.planetRadius;
    213.  
    214.                 patch.name = "FRONT (" + _sX + "," + _sZ + ")";
    215.                 break;
    216.            
    217.             case ProceduralPlanet.CubeIndex.BACK:
    218.                 patchVertices[vertIndex] = new Vector3(x - half + xIncrement, z - half + zIncrement, _lowLeft.z);
    219.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    220.  
    221.                 //Assign UV coordinates
    222.                 patchUV[vertIndex] = new Vector2((x / 2f) + uvXIncrement, (z / 2f) + uvZIncrement);
    223.                 //Draw the triangles
    224.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    225.                 {
    226.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    227.                     {
    228.                         vertBuffer[ti] = vi;
    229.                         vertBuffer[ti + 1] = vi + resolution;
    230.                         vertBuffer[ti + 2] = vi + 1;
    231.  
    232.                         vertBuffer[ti + 3] = vi + resolution;
    233.                         vertBuffer[ti + 4] = vi + resolution + 1;
    234.                         vertBuffer[ti + 5] = vi + 1;
    235.                     }
    236.                 }
    237.  
    238.                 for (int a = 0, b = 0; a < resolution; a++)
    239.                 {
    240.                     for (int c = 0; c < resolution; c++, b++)
    241.                     {
    242.                         noise[b] = _planet.textures[0].GetPixel(c * texXIncrement, a * texZIncrement).grayscale;
    243.                         //noise[b] = Mathf.Clamp(noise[b], _planet.planetRadius + noise[b] + 1.0f, _planet.planetRadius + noise[b] + 1.0f);
    244.                     }
    245.                 }
    246.                
    247.                 patchVertices[vertIndex] *= _planet.planetRadius + noise[vertIndex];
    248.                 //patchVertices[vertIndex] *= _planet.planetRadius;
    249.  
    250.                 patch.name = "BACK (" + _sX + "," + _sZ + ")";
    251.                 break;
    252.  
    253.             case ProceduralPlanet.CubeIndex.LEFT:
    254.                 patchVertices[vertIndex] = new Vector3(_lowLeft.x, x - half + zIncrement, -half + z + xIncrement);
    255.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    256.  
    257.                 //Assign UV coordinates
    258.                 patchUV[vertIndex] = new Vector2((x / 2f) + uvZIncrement, (z / 2f) + uvXIncrement);
    259.                 //Draw the triangles
    260.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    261.                 {
    262.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    263.                     {
    264.                         vertBuffer[ti] = vi;
    265.                         vertBuffer[ti + 1] = vi + resolution;
    266.                         vertBuffer[ti + 2] = vi + 1;
    267.  
    268.                         vertBuffer[ti + 3] = vi + resolution;
    269.                         vertBuffer[ti + 4] = vi + resolution + 1;
    270.                         vertBuffer[ti + 5] = vi + 1;
    271.                     }
    272.                 }
    273.  
    274.                 for (int a = 0, b = 0; a < resolution; a++)
    275.                 {
    276.                     for (int c = 0; c < resolution; c++, b++)
    277.                     {
    278.                         noise[b] = _planet.textures[0].GetPixel(c * texXIncrement, a * texZIncrement).grayscale;
    279.                         //noise[b] = Mathf.Clamp(noise[b], _planet.planetRadius + noise[b] + 1.0f, _planet.planetRadius + noise[b] + 1.0f);
    280.                     }
    281.                 }
    282.                
    283.                 patchVertices[vertIndex] *= _planet.planetRadius + noise[vertIndex];
    284.                 //patchVertices[vertIndex] *= _planet.planetRadius;
    285.  
    286.                 patch.name = "LEFT (" + _sX + "," + _sZ + ")";
    287.                 break;
    288.  
    289.             case ProceduralPlanet.CubeIndex.RIGHT:
    290.                 patchVertices[vertIndex] = new Vector3(_lowLeft.x, x - half + zIncrement, -half + z + xIncrement);
    291.                 patchVertices[vertIndex] = MapToSphere(patchVertices[vertIndex]);
    292.                
    293.                 //Assign UV coordinates
    294.                 patchUV[vertIndex] = new Vector2((x / 2f) + uvZIncrement, (z / 2f) + uvXIncrement);
    295.                 //Draw the triangles
    296.                 for (int ti = 0, vi = 0, i = 0; i < (resolution - 1); i++, vi++)
    297.                 {
    298.                     for (int j = 0; j < (resolution - 1); j++, ti += 6, vi++)
    299.                     {
    300.                         vertBuffer[ti] = vi + 1;
    301.                         vertBuffer[ti + 1] = vi + resolution;
    302.                         vertBuffer[ti + 2] = vi;
    303.  
    304.                         vertBuffer[ti + 3] = vi + 1;
    305.                         vertBuffer[ti + 4] = vi + resolution + 1;
    306.                         vertBuffer[ti + 5] = vi + resolution;
    307.                     }
    308.                 }
    309.  
    310.                 for (int a = 0, b = 0; a < resolution; a++)
    311.                 {
    312.                     for (int c = 0; c < resolution; c++, b++)
    313.                     {
    314.                         noise[b] = _planet.textures[0].GetPixel(c * texXIncrement, a * texZIncrement).grayscale;
    315.                         //noise[b] = Mathf.Clamp(noise[b], _planet.planetRadius + noise[b] + 1.0f, _planet.planetRadius + noise[b] + 1.0f);
    316.                     }
    317.                 }
    318.                
    319.                 patchVertices[vertIndex] *= _planet.planetRadius + noise[vertIndex];
    320.                 //patchVertices[vertIndex] *= _planet.planetRadius;
    321.  
    322.                 patch.name = "RIGHT (" + _sX + "," + _sZ + ")";
    323.                 break;
    324.         }
    325.  
    326.         patch.vertices = patchVertices;
    327.         patch.uv = patchUV;
    328.         patch.triangles = vertBuffer;
    329.         patch.RecalculateNormals();
    330.  
    331.         vertIndex++;
    332.     }
    333.  
    334.     public void Add(SurfacePatch patch)
    335.     {
    336.  
    337.     }
    338.  
    339.     public Mesh ReturnMesh()
    340.     {
    341.         return patch;
    342.     }
    343.  
    344.     public static Vector3 MapToSphere(Vector3 point)
    345.     {
    346.         float dX2, dY2, dZ2;
    347.         float dX2Half, dY2Half, dZ2Half;
    348.  
    349.         dX2 = point.x * point.x;
    350.         dY2 = point.y * point.y;
    351.         dZ2 = point.z * point.z;
    352.  
    353.         dX2Half = dX2 * 0.5f;
    354.         dY2Half = dY2 * 0.5f;
    355.         dZ2Half = dZ2 * 0.5f;
    356.  
    357.         point.x = point.x * Mathf.Sqrt(1f - dY2Half - dZ2Half + (dY2 * dZ2) * (1f / 3f));
    358.         point.y = point.y * Mathf.Sqrt(1f - dZ2Half - dX2Half + (dZ2 * dX2) * (1f / 3f));
    359.         point.z = point.z * Mathf.Sqrt(1f - dX2Half - dY2Half + (dX2 * dY2) * (1f / 3f));
    360.  
    361.         return point;
    362.     }
    363.  
    364.     /*private void OnDrawGizmos()
    365.     {
    366.         if (patchVertices == null)
    367.         {
    368.             return;
    369.         }
    370.  
    371.         for (int i = 0; i < patchVertices.Length; i++)
    372.         {
    373.             if (_index == ProceduralPlanet.CubeIndex.TOP)
    374.             {
    375.                 Gizmos.color = Color.red;
    376.             }
    377.  
    378.             if (_index == ProceduralPlanet.CubeIndex.BOTTOM)
    379.             {
    380.                 Gizmos.color = Color.magenta;
    381.             }
    382.  
    383.             if (_index == ProceduralPlanet.CubeIndex.FRONT)
    384.             {
    385.                 Gizmos.color = Color.green;
    386.             }
    387.  
    388.             if (_index == ProceduralPlanet.CubeIndex.BACK)
    389.             {
    390.                 Gizmos.color = Color.blue;
    391.             }
    392.  
    393.             Gizmos.DrawSphere(patchVertices[i], 0.0125f);
    394.         }
    395.         //Gizmos.color = Color.yellow;
    396.         //Gizmos.DrawRay(vertices[i], normals[i]);
    397.     }*/
    398. }
    399.  
    Could the problem maybe lie in the way I'm mapping the points to the sphere? I'm pretty stuck at this point and out of ideas. Any help would be great, surely there's a person smarter than I that sees something I don't.
     
    landon912, sacunha and LeftyRighty like this.
  7. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    your mesh-fu is far beyond mine but it's really good to see this get updated as you work things out :D
     
    sacunha likes this.
  8. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Appreciate it! I actually re-wrote this beast using some threads I found in the forums as a guide. It seems to work a LOT better now. Haven't implemented the height map displacements yet, but here be the new code. What I really need some major help with now is getting a seamless procedural cubemap implemented.

    Anyone have some insights there?

    Here's the new code!

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. using LibNoise;
    5. using LibNoise.Generator;
    6. using LibNoise.Operator;
    7.  
    8. public class NewTestPlanet : MonoBehaviour
    9. {
    10.     public float planetRadius = 10.0f;
    11.  
    12.     //The number of Subdivions per Face
    13.     public int subDivisions = 2;
    14.     //The number of Vertices per Subdivision
    15.     public int vertsPerSubDivision = 4;
    16.  
    17.     //Texture Data
    18.     public float xOrg = 1.0f;
    19.     public float yOrg = 1.0f;
    20.     public float scale = 10.0f;
    21.  
    22.     public enum cubeIndex { TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK }
    23.  
    24.     public Texture2D[] textures;
    25.  
    26.     void Start()
    27.     {
    28.         CreateSubdivisionSurfaces();
    29.     }
    30.  
    31.     //Responsible for calling CreateSurface. Passes the following arguments to CreateSurface():
    32.     //  x-index (i)
    33.     //  z-index (j)
    34.     //  cube face (index)
    35.     void CreateSubdivisionSurfaces()
    36.     {
    37.         //For each face and for each Sub Division Surface, Create a new Surface
    38.         for (int index = 0; index < 6; index++)
    39.         {
    40.             for (int i = 0; i < subDivisions; i++)
    41.             {
    42.                 for (int j = 0; j < subDivisions; j++)
    43.                 {
    44.                     GenerateSurfaceTextures(index);
    45.                     GenerateSurfaceCoordinates(i, j, (cubeIndex)index);
    46.                 }
    47.             }
    48.         }
    49.     }
    50.  
    51.     //Creates the surfaces called by CreateSubdivisionSurfaces(). Creates a GameObject
    52.     //assigns a MeshRenderer and a MeshCollider to the created Game Object.
    53.     void GenerateSurfaceCoordinates(int xIndex, int zIndex, cubeIndex facing)
    54.     {
    55.         //Appropriately size (and create) the Vertex Array
    56.         Vector3[] vertices = new Vector3[vertsPerSubDivision * vertsPerSubDivision];
    57.         //Appropriately size (and create) the UV Array
    58.         Vector2[] uv  = new Vector2[vertices.Length];
    59.  
    60.         //Calculate the increment to keep the vertices constrained to a 1x1 cube.
    61.         float increment = (1.0f / ((float)vertsPerSubDivision - 1)) / (float)subDivisions;
    62.         float uvIncrement = (1.0f / ((float)vertsPerSubDivision - 1)) / (float)subDivisions;
    63.  
    64.         for (int i = 0, index = 0; i < vertsPerSubDivision; i++)
    65.         {
    66.             for (int j = 0; j < vertsPerSubDivision; j++, index++)
    67.             {
    68.                 //Vertex Coordinates
    69.                 float xPos = (float)j * increment - 0.5f + ((float)xIndex / (float)subDivisions);
    70.                 float yPos = 0.5f;
    71.                 float zPos = (float)i * increment - 0.5f + ((float)zIndex / (float)subDivisions);
    72.                 //UV Coordinates
    73.                 float xUV = (float)j * uvIncrement + ((float)xIndex / (float)subDivisions);
    74.                 float zUV = (float)i * uvIncrement + ((float)zIndex / (float)subDivisions);
    75.  
    76.                 switch(facing)
    77.                 {
    78.                     case cubeIndex.TOP:
    79.                         //Assign Vertex Coordinates
    80.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    81.                         //Assign UV Coordinates
    82.                         uv[index] = new Vector2(xUV, zUV);
    83.                         break;
    84.  
    85.                     case cubeIndex.BOTTOM:
    86.                         //Assign Vertex Coordinates
    87.                         vertices[index] = new Vector3(-xPos, -yPos, zPos);
    88.                         //Assign UV Coordinates
    89.                         uv[index] = new Vector2(xUV, zUV);
    90.                         break;
    91.  
    92.                     case cubeIndex.LEFT:
    93.                         //Assign Vertex Coordinates
    94.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    95.                         //Assign UV Coordinates
    96.                         uv[index] = new Vector2(xUV, zUV);
    97.                         break;
    98.  
    99.                     case cubeIndex.RIGHT:
    100.                         //Assign Vertex Coordinates
    101.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    102.                         //Assign UV Coordinates
    103.                         uv[index] = new Vector2(xUV, zUV);
    104.                         break;
    105.  
    106.                     case cubeIndex.FRONT:
    107.                         //Assign Vertex Coordinates
    108.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    109.                         //Assign UV Coordinates
    110.                         uv[index] = new Vector2(xUV, zUV);
    111.                         break;
    112.  
    113.                     case cubeIndex.BACK:
    114.                         //Assign Vertex Coordinates
    115.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    116.                         //Assign UV Coordinates
    117.                         uv[index] = new Vector2(xUV, zUV);
    118.                         break;
    119.                 }
    120.  
    121.                 //Spherize the Vertices
    122.                 vertices[index] = vertices[index].normalized;
    123.                 //Set the newly created sphere equal to the Radius
    124.                 vertices[index] *= planetRadius;
    125.             }
    126.         }
    127.         //Create the Surface Object
    128.         CreateSurfaceObject(vertices, uv, facing, xIndex, zIndex);
    129.     }
    130.  
    131.     //Creates a new GameObject to which a MeshRenderer, MeshCollider and MeshFilter are attached.
    132.     //This is then passed to CreateMeshSurface where the actual Mesh is created.
    133.     void CreateSurfaceObject(Vector3[] vertexArray, Vector2[] uvArray, cubeIndex facing, int xIndex, int zIndex)
    134.     {
    135.         //Create a new GameObject
    136.         GameObject surface = new GameObject(facing + "Surface(" + xIndex + ", " + zIndex + ")");
    137.         surface.tag     = gameObject.tag;
    138.         surface.layer   = gameObject.layer;
    139.         surface.transform.parent    = transform;
    140.         surface.transform.position  = transform.position;
    141.         //Add the MeshRenderer
    142.         surface.gameObject.AddComponent<MeshRenderer>();
    143.         //Add the MeshCollider
    144.         surface.gameObject.AddComponent<MeshCollider>();
    145.         //Add the MeshFilter
    146.         surface.gameObject.AddComponent<MeshFilter>();
    147.  
    148.         //Initialize the Renderer
    149.         surface.GetComponent<Renderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    150.         surface.GetComponent<Renderer>().receiveShadows = true;
    151.         surface.GetComponent<Renderer>().enabled = true;
    152.  
    153.         //Assign the generated textures to the cube sphere
    154.         surface.GetComponent<Renderer>().material.mainTexture = textures[(int)facing];
    155.  
    156.         //Create the Mesh
    157.         CreateSurfaceMesh(surface, vertexArray, uvArray, facing);
    158.     }
    159.  
    160.     void CreateSurfaceMesh(GameObject surface, Vector3[] vertexArray, Vector2[] uvArray, cubeIndex facing)
    161.     {
    162.         //Create and size the Vertex Buffer
    163.         int vertBufferSize = vertsPerSubDivision - 1;
    164.         int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    165.         //Create a new Mesh object
    166.         Mesh surfaceMesh;
    167.         //Create a new Mesh using the Objects MeshFilter
    168.         surfaceMesh = surface.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    169.         surfaceMesh.name = surface.name;
    170.  
    171.         //Step through the Vertex Buffer
    172.         for (int triIndex = 0, vertIndex = 0, i = 0; i < vertBufferSize; i++, vertIndex++)
    173.         {
    174.             for (int j = 0; j < vertBufferSize; j++, triIndex += 6, vertIndex++)
    175.             {
    176.                 //  1
    177.                 //  | \
    178.                 //  |  \
    179.                 //  |   \
    180.                 //  0----2
    181.                 vertexBuffer[triIndex]      = vertIndex;
    182.                 vertexBuffer[triIndex + 1]  = vertIndex + vertsPerSubDivision;
    183.                 vertexBuffer[triIndex + 2]  = vertIndex + 1;
    184.                 //  1----3
    185.                 //    \  |
    186.                 //     \ |
    187.                 //      \|
    188.                 //       2
    189.                 vertexBuffer[triIndex + 3]  = vertIndex + vertsPerSubDivision;
    190.                 vertexBuffer[triIndex + 4]  = vertIndex + vertsPerSubDivision + 1;
    191.                 vertexBuffer[triIndex + 5]  = vertIndex + 1;
    192.             }
    193.         }
    194.         //Assign the Vertex Array from Generate Surface Coordinates to the Mesh Vertex Array
    195.         surfaceMesh.vertices = vertexArray;
    196.         //Assign the UV Coordinates
    197.         surfaceMesh.uv = uvArray;
    198.         //Assign the Vertex Buffer
    199.         surfaceMesh.triangles = vertexBuffer;
    200.         //Recalculate the Normals
    201.         surfaceMesh.RecalculateNormals();
    202.         //After the Mesh has been created, pass it back to the MeshCollider
    203.         surface.GetComponent<MeshCollider>().sharedMesh = surfaceMesh;
    204.     }
    205.  
    206.     public void GenerateSurfaceTextures(int index)
    207.     {
    208.         //Texture size is equal to the number of sub divisions x the vertices per subdivision
    209.         int textureSize = subDivisions * vertsPerSubDivision;
    210.         //Create the texture and pixel color arrays
    211.         textures = new Texture2D[6];
    212.         Color[] pixelColor = new Color[textureSize * textureSize];
    213.  
    214.         for (int i = 0; i < 6; i++)
    215.         {
    216.             textures[i] = new Texture2D(textureSize, textureSize);
    217.  
    218.             float y = 0.0f;
    219.             while (y < textureSize)
    220.             {
    221.                 float x = 0.0f;
    222.                 while (x < textureSize)
    223.                 {
    224.                     float xCoord = xOrg + x / textureSize * scale;
    225.                     float yCoord = xOrg + y / textureSize * scale;
    226.                     float sample = Mathf.PerlinNoise(xCoord, yCoord);
    227.                     pixelColor[(int)y * textureSize + (int)x] = new Color(sample, sample, sample);
    228.                     x++;
    229.                 }
    230.                 y++;
    231.             }
    232.             textures[i].SetPixels(pixelColor);
    233.             textures[i].Apply();
    234.         }
    235.     }
    236. }
    237.  
     
  9. vikankraft

    vikankraft

    Joined:
    Feb 25, 2016
    Posts:
    88
    So cool! You are making your own Terrangen? What noise do you use?
     
  10. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Thanks, and right now just the default Unity Perlin.

    So, here's what I've more or less figured out for creating a cubemap with noise....

    1. Determine what side of the cube map you're going to start on
    2. Using the Vertices that are making up the cube face, figure out where that vertex lies on the cube map face
    3. Sample the above location to determine the 'height' of the noise
    4. Add that noise to the radius.

    So I have to figure out the math to map a cube face point to my sphere...and maybe 3D noise....

    Will continue after work!
     
  11. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Alrighty...well, got myself back to where I was. The noise stuff is working pretty good, the only problem is I have seams between each patch. I tried a combine function, but that didn't work. Evidently I had to many pieces to combine.

    Anyone have any ideas/insights? I'm sort of approaching the limit of what I can figure out and complete, so help would definitely be appreciated!

    Also...Anyone have any idea how to get a seamless set of textures generated? Right now I just generate six separate and independent textures that are in no way contiguous with one another (which you can see). Each face though works out well. I need to decouple the size of the texture maps from the size of each face, but for now this is working as a good starting point for understanding what's going on...and I can easily enough adapt the existing code for different size height maps once I get the seamless bits and joined meshes working.

    Here's the updated code!

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. using LibNoise;
    6. using LibNoise.Generator;
    7. using LibNoise.Operator;
    8.  
    9. public class NewTestPlanet : MonoBehaviour
    10. {
    11.     public float planetRadius = 10.0f;
    12.  
    13.     //The number of Subdivions per Face
    14.     public int subDivisions = 2;
    15.     //The number of Vertices per Subdivision
    16.     public int vertsPerSubDivision = 4;
    17.  
    18.     //Texture Data
    19.     public float xOrg = 1.0f;
    20.     public float yOrg = 1.0f;
    21.     public float scale = 10.0f;
    22.  
    23.     public enum cubeIndex { TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK }
    24.  
    25.     public Texture2D[] textures = new Texture2D[6];
    26.  
    27.     public List<Mesh> meshList;
    28.  
    29.     void Start()
    30.     {
    31.         //gameObject.AddComponent<MeshFilter>();
    32.  
    33.         CreateSubdivisionSurfaces();
    34.     }
    35.  
    36.     //Responsible for calling CreateSurface. Passes the following arguments to CreateSurface():
    37.     //  x-index (i)
    38.     //  z-index (j)
    39.     //  cube face (index)
    40.     void CreateSubdivisionSurfaces()
    41.     {
    42.         //For each face and for each Sub Division Surface, Create a new Surface
    43.         for (int index = 0; index < 6; index++)
    44.         {
    45.             for (int i = 0; i < subDivisions; i++)
    46.             {
    47.                 for (int j = 0; j < subDivisions; j++)
    48.                 {
    49.                     GenerateSurfaceTextures(index);
    50.                     GenerateSurfaceCoordinates(i, j, (cubeIndex)index);
    51.                 }
    52.             }
    53.         }
    54.         //Now that everything has been created, combine it...
    55.         //CombineSurfaceMesh();
    56.     }
    57.  
    58.     //Creates the surfaces called by CreateSubdivisionSurfaces(). Creates a GameObject
    59.     //assigns a MeshRenderer and a MeshCollider to the created Game Object.
    60.     void GenerateSurfaceCoordinates(int xIndex, int zIndex, cubeIndex facing)
    61.     {
    62.         //Appropriately size (and create) the Vertex Array
    63.         Vector3[] vertices = new Vector3[vertsPerSubDivision * vertsPerSubDivision];
    64.         //Appropriately size (and create) the UV Array
    65.         Vector2[] uv  = new Vector2[vertices.Length];
    66.  
    67.         //Calculate the increment to keep the vertices constrained to a 1x1 cube.
    68.         float increment = (1.0f / ((float)vertsPerSubDivision - 1)) / (float)subDivisions;
    69.         float uvIncrement = (1.0f / ((float)vertsPerSubDivision - 1)) / (float)subDivisions;
    70.         //A float to hold returned noise for a given pixel
    71.         //Texture size is equal to the number of sub divisions x the vertices per subdivision
    72.         int textureSize = subDivisions * vertsPerSubDivision;
    73.         //Create the pixel color array
    74.         float[] noise = new float[textureSize * textureSize];
    75.  
    76.         for (int i = 0, index = 0; i < vertsPerSubDivision; i++)
    77.         {
    78.             for (int j = 0; j < vertsPerSubDivision; j++, index++)
    79.             {
    80.                 //Vertex Coordinates
    81.                 float xPos = (float)j * increment - 0.5f + ((float)xIndex / (float)subDivisions);
    82.                 float yPos = 0.5f;
    83.                 float zPos = (float)i * increment - 0.5f + ((float)zIndex / (float)subDivisions);
    84.                 //UV Coordinates
    85.                 float xUV = (float)j * uvIncrement + ((float)xIndex / (float)subDivisions);
    86.                 float zUV = (float)i * uvIncrement + ((float)zIndex / (float)subDivisions);
    87.  
    88.                 switch(facing)
    89.                 {
    90.                     case cubeIndex.TOP:
    91.                         //Assign Vertex Coordinates
    92.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    93.                         //Assign UV Coordinates
    94.                         uv[index] = new Vector2(xUV, zUV);
    95.                         //Store noise values
    96.                         noise[index] = textures[(int)facing].GetPixel(j + (xIndex * vertsPerSubDivision), i + (zIndex * vertsPerSubDivision)).grayscale;
    97.                         break;
    98.  
    99.                     case cubeIndex.BOTTOM:
    100.                         //Assign Vertex Coordinates
    101.                         vertices[index] = new Vector3(xPos, -yPos, -zPos);
    102.                         //Assign UV Coordinates
    103.                         uv[index] = new Vector2(xUV, zUV);
    104.                         //Store noise values
    105.                         noise[index] = textures[(int)facing].GetPixel(j + (xIndex * vertsPerSubDivision), i + (zIndex * vertsPerSubDivision)).grayscale;
    106.                         break;
    107.  
    108.                     case cubeIndex.LEFT:
    109.                         //Assign Vertex Coordinates
    110.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    111.                         //Assign UV Coordinates
    112.                         uv[index] = new Vector2(xUV, zUV);
    113.                         //Store noise values
    114.                         noise[index] = textures[(int)facing].GetPixel(j + (xIndex * vertsPerSubDivision), i + (zIndex * vertsPerSubDivision)).grayscale;
    115.                         break;
    116.  
    117.                     case cubeIndex.RIGHT:
    118.                         //Assign Vertex Coordinates
    119.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    120.                         //Assign UV Coordinates
    121.                         uv[index] = new Vector2(xUV, zUV);
    122.                         //Store noise values
    123.                         noise[index] = textures[(int)facing].GetPixel(j + (xIndex * vertsPerSubDivision), i + (zIndex * vertsPerSubDivision)).grayscale;
    124.                         break;
    125.  
    126.                     case cubeIndex.FRONT:
    127.                         //Assign Vertex Coordinates
    128.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    129.                         //Assign UV Coordinates
    130.                         uv[index] = new Vector2(xUV, zUV);
    131.                         //Store noise values
    132.                         noise[index] = textures[(int)facing].GetPixel(j + (xIndex * vertsPerSubDivision), i + (zIndex * vertsPerSubDivision)).grayscale;
    133.                         break;
    134.  
    135.                     case cubeIndex.BACK:
    136.                         //Assign Vertex Coordinates
    137.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    138.                         //Assign UV Coordinates
    139.                         uv[index] = new Vector2(xUV, zUV);
    140.                         //Store noise values
    141.                         noise[index] = textures[(int)facing].GetPixel(j + (xIndex * vertsPerSubDivision), i + (zIndex * vertsPerSubDivision)).grayscale;
    142.                         break;
    143.                 }
    144.  
    145.                 //Spherize the Vertices
    146.                 vertices[index] = vertices[index].normalized;
    147.                 //Set the newly created sphere equal to the Radius
    148.                 vertices[index] *= planetRadius + noise[index];
    149.             }
    150.         }
    151.         //Create the Surface Object
    152.         CreateSurfaceObject(vertices, uv, facing, xIndex, zIndex);
    153.     }
    154.  
    155.     //Creates a new GameObject to which a MeshRenderer, MeshCollider and MeshFilter are attached.
    156.     //This is then passed to CreateMeshSurface where the actual Mesh is created.
    157.     void CreateSurfaceObject(Vector3[] vertexArray, Vector2[] uvArray, cubeIndex facing, int xIndex, int zIndex)
    158.     {
    159.         //Create a new GameObject
    160.         GameObject surface = new GameObject(facing + "Surface(" + xIndex + ", " + zIndex + ")");
    161.         surface.tag     = gameObject.tag;
    162.         surface.layer   = gameObject.layer;
    163.         surface.transform.parent    = transform;
    164.         surface.transform.position  = transform.position;
    165.         //Add the MeshRenderer
    166.         surface.gameObject.AddComponent<MeshRenderer>();
    167.         //Add the MeshCollider
    168.         surface.gameObject.AddComponent<MeshCollider>();
    169.         //Add the MeshFilter
    170.         surface.gameObject.AddComponent<MeshFilter>();
    171.  
    172.         //Initialize the Renderer
    173.         surface.GetComponent<Renderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    174.         surface.GetComponent<Renderer>().receiveShadows = true;
    175.         surface.GetComponent<Renderer>().enabled = true;
    176.  
    177.         //Assign the generated textures to the cube sphere
    178.         surface.GetComponent<Renderer>().material.mainTexture = textures[(int)facing];
    179.  
    180.         //Create the Mesh
    181.         CreateSurfaceMesh(surface, vertexArray, uvArray, facing);
    182.     }
    183.  
    184.     void CreateSurfaceMesh(GameObject surface, Vector3[] vertexArray, Vector2[] uvArray, cubeIndex facing)
    185.     {
    186.         //Create and size the Vertex Buffer
    187.         int vertBufferSize = vertsPerSubDivision - 1;
    188.         int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    189.         //Create a new Mesh object
    190.         Mesh surfaceMesh;
    191.         //Create a new Mesh using the Objects MeshFilter
    192.         surfaceMesh = surface.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    193.         surfaceMesh.name = surface.name;
    194.  
    195.         //Step through the Vertex Buffer
    196.         for (int triIndex = 0, vertIndex = 0, i = 0; i < vertBufferSize; i++, vertIndex++)
    197.         {
    198.             for (int j = 0; j < vertBufferSize; j++, triIndex += 6, vertIndex++)
    199.             {
    200.                 //  1
    201.                 //  | \
    202.                 //  |  \
    203.                 //  |   \
    204.                 //  0----2
    205.                 vertexBuffer[triIndex]      = vertIndex;
    206.                 vertexBuffer[triIndex + 1]  = vertIndex + vertsPerSubDivision;
    207.                 vertexBuffer[triIndex + 2]  = vertIndex + 1;
    208.                 //  1----3
    209.                 //    \  |
    210.                 //     \ |
    211.                 //      \|
    212.                 //       2
    213.                 vertexBuffer[triIndex + 3]  = vertIndex + vertsPerSubDivision;
    214.                 vertexBuffer[triIndex + 4]  = vertIndex + vertsPerSubDivision + 1;
    215.                 vertexBuffer[triIndex + 5]  = vertIndex + 1;
    216.             }
    217.         }
    218.         //Assign the Vertex Array from Generate Surface Coordinates to the Mesh Vertex Array
    219.         surfaceMesh.vertices = vertexArray;
    220.         //Assign the UV Coordinates
    221.         surfaceMesh.uv = uvArray;
    222.         //Assign the Vertex Buffer
    223.         surfaceMesh.triangles = vertexBuffer;
    224.         //Recalculate the Normals
    225.         surfaceMesh.RecalculateNormals();
    226.         //After the Mesh has been created, pass it back to the MeshCollider
    227.         surface.GetComponent<MeshCollider>().sharedMesh = surfaceMesh;
    228.  
    229.         meshList.Add(surfaceMesh);
    230.     }
    231.  
    232.     //This is a test...it doesn't work...well, it does...but there are to many surface objects for it to work...
    233.     public void CombineSurfaceMesh()
    234.     {
    235.         //Acquire ALL the mesh filters created
    236.         MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
    237.         //Create a new combine instance array equal to the size of the number of mesh filters in the object
    238.         CombineInstance[] combine = new CombineInstance[meshFilters.Length];
    239.  
    240.         for (int i = 0; i < meshFilters.Length; i++)
    241.         {
    242.             combine[i].mesh = meshFilters[i].sharedMesh;
    243.             combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
    244.             meshFilters[i].gameObject.SetActive(false);
    245.         }
    246.  
    247.         transform.GetComponent<MeshFilter>().mesh = new Mesh();
    248.         transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combine);
    249.         transform.gameObject.SetActive(true);
    250.     }
    251.  
    252.     public void GenerateSurfaceTextures(int index)
    253.     {
    254.         //Texture size is equal to the number of sub divisions x the vertices per subdivision
    255.         int textureSize = subDivisions * vertsPerSubDivision;
    256.         //Create the pixel color array
    257.         Color[] pixelColor = new Color[textureSize * textureSize];
    258.  
    259.         for (int i = 0; i < 6; i++)
    260.         {
    261.             textures[i] = new Texture2D(textureSize, textureSize);
    262.             textures[i].wrapMode = TextureWrapMode.Clamp;
    263.  
    264.             float y = 0.0f;
    265.             while (y < textureSize)
    266.             {
    267.                 float x = 0.0f;
    268.                 while (x < textureSize)
    269.                 {
    270.                     float xCoord = xOrg + x / textureSize * scale;
    271.                     float yCoord = xOrg + y / textureSize * scale;
    272.                     float sample = Mathf.PerlinNoise(xCoord, yCoord);
    273.                     pixelColor[(int)y * textureSize + (int)x] = new Color(sample, sample, sample);
    274.                     x++;
    275.                 }
    276.                 y++;
    277.             }
    278.             textures[i].SetPixels(pixelColor);
    279.             textures[i].Apply();
    280.         }
    281.     }
    282. }
    283.  
     

    Attached Files:

  12. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    It is amazing the work you have put into this!

    Something I noticed is when you call GenerateSurfaceTextures(index) in the loop, it recreates the whole array each time. I know its a work-in-progress, but for now maybe use flat textures, generated before the meshes so they can be applied to the renderers.

    RecalculateNormals doesn't do a perfect job, the seams can be noticed when you use a flat colour. For a perfect sphere just using the vertex normalized is fine, but for a noisy sphere .... huge task. CatLikeCoding covers this in this post (scroll down to Calculating Normals, about 2/5 down the page).
    To calculate normals ourselves, we have to loop through all vertices and figure out the normal of each one. Because our mesh is a grid, most vertices have four neighbors. If you connect opposite neighbors, you get two lines that form a cross. Those two lines define a plane, which we can consider an approximation of the tangent plane of the slope at the center vertex. If we take the cross product of those two vectors and normalize it, we end up with the normal of that slope.

    As we consider the two lines lines tangent to the slope at our vertex, then if we scale them so that their X or Z component is 1, then their Y component is the proper rate of change in that direction. Let's go one step at a time, and misuse our normals to show the rate of change – the derivative – of the X direction only. For that reason we won't assign our normals to the mesh just yet
    Which also brings up mesh tangents if you want to create bumpmaps as well as textures.

    With noise, 3D Simplex noise is the way to go. You can get a value directly using the Vector3 vertex position, even do some calculations to get a fractal noise rather than just basic waves.

    Overall I think creating procedural textures is a mammoth task. Just like adding noise to the vertices, you will have to know where every pixel is located in 3D space to evaluate 3D noise and apply it to the texture. Have you considered using vertex colours instead of textures? This could be calculated and applied while you are doing the verts and uvs (and normals eventually).

    Here are some scripts from playing around with your amazing work.

    This one creates flat grey textures, then uses 3D noise to offset the heights. I have attached the Noise script I used :
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4.  
    5. public class PlanetTest : MonoBehaviour
    6. {
    7.    public float noiseScale = 2.8f;
    8.    public float noiseHeight = 0.5f;
    9.  
    10.  
    11.    //   -------------------------------------------------------  Persistent Functions
    12.  
    13.  
    14.    void Start()
    15.    {
    16.      CreateSubdivisionSurfaces();
    17.    }
    18.  
    19.  
    20.    //   -------------------------------------------------------  Planet Generation Functions
    21.  
    22.  
    23.    public float planetRadius = 10.0f;
    24.  
    25.    //The number of Subdivions per Face
    26.    public int subDivisions = 4;
    27.    //The number of Vertices per Subdivision
    28.    public int vertsPerSubDivision = 16;
    29.  
    30.    //Texture Data
    31.    public float xOrg = 1.0f;
    32.    public float yOrg = 1.0f;
    33.    public float scale = 10.0f;
    34.  
    35.    public enum cubeIndex { TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK }
    36.  
    37.    public Texture2D[] textures;
    38.  
    39.    //Responsible for calling CreateSurface. Passes the following arguments to CreateSurface():
    40.    //  x-index (i)
    41.    //  z-index (j)
    42.    //  cube face (index)
    43.    void CreateSubdivisionSurfaces()
    44.    {
    45.      // ** FOR TESTING **
    46.      GenerateSurfaceTextures();
    47.  
    48.      //For each face and for each Sub Division Surface, Create a new Surface
    49.      for (int index = 0; index < 6; index++)
    50.      {
    51.        for (int i = 0; i < subDivisions; i++)
    52.        {
    53.          for (int j = 0; j < subDivisions; j++)
    54.          {
    55.            //GenerateSurfaceTextures(index);
    56.            GenerateSurfaceCoordinates(i, j, (cubeIndex)index);
    57.          }
    58.        }
    59.      }
    60.    }
    61.  
    62.    //Creates the surfaces called by CreateSubdivisionSurfaces(). Creates a GameObject
    63.    //assigns a MeshRenderer and a MeshCollider to the created Game Object.
    64.    void GenerateSurfaceCoordinates(int xIndex, int zIndex, cubeIndex facing)
    65.    {
    66.      //Appropriately size (and create) the Vertex Array
    67.      Vector3[] vertices = new Vector3[vertsPerSubDivision * vertsPerSubDivision];
    68.      //Appropriately size (and create) the UV Array
    69.      Vector2[] uv  = new Vector2[vertices.Length];
    70.  
    71.      //Calculate the increment to keep the vertices constrained to a 1x1 cube.
    72.      float increment = (1.0f / ((float)vertsPerSubDivision - 1)) / (float)subDivisions;
    73.      float uvIncrement = (1.0f / ((float)vertsPerSubDivision - 1)) / (float)subDivisions;
    74.  
    75.      for (int i = 0, index = 0; i < vertsPerSubDivision; i++)
    76.      {
    77.        for (int j = 0; j < vertsPerSubDivision; j++, index++)
    78.        {
    79.          //Vertex Coordinates
    80.          float xPos = (float)j * increment - 0.5f + ((float)xIndex / (float)subDivisions);
    81.          float yPos = 0.5f;
    82.          float zPos = (float)i * increment - 0.5f + ((float)zIndex / (float)subDivisions);
    83.          //UV Coordinates
    84.          float xUV = (float)j * uvIncrement + ((float)xIndex / (float)subDivisions);
    85.          float zUV = (float)i * uvIncrement + ((float)zIndex / (float)subDivisions);
    86.      
    87.          switch(facing)
    88.          {
    89.            case cubeIndex.TOP:
    90.              //Assign Vertex Coordinates
    91.              vertices[index] = new Vector3(xPos, yPos, zPos);
    92.              //Assign UV Coordinates
    93.              uv[index] = new Vector2(xUV, zUV);
    94.              break;
    95.          
    96.            case cubeIndex.BOTTOM:
    97.              //Assign Vertex Coordinates
    98.              vertices[index] = new Vector3(-xPos, -yPos, zPos);
    99.              //Assign UV Coordinates
    100.              uv[index] = new Vector2(xUV, zUV);
    101.              break;
    102.          
    103.            case cubeIndex.LEFT:
    104.              //Assign Vertex Coordinates
    105.              vertices[index] = new Vector3(-yPos, zPos, -xPos);
    106.              //Assign UV Coordinates
    107.              uv[index] = new Vector2(xUV, zUV);
    108.              break;
    109.          
    110.            case cubeIndex.RIGHT:
    111.              //Assign Vertex Coordinates
    112.              vertices[index] = new Vector3(yPos, zPos, xPos);
    113.              //Assign UV Coordinates
    114.              uv[index] = new Vector2(xUV, zUV);
    115.              break;
    116.          
    117.            case cubeIndex.FRONT:
    118.              //Assign Vertex Coordinates
    119.              vertices[index] = new Vector3(-xPos, zPos, yPos);
    120.              //Assign UV Coordinates
    121.              uv[index] = new Vector2(xUV, zUV);
    122.              break;
    123.          
    124.            case cubeIndex.BACK:
    125.              //Assign Vertex Coordinates
    126.              vertices[index] = new Vector3(xPos, zPos, -yPos);
    127.              //Assign UV Coordinates
    128.              uv[index] = new Vector2(xUV, zUV);
    129.              break;
    130.          }
    131.      
    132.          //Spherize the Vertices
    133.          vertices[index] = vertices[index].normalized;
    134.      
    135.          // get noise
    136.          float noise = Noise.Noise.GetNoise( vertices[index].x * noiseScale, vertices[index].y * noiseScale, vertices[index].z * noiseScale );
    137.          noise *= noiseHeight;
    138.      
    139.          //Set the newly created sphere equal to the Radius
    140.          vertices[index] *= planetRadius + noise;
    141.        }
    142.      }
    143.      //Create the Surface Object
    144.      CreateSurfaceObject(vertices, uv, facing, xIndex, zIndex);
    145.    }
    146.  
    147.    //Creates a new GameObject to which a MeshRenderer, MeshCollider and MeshFilter are attached.
    148.    //This is then passed to CreateMeshSurface where the actual Mesh is created.
    149.    void CreateSurfaceObject(Vector3[] vertexArray, Vector2[] uvArray, cubeIndex facing, int xIndex, int zIndex)
    150.    {
    151.      //Create a new GameObject
    152.      GameObject surface = new GameObject(facing + "Surface(" + xIndex + ", " + zIndex + ")");
    153.      surface.tag  = gameObject.tag;
    154.      surface.layer  = gameObject.layer;
    155.      surface.transform.parent  = transform;
    156.      surface.transform.position  = transform.position;
    157.      //Add the MeshRenderer
    158.      surface.gameObject.AddComponent<MeshRenderer>();
    159.      //Add the MeshCollider
    160.      surface.gameObject.AddComponent<MeshCollider>();
    161.      //Add the MeshFilter
    162.      surface.gameObject.AddComponent<MeshFilter>();
    163.  
    164.      //Initialize the Renderer
    165.      surface.GetComponent<Renderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    166.      surface.GetComponent<Renderer>().receiveShadows = true;
    167.      surface.GetComponent<Renderer>().enabled = true;
    168.  
    169.      //Assign the generated textures to the cube sphere
    170.      surface.GetComponent<Renderer>().material.mainTexture = textures[(int)facing];
    171.  
    172.      //Create the Mesh
    173.      CreateSurfaceMesh(surface, vertexArray, uvArray, facing);
    174.    }
    175.  
    176.    void CreateSurfaceMesh(GameObject surface, Vector3[] vertexArray, Vector2[] uvArray, cubeIndex facing)
    177.    {
    178.      //Create and size the Vertex Buffer
    179.      int vertBufferSize = vertsPerSubDivision - 1;
    180.      int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    181.      //Create a new Mesh object
    182.      Mesh surfaceMesh;
    183.      //Create a new Mesh using the Objects MeshFilter
    184.      surfaceMesh = surface.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    185.      surfaceMesh.name = surface.name;
    186.  
    187.      //Step through the Vertex Buffer
    188.      for (int triIndex = 0, vertIndex = 0, i = 0; i < vertBufferSize; i++, vertIndex++)
    189.      {
    190.        for (int j = 0; j < vertBufferSize; j++, triIndex += 6, vertIndex++)
    191.        {
    192.          //  1
    193.          //  | \
    194.          //  |  \
    195.          //  |  \
    196.          //  0----2
    197.          vertexBuffer[triIndex]  = vertIndex;
    198.          vertexBuffer[triIndex + 1]  = vertIndex + vertsPerSubDivision;
    199.          vertexBuffer[triIndex + 2]  = vertIndex + 1;
    200.          //  1----3
    201.          //  \  |
    202.          //  \ |
    203.          //  \|
    204.          //  2
    205.          vertexBuffer[triIndex + 3]  = vertIndex + vertsPerSubDivision;
    206.          vertexBuffer[triIndex + 4]  = vertIndex + vertsPerSubDivision + 1;
    207.          vertexBuffer[triIndex + 5]  = vertIndex + 1;
    208.        }
    209.      }
    210.      //Assign the Vertex Array from Generate Surface Coordinates to the Mesh Vertex Array
    211.      surfaceMesh.vertices = vertexArray;
    212.      //Assign the UV Coordinates
    213.      surfaceMesh.uv = uvArray;
    214.      //Assign the Vertex Buffer
    215.      surfaceMesh.triangles = vertexBuffer;
    216.      //Recalculate the Normals
    217.      surfaceMesh.RecalculateNormals();
    218.      //After the Mesh has been created, pass it back to the MeshCollider
    219.      surface.GetComponent<MeshCollider>().sharedMesh = surfaceMesh;
    220.    }
    221.  
    222.    public void GenerateSurfaceTextures( int index )
    223.    {
    224.      //Texture size is equal to the number of sub divisions x the vertices per subdivision
    225.      int textureSize = subDivisions * vertsPerSubDivision;
    226.      //Create the texture and pixel color arrays
    227.      textures = new Texture2D[6];
    228.      Color[] pixelColor = new Color[textureSize * textureSize];
    229.  
    230.      for (int i = 0; i < 6; i++)
    231.      {
    232.        textures[i] = new Texture2D(textureSize, textureSize);
    233.    
    234.        float y = 0.0f;
    235.        while (y < textureSize)
    236.        {
    237.          float x = 0.0f;
    238.          while (x < textureSize)
    239.          {
    240.            float xCoord = xOrg + x / textureSize * scale;
    241.            float yCoord = xOrg + y / textureSize * scale;
    242.            float sample = Mathf.PerlinNoise(xCoord, yCoord);
    243.            pixelColor[(int)y * textureSize + (int)x] = new Color(sample, sample, sample);
    244.            x++;
    245.          }
    246.          y++;
    247.        }
    248.        textures[i].SetPixels(pixelColor);
    249.        textures[i].Apply();
    250.      }
    251.    }
    252.  
    253.    // ** FOR TESTING **
    254.    public void GenerateSurfaceTextures()
    255.    {
    256.      //Texture size is equal to the number of sub divisions x the vertices per subdivision
    257.      int textureSize = subDivisions * vertsPerSubDivision;
    258.      //Create the texture and pixel color arrays
    259.      textures = new Texture2D[6];
    260.      Color[] pixelColor = new Color[textureSize * textureSize];
    261.  
    262.      for (int i = 0; i < 6; i++)
    263.      {
    264.        textures[i] = new Texture2D(textureSize, textureSize);
    265.    
    266.        float y = 0.0f;
    267.        while (y < textureSize)
    268.        {
    269.          float x = 0.0f;
    270.          while (x < textureSize)
    271.          {
    272.            pixelColor[(int)y * textureSize + (int)x] = Color.grey;
    273.            x++;
    274.          }
    275.          y++;
    276.        }
    277.        textures[i].SetPixels(pixelColor);
    278.        textures[i].Apply();
    279.      }
    280.    }
    281. }

    This script uses Vertex Colours instead of textures. Create a material, use Custom/VertexColored shader, then drop in the inspector. I have attached the Shader I used :
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4.  
    5. public class PlanetTestVertexColour : MonoBehaviour
    6. {
    7.    public float noiseScale = 2.8f;
    8.    public float noiseHeight = 0.5f;
    9.  
    10.    public Material vertexColoured;
    11.  
    12.    public Gradient planetColours;
    13.  
    14.  
    15.    //   -------------------------------------------------------  Persistent Functions
    16.  
    17.  
    18.    void Start()
    19.    {
    20.      CreateColourGradient();
    21.      CreateSubdivisionSurfaces();
    22.    }
    23.  
    24.  
    25.    //   -------------------------------------------------------  Planet Generation Functions
    26.  
    27.  
    28.    public float planetRadius = 10.0f;
    29.  
    30.    //The number of Subdivions per Face
    31.    public int subDivisions = 4;
    32.    //The number of Vertices per Subdivision
    33.    public int vertsPerSubDivision = 16;
    34.  
    35.    //Texture Data
    36.    public float xOrg = 1.0f;
    37.    public float yOrg = 1.0f;
    38.    public float scale = 10.0f;
    39.  
    40.    public enum cubeIndex { TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK }
    41.  
    42.    //Responsible for calling CreateSurface. Passes the following arguments to CreateSurface():
    43.    //  x-index (i)
    44.    //  z-index (j)
    45.    //  cube face (index)
    46.    void CreateSubdivisionSurfaces()
    47.    {
    48.      //For each face and for each Sub Division Surface, Create a new Surface
    49.      for (int index = 0; index < 6; index++)
    50.      {
    51.        for (int i = 0; i < subDivisions; i++)
    52.        {
    53.          for (int j = 0; j < subDivisions; j++)
    54.          {
    55.            //GenerateSurfaceTextures(index);
    56.            GenerateSurfaceCoordinates(i, j, (cubeIndex)index);
    57.          }
    58.        }
    59.      }
    60.    }
    61.  
    62.    //Creates the surfaces called by CreateSubdivisionSurfaces(). Creates a GameObject
    63.    //assigns a MeshRenderer and a MeshCollider to the created Game Object.
    64.    void GenerateSurfaceCoordinates(int xIndex, int zIndex, cubeIndex facing)
    65.    {
    66.      //Appropriately size (and create) the Vertex Array
    67.      Vector3[] vertices = new Vector3[vertsPerSubDivision * vertsPerSubDivision];
    68.      //Appropriately size (and create) the UV Array
    69.      Vector2[] uv  = new Vector2[vertices.Length];
    70.      //Appropriately size (and create) the Vertex Colour Array
    71.      Color[] colours = new Color[vertices.Length];
    72.  
    73.      //Calculate the increment to keep the vertices constrained to a 1x1 cube.
    74.      float increment = (1.0f / ((float)vertsPerSubDivision - 1)) / (float)subDivisions;
    75.      float uvIncrement = (1.0f / ((float)vertsPerSubDivision - 1)) / (float)subDivisions;
    76.  
    77.      for (int i = 0, index = 0; i < vertsPerSubDivision; i++)
    78.      {
    79.        for (int j = 0; j < vertsPerSubDivision; j++, index++)
    80.        {
    81.          //Vertex Coordinates
    82.          float xPos = (float)j * increment - 0.5f + ((float)xIndex / (float)subDivisions);
    83.          float yPos = 0.5f;
    84.          float zPos = (float)i * increment - 0.5f + ((float)zIndex / (float)subDivisions);
    85.          //UV Coordinates
    86.          float xUV = (float)j * uvIncrement + ((float)xIndex / (float)subDivisions);
    87.          float zUV = (float)i * uvIncrement + ((float)zIndex / (float)subDivisions);
    88.      
    89.          switch(facing)
    90.          {
    91.            case cubeIndex.TOP:
    92.              //Assign Vertex Coordinates
    93.              vertices[index] = new Vector3(xPos, yPos, zPos);
    94.              //Assign UV Coordinates
    95.              uv[index] = new Vector2(xUV, zUV);
    96.              break;
    97.          
    98.            case cubeIndex.BOTTOM:
    99.              //Assign Vertex Coordinates
    100.              vertices[index] = new Vector3(-xPos, -yPos, zPos);
    101.              //Assign UV Coordinates
    102.              uv[index] = new Vector2(xUV, zUV);
    103.              break;
    104.          
    105.            case cubeIndex.LEFT:
    106.              //Assign Vertex Coordinates
    107.              vertices[index] = new Vector3(-yPos, zPos, -xPos);
    108.              //Assign UV Coordinates
    109.              uv[index] = new Vector2(xUV, zUV);
    110.              break;
    111.          
    112.            case cubeIndex.RIGHT:
    113.              //Assign Vertex Coordinates
    114.              vertices[index] = new Vector3(yPos, zPos, xPos);
    115.              //Assign UV Coordinates
    116.              uv[index] = new Vector2(xUV, zUV);
    117.              break;
    118.          
    119.            case cubeIndex.FRONT:
    120.              //Assign Vertex Coordinates
    121.              vertices[index] = new Vector3(-xPos, zPos, yPos);
    122.              //Assign UV Coordinates
    123.              uv[index] = new Vector2(xUV, zUV);
    124.              break;
    125.          
    126.            case cubeIndex.BACK:
    127.              //Assign Vertex Coordinates
    128.              vertices[index] = new Vector3(xPos, zPos, -yPos);
    129.              //Assign UV Coordinates
    130.              uv[index] = new Vector2(xUV, zUV);
    131.              break;
    132.          }
    133.      
    134.          //Spherize the Vertices
    135.          vertices[index] = vertices[index].normalized;
    136.      
    137.          // get Noise
    138.          float noise = Noise.Noise.GetNoise( vertices[index].x * noiseScale, vertices[index].y * noiseScale, vertices[index].z * noiseScale );
    139.      
    140.          // get Vertex Colour
    141.          colours[index] = planetColours.Evaluate( noise );
    142.      
    143.          //Set the newly created sphere equal to the Radius
    144.          vertices[index] *= planetRadius + ( noise * noiseHeight );
    145.        }
    146.      }
    147.      //Create the Surface Object
    148.      CreateSurfaceObject(vertices, uv, colours, facing, xIndex, zIndex);
    149.    }
    150.  
    151.    //Creates a new GameObject to which a MeshRenderer, MeshCollider and MeshFilter are attached.
    152.    //This is then passed to CreateMeshSurface where the actual Mesh is created.
    153.    void CreateSurfaceObject(Vector3[] vertexArray, Vector2[] uvArray, Color[] colourArray, cubeIndex facing, int xIndex, int zIndex)
    154.    {
    155.      //Create a new GameObject
    156.      GameObject surface = new GameObject(facing + "Surface(" + xIndex + ", " + zIndex + ")");
    157.      surface.tag  = gameObject.tag;
    158.      surface.layer  = gameObject.layer;
    159.      surface.transform.parent  = transform;
    160.      surface.transform.position  = transform.position;
    161.      //Add the MeshRenderer
    162.      surface.gameObject.AddComponent<MeshRenderer>();
    163.      //Add the MeshCollider
    164.      surface.gameObject.AddComponent<MeshCollider>();
    165.      //Add the MeshFilter
    166.      surface.gameObject.AddComponent<MeshFilter>();
    167.  
    168.      //Initialize the Renderer
    169.      surface.GetComponent<Renderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    170.      surface.GetComponent<Renderer>().receiveShadows = true;
    171.      surface.GetComponent<Renderer>().enabled = true;
    172.  
    173.      //Assign the generated textures to the cube sphere
    174.      surface.GetComponent<Renderer>().material = vertexColoured;
    175.  
    176.      //Create the Mesh
    177.      CreateSurfaceMesh(surface, vertexArray, uvArray, colourArray, facing);
    178.    }
    179.  
    180.    void CreateSurfaceMesh(GameObject surface, Vector3[] vertexArray, Vector2[] uvArray, Color[] colourArray, cubeIndex facing)
    181.    {
    182.      //Create and size the Vertex Buffer
    183.      int vertBufferSize = vertsPerSubDivision - 1;
    184.      int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    185.      //Create a new Mesh object
    186.      Mesh surfaceMesh;
    187.      //Create a new Mesh using the Objects MeshFilter
    188.      surfaceMesh = surface.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    189.      surfaceMesh.name = surface.name;
    190.  
    191.      //Step through the Vertex Buffer
    192.      for (int triIndex = 0, vertIndex = 0, i = 0; i < vertBufferSize; i++, vertIndex++)
    193.      {
    194.        for (int j = 0; j < vertBufferSize; j++, triIndex += 6, vertIndex++)
    195.        {
    196.          //  1
    197.          //  | \
    198.          //  |  \
    199.          //  |  \
    200.          //  0----2
    201.          vertexBuffer[triIndex]  = vertIndex;
    202.          vertexBuffer[triIndex + 1]  = vertIndex + vertsPerSubDivision;
    203.          vertexBuffer[triIndex + 2]  = vertIndex + 1;
    204.          //  1----3
    205.          //  \  |
    206.          //  \ |
    207.          //  \|
    208.          //  2
    209.          vertexBuffer[triIndex + 3]  = vertIndex + vertsPerSubDivision;
    210.          vertexBuffer[triIndex + 4]  = vertIndex + vertsPerSubDivision + 1;
    211.          vertexBuffer[triIndex + 5]  = vertIndex + 1;
    212.        }
    213.      }
    214.      //Assign the Vertex Array from Generate Surface Coordinates to the Mesh Vertex Array
    215.      surfaceMesh.vertices = vertexArray;
    216.      //Assign the UV Coordinates
    217.      surfaceMesh.uv = uvArray;
    218.      //Assign the Vertex Buffer
    219.      surfaceMesh.triangles = vertexBuffer;
    220.      // Assign the Vertx Colours
    221.      surfaceMesh.colors = colourArray;
    222.      //Recalculate the Normals
    223.      surfaceMesh.RecalculateNormals();
    224.      //After the Mesh has been created, pass it back to the MeshCollider
    225.      surface.GetComponent<MeshCollider>().sharedMesh = surfaceMesh;
    226.    }
    227.  
    228.  
    229.    //   -------------------------------------------------------  Planet Colour Gradient Functions
    230.  
    231.  
    232.    void CreateColourGradient()
    233.    {
    234.      // using colour scale from LibNoise example : http://libnoise.sourceforge.net/tutorials/tutorial3.html
    235.      //renderer.AddGradientPoint (-1.0000, utils::Color (  0,  0, 128, 255)); // deeps
    236.      //renderer.AddGradientPoint (-0.2500, utils::Color (  0,  0, 255, 255)); // shallow
    237.      //renderer.AddGradientPoint ( 0.0000, utils::Color (  0, 128, 255, 255)); // shore
    238.      //renderer.AddGradientPoint ( 0.0625, utils::Color (240, 240,  64, 255)); // sand
    239.      //renderer.AddGradientPoint ( 0.1250, utils::Color ( 32, 160,  0, 255)); // grass
    240.      //renderer.AddGradientPoint ( 0.3750, utils::Color (224, 224,  0, 255)); // dirt
    241.      //renderer.AddGradientPoint ( 0.7500, utils::Color (128, 128, 128, 255)); // rock
    242.      //renderer.AddGradientPoint ( 1.0000, utils::Color (255, 255, 255, 255)); // snow
    243.  
    244.      planetColours = new Gradient();
    245.  
    246.      GradientColorKey[] gck = new GradientColorKey[8];
    247.      gck[0].color = CalculateGradientColour(  0,  0, 128 );
    248.      gck[0].time = CalculateGradientTime( -1.0000 );
    249.      gck[1].color = CalculateGradientColour(  0,  0, 255 );
    250.      gck[1].time = CalculateGradientTime( -0.2500 );
    251.      gck[2].color = CalculateGradientColour(  0, 128, 255 );
    252.      gck[2].time = CalculateGradientTime(  0.0000 );
    253.      gck[3].color = CalculateGradientColour(  240, 240,  64 );
    254.      gck[3].time = CalculateGradientTime(  0.0625 );
    255.      gck[4].color = CalculateGradientColour(  32, 160,  0 );
    256.      gck[4].time = CalculateGradientTime(  0.1250 );
    257.      gck[5].color = CalculateGradientColour( 224, 224,  0 );
    258.      gck[5].time = CalculateGradientTime(  0.3750 );
    259.      gck[6].color = CalculateGradientColour( 128, 128, 128 );
    260.      gck[6].time = CalculateGradientTime(  0.7500 );
    261.      gck[7].color = CalculateGradientColour( 255, 255, 255 );
    262.      gck[7].time = CalculateGradientTime(  1.0000 );
    263.  
    264.      GradientAlphaKey[] gak = new GradientAlphaKey[2];
    265.      gak[0].alpha = 1f;
    266.      gak[0].time = 0f;
    267.      gak[1].alpha = 1f;
    268.      gak[1].time = 1f;
    269.  
    270.      planetColours.SetKeys( gck, gak );
    271.    }
    272.  
    273.    Color CalculateGradientColour( int r, int g, int b )
    274.    {
    275.      return new Color( (float)r / 255f, (float)g / 255f, (float)b / 255f );
    276.    }
    277.  
    278.    float CalculateGradientTime( double t )
    279.    {
    280.      return (float)( ( t + 1 ) * 0.5 );
    281.    }
    282. }

    You can remove CreateColourGradient() from the start function and assign your own gradient colours in the inspector :)

    Again, great work, I look forward to seeing where you take this. All the Best.

    planettest.png


    Edit : not trying to persuade you to use vertex colours or anything, but I was playing around with fractal after posting the above. Modify the PlanetTestVertexColour script like this :
    Code (csharp):
    1. replace line 138 with :
    2. float noise = GetNoise( vertices[index] ); // Noise.Noise.GetNoise( vertices[index].x * noiseScale, vertices[index].y * noiseScale, vertices[index].z * noiseScale );
    3.  
    4. then add this function :
    5.    //   -------------------------------------------------------  Noise Functions
    6.  
    7.  
    8.    public bool useBasicNoise = false;
    9.  
    10.    public int octaves = 8;
    11.    public float amp = 1f;
    12.  
    13.  
    14.    float GetNoise( Vector3 vertex )
    15.    {
    16.      // basic
    17.      if ( useBasicNoise )
    18.      {
    19.        return Noise.Noise.GetNoise( vertex.x * noiseScale, vertex.y * noiseScale, vertex.z * noiseScale );
    20.      }
    21.    
    22.      // fractal
    23.      float noise = 0f;
    24.      float gain = 1f;
    25.      float factor = 0f;
    26.    
    27.      for ( int i = 0; i < octaves; i++ )
    28.      {
    29.        factor += 1f / gain;
    30.      
    31.        noise += Noise.Noise.GetNoise( vertex.x * noiseScale * gain, vertex.y * noiseScale * gain, vertex.z * noiseScale * gain ) * ( amp / gain );
    32.      
    33.        gain *= 2f;
    34.      }
    35.    
    36.      noise /= factor;
    37.    
    38.      return noise;
    39.    }

    I used 8 subdivisions and 16 verts per subdivision in the screenshots.
    The issue with the normals is still there, something you have to deal with using the texture or vertex colour method.

    planettestfractal.png


    Edit 2 :
    It just occurred to me that creating the textures would actually be the same method as for calculating the vertices! Just by using 1 for subdivision, and the pixels (eg 512) for verts per subdivision :D
    Also take a look at this CubeSphere tutorial by CatLikeCoding. It has a great method for spacing the vertices more evenly.
     

    Attached Files:

    Last edited: May 14, 2016
    Hanowde, landon912 and BradMick like this.
  13. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Wow! You're the man Alucardj! So...here's what I learned from what you did there, and please correct me if I'm wrong on my understanding:

    Rather than trying to find where a vertex falls on a flat plane it's easier to use 3D Noise. This 3D noise in my mind conceptually is like standing inside of a cloud. All around me is a noise, or fog or some other obstruction to vision. Some of that obscuration happens to be tangent with my sphere, my goal is to use the vertices of my sphere to find out how high that particular point in space is. So the noise is...just hangin' out in the world and we say 'at this vertex location, how 'high' is that noise?

    Does that make sense? I've got a bunch of clouds hanging out in the world and I'm just comparing how much of a cloud is at my vertex location in the 3D space. It could be 0, it could be 255, or some number there in between. After the sampling takes place, and Can then say 'hey, at this point on the texture map the brightness is 122, the vertex beside and around is uniformly (unlikely, but for the sake of argument) 120, the next vertex ring is 115. And that generates the height map based on the difference in altitude between the vertex post noise application and the vertex pre noise application.

    The shader is awesome too! I didn't even think to use a shader (mainly because I have zero clue how to right shader code...my one foray ended in utter disaster with wailing and gnashing of teeth and birds falling out of the sky ;))

    Cool! Thank you. I'll get back here once I've dug a bit more into what you've done and have a firmer grasp on what's going on!
     
  14. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    Your cloud analogy is spot on. At this vertex position, how 'dense' is the cloud. That's 3D noise.

    I made two scripts to help visualize this. (I really like procedural generation, and playing with noise!) :
    1/ SimplexNoiseExample.cs : create a sphere, attach the vertex coloured material and the script, then hit play.
    2/ SimplexNoiseCubes.cs : create an empty gameObject, attach the script, then hit play.

    While running, play around toggling the axis booleans, and changing the speed. The cube example is much more fun, can really visualize moving through the cloud of noise.

    I have to thank you too! First for the interesting question and your efforts. And I've always wanted to make procedural nebula skyboxes, and you've helped me think about how I can do that now (something you may be interested in as well).

    Oh, and about this link regarding spacing the vertices more evenly, I realized that your uvs are actually evenly spaced, so at the moment they are stretched and compressed between the vertices of your meshes.That's what makes uv mapping a sphere so much fun... But if you implement the more evenly spaced vertices, that will make the distortion a little less noticeable. I'll play around with it myself to see what I discover. But I already wrote in the spaced vertices if you want :
    Code (csharp):
    1. // replace :
    2. vertices[index] = vertices[index].normalized;
    3. // with :
    4. vertices[index] = SmoothedVertex( vertices[index] );
    5.  
    6. // then add this function :
    7.   Vector3 SmoothedVertex( Vector3 vertex )
    8.    {
    9.      Vector3 v = vertex * 2f;
    10.      float x2 = v.x * v.x;
    11.      float y2 = v.y * v.y;
    12.      float z2 = v.z * v.z;
    13.  
    14.      Vector3 s;
    15.      s.x = v.x * Mathf.Sqrt(1f - y2 / 2f - z2 / 2f + y2 * z2 / 3f);
    16.      s.y = v.y * Mathf.Sqrt(1f - x2 / 2f - z2 / 2f + x2 * z2 / 3f);
    17.      s.z = v.z * Mathf.Sqrt(1f - x2 / 2f - y2 / 2f + x2 * y2 / 3f);
    18.  
    19.      return s;
    20.    }

    I will stop making suggestions now, give you a chance to explore for yourself. :S


    EDIT : a little motivational teaser for you. Textures are definitely the way to go.

    planet_teaser.png

    Smoothing the vertices fixes the UV problem anyway.
    Generate each face texture the same way as for vertices. Use 1 subdivision, pixels for verts per subdivision.
     

    Attached Files:

    Last edited: May 16, 2016
    Hanowde and landon912 like this.
  15. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Awesome! Great stuff! I've been busy working on wrapping my head around this and also implementing the next big piece of this puzzle:

    Procedural Solar System Generation

    I've got a bunch of articles I've bought and books too that I've been using here's a link to the articles in my DropBox:
    https://www.dropbox.com/sh/ock9z0xqy6nswrd/AADTh3bApQZzxd4V0aAuiZlUa?dl=0 (let me know if you have issues snagging it)

    I've been trying to more or less convert (with little success) the C code for a program called Accrete (http://znark.com/create/accrete.html) and also (http://www.eldacur.com/~brons/NerdCorner/StarGen/StarGen.html). The problem is...C# has some quirks to it that make the conversion...problematic. One of the biggest problems I'm running into surrounds Structs and Pointers. Pointers it turns out are unsafe....as I understand it, C# doesn't let you go into the memory realm because of it's garbage collection functions, so if you want to go directly into memory, you have to declare that code as 'unsafe' and do some special things in unity to make it work. So what I did was essentially convert everything to classes. Here's an example of one of the classes I've converted:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Dust : MonoBehaviour
    5. {
    6.     private float innerEdge;
    7.     private float outerEdge;
    8.     private bool dustPresent;
    9.     private bool gasPresent;
    10.  
    11.     private Dust nextDustBand;
    12.  
    13.     //Helper functions
    14.     public float InnerEdge
    15.     {
    16.         get { return innerEdge; }
    17.         set { innerEdge = value; }
    18.     }
    19.  
    20.     public float OuterEdge
    21.     {
    22.         get { return outerEdge; }
    23.         set { outerEdge = value; }
    24.     }
    25.  
    26.     public bool DustPresent
    27.     {
    28.         get { return dustPresent; }
    29.         set { dustPresent = value; }
    30.     }
    31.  
    32.     public bool GasPresent
    33.     {
    34.         get { return gasPresent; }
    35.         set { gasPresent = value; }
    36.     }
    37.  
    38.     public Dust NextDustBand
    39.     {
    40.         get { return nextDustBand; }
    41.         set { nextDustBand = value; }
    42.     }
    43. }
    44.  
    and Here's the Original C version:

    Code (CSharp):
    1. typedef struct dust_record    *dust_pointer;
    2.  
    3.  
    4. typedef struct dust_record {
    5.     long double inner_edge;
    6.     long double outer_edge;
    7.     int         dust_present;
    8.     int         gas_present;
    9.     dust_pointer next_band;
    10.      } dust;
    Kind of a problem, considering the Pointer there. Where I run into an issue, if I understand the difference between the pass by reference and pass by value differences between a class and struct, is here:

    Code (CSharp):
    1.     Dust dustStart;
    2.     Planet planetStart;
    3.  
    4.     //1.
    5.     void SetInitialConditions(float innerDustLimit, float outerDustLimit)
    6.     {
    7.         dustStart = new Dust();
    8.         planetStart = new Planet();
    9.  
    10.         dustStart.InnerEdge = innerDustLimit;
    11.         dustStart.OuterEdge = outerDustLimit;
    12.         dustStart.DustPresent = true;
    13.         dustStart.GasPresent = true;
    14.         dustStart.NextDustBand = null;
    15.  
    16.         dustLeft = true;
    17.         dustCloudEccentricity = 0.2f;
    18.  
    19.         if (dustStart == null)
    20.         {
    21.             Debug.Log("dustStart is NULL");
    22.         }
    23.     }
    Duststart is ALWAYS null...which factors into other code down the line...Even though I create a new Dust object, and initilialize all the relevant bits, it returns NULL...which is bad. Because when I go to check later down the line if something is null:

    Code (CSharp):
    1.  //5. Calculate the Dust Available
    2.     bool IsDustAvailable(float innerEffectLimit, float outerEffectLimit)
    3.     {
    4.         Dust currentDustBand;
    5.         bool dustHere;
    6.  
    7.         currentDustBand = dustStart;
    8.         Debug.Log("Current Dust Band: " + currentDustBand);
    9.  
    10.         //So long as the current dust band isn't null AND the current dust bands outer radius is still less than the inner effect limit, then set the current dust
    11.         //band to the next dust band
    12.         while ((currentDustBand != null) && (currentDustBand.OuterEdge < innerEffectLimit))
    13.         {
    14.             Debug.Log("1");
    15.             currentDustBand = currentDustBand.NextDustBand;
    16.         }
    17.  
    18.         //If the current dust band is null, then there's no more dust...otherwise, there's still dust
    19.         if (currentDustBand == null)
    20.         {
    21.             Debug.Log("currentDustBand == null");
    22.             dustHere = false;
    23.         }
    24.         else
    25.         {
    26.             Debug.Log("currentDustBand != null");
    27.             dustHere = currentDustBand.DustPresent;
    28.         }
    29.  
    30.         while ((currentDustBand != null) && (currentDustBand.InnerEdge < outerEffectLimit))
    31.         {
    32.             Debug.Log("4");
    33.             dustHere = dustHere || currentDustBand.DustPresent;
    34.             currentDustBand = currentDustBand.NextDustBand;
    35.         }
    36.  
    37.         return dustHere;
    38.     }
    It always returns currentDustBand as NULL...even though I'm passing an initialized object...

    So...anyone have any ideas here? I'm slowly reading through everything I can on the subject...some of it makes sense, some of it doesn't. Any insights into what is going on would be awesome! I definitely want to use the Accrete bits as the core of my Planet Generation code, it's just a matter of getting it to work with C#. Which I know I'll eventually figure out.

    Oh!

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class GravityAttractor : MonoBehaviour
    5. {
    6.     public float gravity = -9.806f;
    7.  
    8.     public float gravityPerSecond;
    9.  
    10.     public void Attract(Transform body)
    11.     {
    12.         Vector3 targetDirection = (body.position - transform.position).normalized;
    13.  
    14.         Vector3 bodyUp = body.up;
    15.  
    16.         body.rotation = Quaternion.FromToRotation(bodyUp, targetDirection) * body.rotation;
    17.         body.GetComponent<Rigidbody>().AddForce(targetDirection * gravity, ForceMode.Acceleration);
    18.     }
    19. }
    20.  
    21.  
    22. using UnityEngine;
    23. using System.Collections;
    24.  
    25. [RequireComponent(typeof(Rigidbody))]
    26.  
    27. public class GravityBody : MonoBehaviour
    28. {
    29.     GravityAttractor planet;
    30.  
    31.     void Awake()
    32.     {
    33.         planet = GameObject.Find("Planet").GetComponent<GravityAttractor>();
    34.         GetComponent<Rigidbody>().useGravity = false;
    35.         GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeRotation;
    36.     }
    37.  
    38.     void FixedUpdate()
    39.     {
    40.         planet.Attract(transform);
    41.     }
    42.  
    43. }
    44.  
    So those two scripts, which I learned from a YouTube video applied to the planet that is generated work like a champ! You can walk around the planet using that script there. So...once the solar system generator is done, and the noise bits are properly working with noise, you've got a fully functioning basic framework for procedurally generating a solar system with semi-realistic planets and nice textures.

    Anyway, that's where I'm at right now. I'll definitely dig deeper into the 3D noise after I've got the planet generator solved!
     
    landon912 likes this.
  16. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    Woah, heavy stuff!! Was wondering how you were going, busy I guess! Wasn't sure if my last edit with image was annoying; am more than happy to give the code used (let me know), just assumed you wanted to wrap your head around it and work it out. I've also been looking into calculating mesh tangents and creating bumpmaps at runtime too as you may want them. The normal issue is still bugging me; the only way out is for each mesh to have a reference to its surrounding meshes, so the normals can be manually calculated at the seams. A cheat would be to make every edge normal just the vertex.normalized, but that could create some odd lighting and be just as noticeable.

    The struct and pointer thing is waaay beyond my scope, but everything you've converted looks ok. With your basic Dust class, you don't need to inherit Monobehaviour.

    In SetInitialConditions(), you assign NextDustBand = null, this is the null you are seeing. the class variable dustStart is initialized fine, and definitely exists.

    //5 line 15 you are setting currentDustBand = currentDustBand.NextDustBand; which is making currentDustBand null. If NextDustBand was populated with dust greater than innerEffectLimit, then it would return not null.
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4.  
    5. public class Dust
    6. {
    7.    private float innerEdge;
    8.    private float outerEdge;
    9.    private bool dustPresent;
    10.    private bool gasPresent;
    11.  
    12.    private Dust nextDustBand; // wow, can declare class variable WITHIN the class!
    13.  
    14.    //Helper functions
    15.    public float InnerEdge
    16.    {
    17.      get { return innerEdge; }
    18.      set { innerEdge = value; }
    19.    }
    20.  
    21.    public float OuterEdge
    22.    {
    23.      get { return outerEdge; }
    24.      set { outerEdge = value; }
    25.    }
    26.  
    27.    public bool DustPresent
    28.    {
    29.      get { return dustPresent; }
    30.      set { dustPresent = value; }
    31.    }
    32.  
    33.    public bool GasPresent
    34.    {
    35.      get { return gasPresent; }
    36.      set { gasPresent = value; }
    37.    }
    38.  
    39.    public Dust NextDustBand
    40.    {
    41.      get { return nextDustBand; }
    42.      set { nextDustBand = value; }
    43.    }
    44. }
    45.  
    46. // testing struct
    47. public struct DustStruct
    48. {
    49.    public float innerEdge;
    50.    public float outerEdge;
    51.    public bool dustPresent;
    52.    public bool gasPresent;
    53.  
    54.    //public DustStruct nextDustBand; // cannot declare struct variable within the struct :(
    55.    // error CS0523: Struct member `DustStruct.nextDustBand' of type `DustStruct' causes a cycle in the struct layout
    56. }
    57.  
    58.  
    59. public class SolarSystemGenTest : MonoBehaviour
    60. {
    61.  
    62.  
    63.    //   -------------------------------------------------------  Persistent Functions
    64.  
    65.  
    66.    void Start()
    67.    {
    68.      SetInitialConditions( 1f, 2f );
    69.    
    70.      dustStart.NextDustBand = AssignNextDust( 0.8f, 0.9f ); // test function to populate NextDustBand in startDust
    71.      dustStart.NextDustBand.NextDustBand = AssignNextDust( 5f, 6f ); // test function to populate NextDustBand in startDust.NextDustBand
    72.      // so now there are 3 Dusts : dustStart, dustStart.NextDustBand, and dustStart.NextDustBand.NextDustBand (my head hurts!)
    73.    
    74.      Debug.Log( "" );
    75.      Debug.Log( "** TEST 1 - inner is less than **" ); // returns currentDustBand != null
    76.      Debug.Log( IsDustAvailable( 3f, 4f ) );
    77.      Debug.Log( "" );
    78.    
    79.      Debug.Log( "" );
    80.      Debug.Log( "** TEST 2 - inner is greater than **" ); // returns currentDustBand == null
    81.      Debug.Log( IsDustAvailable( 8f, 9f ) );
    82.      Debug.Log( "" );
    83.    }
    84.  
    85.  
    86.    //   -------------------------------------------------------  Test Functions
    87.  
    88.  
    89.    Dust AssignNextDust( float innerEdge, float outerEdge )
    90.    {
    91.      Dust nextDust = new Dust();
    92.    
    93.      nextDust.InnerEdge = innerEdge;
    94.      nextDust.OuterEdge = outerEdge;
    95.      nextDust.DustPresent = false;
    96.      nextDust.GasPresent = false;
    97.      nextDust.NextDustBand = null;
    98.    
    99.      return nextDust;
    100.    }
    101.  
    102.  
    103.    //   -------------------------------------------------------  Forum Functions
    104.  
    105.  
    106.    Dust dustStart;
    107.    //Planet planetStart;
    108.  
    109.    //1.
    110.    void SetInitialConditions(float innerDustLimit, float outerDustLimit)
    111.    {
    112.      dustStart = new Dust();
    113.      //planetStart = new Planet();
    114.    
    115.      dustStart.InnerEdge = innerDustLimit;
    116.      dustStart.OuterEdge = outerDustLimit;
    117.      dustStart.DustPresent = true;
    118.      dustStart.GasPresent = true;
    119.      dustStart.NextDustBand = null;
    120.    
    121.      //dustLeft = true;
    122.      //dustCloudEccentricity = 0.2f;
    123.    
    124.      //if (dustStart == null) Debug.Log("dustStart is NULL");
    125.      Debug.Log( dustStart == null ? "dustStart is NULL" : "dustStart is populated" );
    126.    }
    127.  
    128.  
    129.    //5. Calculate the Dust Available
    130.    bool IsDustAvailable(float innerEffectLimit, float outerEffectLimit)
    131.    {
    132.      Dust currentDustBand;
    133.      bool dustHere;
    134.    
    135.      currentDustBand = dustStart;
    136.      Debug.Log("Current Dust Band: " + currentDustBand);
    137.    
    138.      //So long as the current dust band isn't null AND the current dust bands outer radius is still less than the inner effect limit, then set the current dust
    139.      //band to the next dust band
    140.      while ((currentDustBand != null) && (currentDustBand.OuterEdge < innerEffectLimit))
    141.      {
    142.        // debug the current dust variables
    143.        Debug.Log(" ---- ");
    144.        Debug.Log("Dust InnerEdge: "  + currentDustBand.InnerEdge);
    145.        Debug.Log("Dust OuterEdge: "  + currentDustBand.OuterEdge);
    146.        Debug.Log("Dust DustPresent: "  + currentDustBand.DustPresent);
    147.        Debug.Log("Dust GasPresent: "  + currentDustBand.GasPresent);
    148.        Debug.Log("Dust NextDustBand: " + ( currentDustBand.NextDustBand == null ? "NULL" : "Populated" ) );
    149.        Debug.Log(" ---- ");
    150.      
    151.        Debug.Log("1");
    152.        currentDustBand = currentDustBand.NextDustBand;
    153.      }
    154.    
    155.      //If the current dust band is null, then there's no more dust...otherwise, there's still dust
    156.      if (currentDustBand == null)
    157.      {
    158.        Debug.Log("currentDustBand == null");
    159.        dustHere = false;
    160.      }
    161.      else
    162.      {
    163.        Debug.Log("currentDustBand != null");
    164.        dustHere = currentDustBand.DustPresent;
    165.      }
    166.    
    167.      while ((currentDustBand != null) && (currentDustBand.InnerEdge < outerEffectLimit))
    168.      {
    169.        Debug.Log("4");
    170.        dustHere = dustHere || currentDustBand.DustPresent;
    171.        currentDustBand = currentDustBand.NextDustBand;
    172.      }
    173.    
    174.      return dustHere;
    175.    }
    176. }

    If you can decipher all that debug output(!) the while recursion loop
    does work when NextDustBand != null and NextDustBand.OuterEdge is < innerEffectLimit ,
    but not when NextDustBand != null and NextDustBand.OuterEdge is > innerEffectLimit or NextDustBand == null .
    TL;DR: your code works :D

    That was just a quick look and test. Hope some of that made sense... I havn't looked hard at the ACCRETE.C source yet, but it seems all the Dusts have to be declared first, and also assigned to respective nextDust variables. Then you should see some results from the recursion.
     
    landon912 likes this.
  17. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Alrighty...So this rig is sort of working now. It won't go beyond injecting 2-3 nuclei...but it's a start! Here's the original version in C++ and also my conversion efforts. I need a second set of eyes and definitely someone a bit smarter on the nuances of C# than Myself. I've been on a crash course of learning in adapting the existing code over. I suspect it's a disconnect with the events and coalescence code...I'm getting output for 9 events, which would imply a LOT more nuclei are being injected...I guess it would probably be more helpful to actually have some kind of output to visually verify things too...

    I know too that the differences in the number generator are probably causing issues. The test run with a seed of 41 (on an IBM 7044 pc) using the code (which was just a translation from Fortran to C++..and really, Fortran to C to C++), should have reported 17 events, and injected 59 nuclei...The normal ranges for nuclei injection sits around 40 to 500, with the average being less than 150. So there's definitely something not right in my implementation as it sits right now...I think the biggest issue is that I'm not getting any Gas Giants...whereas I was prior to the code in lines 433 through...481-ish...

    So actually, I just commented out the section that I don't think is working. This immediately led to the formation of gas giants...nuclei injection is still super low...Yup. I need an extra set of eyes.

    Anyway, code!

    The Original:
    Code (CSharp):
    1. /*%CC acrete.c -o acrete -lm
    2. *
    3. * This will also work with g++ 1.40.3: g++ acrete.c -o acrete -lm
    4. *
    5. * Dole accretion model for planetary formation.
    6. * Adapted from Wales Larrison's BASIC code.
    7. * References:
    8. *
    9. *  Dole, S.H. "Formation of Planetary Systems by Aggregation:
    10. *        a Computer Simulation" Icarus, vol 13, p 494, 1970
    11. *  Isaacman, R. & Sagan, C. "Computer Simulation of Planetary
    12. *        Accretion Dynamics: Sensitivity to Initial Conditions"
    13. *        Icarus, vol 31, p 510, 1977
    14. *
    15. * Usage:
    16. *  acrete [-seed #] [-dump]
    17. *
    18. *    -seed specifies initial value to random number generator (uses time otherwise)
    19. *    -dump causes a dump of the generated system on stdout
    20. *    Produces a PostScript file "planets.ps"
    21. *
    22. * Jon Leech (leech@cs.unc.edu)
    23. */
    24. #include <stream.h>
    25. #include <string.h>
    26. #include <math.h>
    27. #include <stdlib.h>
    28. // Should include <rand48.h>, but this isn't on the Ultrix distribution
    29. extern "C" {
    30.     double  drand48();
    31.     long    lrand48();
    32.     void    srand48(long);
    33. };
    34.  
    35. // G++ *still* doesn't have an iostream library, 3 years behind the rest
    36. //  of the world.
    37. #ifdef __GNUG__
    38.  
    39. // ostream << void *
    40. inline ostream &operator<< (ostream &o, void *p) {
    41.     return o << "0x" << hex(long(p));
    42. }
    43.  
    44. // Manipulators 'endl' and 'flush'
    45. inline ostream &operator<<(ostream &o, ostream &(*manip)(ostream &)) {
    46.     return (*manip)(o);
    47. }
    48.  
    49. inline ostream &endl(ostream &o) { o << '\n'; return o.flush(); }
    50. inline ostream &flush(ostream &o) { return o.flush(); }
    51.  
    52. typedef long streamoff;
    53.  
    54. // ofstream w/file name constructor and seekp() method
    55. struct ofstream : public ostream {
    56.     ofstream(const char *file) : ostream(new Filebuf(file, io_writeonly, a_create)) { }
    57. };
    58.  
    59. #endif /*__GNUG__*/
    60.  
    61. // Simulation parameters
    62. struct DoleParams {
    63.     double
    64.     A,
    65.     B,
    66.     Alpha,
    67.     Beta,
    68.     Eccentricity,
    69.     Gamma,
    70.     K,
    71.     MassSol,
    72.     MassSun,
    73.     MassNuclei,
    74.     W;
    75.  
    76.     DoleParams();
    77.     double density(double au);
    78.     double gasdensity(double au, double massratio);
    79.     double masscritical(double au);
    80.     double lowbound(double radius, double margin);
    81.     double highbound(double radius, double margin);
    82. };
    83.  
    84. // Initialize to defaults. See Sagan's article for insight into changing them.
    85. DoleParams::DoleParams() {
    86.     A = .00150;
    87.     Alpha = 5;
    88.     Beta = 0.5;
    89.     Eccentricity = 0.15;
    90.     Gamma = 1 / 3.0;
    91.     K = 50;
    92.     MassSol = 1;
    93.     MassSun = MassSol * 1.0;
    94.     MassNuclei = MassSun * 1e-15;
    95.     B = MassSun * 1.2e-5;
    96.     W = .2;
    97. }
    98.  
    99. // Return disk density at orbital distance au
    100. double DoleParams::density(double au) {
    101.     return A * exp(-Alpha * pow(au, Gamma));
    102. }
    103.  
    104. // Returns dust+gas density swept at orbital distance AU by a body of given
    105. //  ratio (critical mass / mass).
    106. double DoleParams::gasdensity(double au, double massratio) {
    107.     return (K * density(au)) / (1 + sqrt(massratio) * (K-1));
    108. }
    109.  
    110. // Return critical mass to form a gas giant at orbital distance au
    111. double DoleParams::masscritical(double au) {
    112.     return B * pow(au, -.75);
    113. }
    114.  
    115. // I don't know what these functions really *represent*, but they're
    116. //  repeatedly used.
    117. double DoleParams::lowbound(double radius, double margin) {
    118.     return radius - margin - W * (radius - margin) / (1 + W);
    119. }
    120.  
    121. double DoleParams::highbound(double radius, double margin) {
    122.     return radius + margin + W * (radius + margin) / (1 - W);
    123. }
    124.  
    125. const double epsilon = .00001;
    126. const int Maxint = 32767;
    127.  
    128. int imin(int a, int b = Maxint, int c = Maxint) {
    129.     if (a < b)
    130.     return (a < c) ? a : c;
    131.     else
    132.     return (b < c) ? b : c;
    133. }
    134.  
    135. int imax(int a, int b = -Maxint, int c = -Maxint) {
    136.     if (a > b)
    137.     return (a > c) ? a : c;
    138.     else
    139.     return (b > c) ? b : c;
    140. }
    141.  
    142. ofstream *Ps;
    143. int PsPage = 1;
    144. double
    145.     PsBase = 50,            // base offset from lower left corner
    146.     PsXscale = 1, PsYscale = 1,
    147.     PsXoff = 0, PsYoff = 0;
    148.  
    149. void ps_end() {
    150.     *Ps << "%%Trailer\nend" << flush;
    151.     delete Ps;
    152.  
    153.     Ps = NULL;
    154. }
    155.  
    156. void ps_beginpage(int page) {
    157.     *Ps << "%%Page: " << page << ' ' << page << '\n'
    158.     << PsXoff+PsBase << ' ' << PsYoff+PsBase << " translate\n"
    159.     << PsXscale << ' ' << PsYscale << ' ' << " scale\n"
    160.     << "/Helvetica findfont " << 9 / PsXscale << " scalefont setfont\n"
    161.     << "0 setlinewidth\n";
    162. }
    163.  
    164. void ps_begin(const char *file) {
    165.     if (Ps != NULL) {
    166.     ps_end();
    167.     }
    168.  
    169.     Ps = new ofstream(file);
    170.     *Ps << "%!PS-Adobe-2.1\n"
    171.     << "%%Pages: 3\n"
    172.     << "%%EndComments\n"
    173.     << "/Helvetica findfont 12 scalefont setfont\n"
    174.     << "0 setlinewidth\n"
    175.     << "newpath\n"
    176.     << "%%EndProlog\n";
    177.  
    178.     ps_beginpage(PsPage++);
    179. }
    180.  
    181. void ps_showpage() {
    182.     *Ps << "showpage\n";
    183.     ps_beginpage(PsPage++);
    184. }
    185.  
    186. void ps_window(double x1, double y1, double x2, double y2)
    187. {
    188.     const double width = 450;
    189.     double
    190.     xspan = x2 - x1, yspan = y2 - y1;
    191.  
    192.     PsXscale = width / xspan;
    193.     PsYscale = PsXscale;        // could be width / yspan
    194.     PsXoff   = -PsXscale * x1;
    195.     PsYoff   = -PsYscale * y1;
    196. }
    197.  
    198. void ps_circle(double x, double y, double radius, int fill)
    199. {
    200.     *Ps << x << ' ' << y << ' ' << radius << " 0 360 arc ";
    201.     *Ps << (fill ? "fill" : "stroke") << endl;
    202. }
    203.  
    204. void randomize(long seed)
    205. {
    206.     srand48(seed);
    207. }
    208.  
    209. double rand01()
    210. {
    211.     return drand48();
    212. }
    213. #ifdef sun
    214. double        sqr(double x)
    215. {
    216.     return x * x;
    217. }
    218. #endif
    219.  
    220. // Return closest integer to arg
    221. int cint(double arg)
    222. {
    223.     return int(floor(arg+0.5));
    224. }
    225.  
    226. void ps_pset(double x, double y)
    227. {
    228.     ps_circle(x, y, 0.01, 0);
    229. }
    230.  
    231. void ps_line(double x1, double y1, double x2, double y2)
    232. {
    233.     *Ps << x1 << ' ' << y1 << " moveto "
    234.     << x2 << ' ' << y2 << " lineto stroke\n";
    235. }
    236.  
    237. void ps_text(double x, double y, const char *s) {
    238.     *Ps << x << ' ' << y << " moveto (" << s << ") show newpath\n";
    239. }
    240.  
    241. // Draw scale on figure
    242. void logscale(const char *xlabel = "", const char *ylabel = "") {
    243.     ps_line(-1, -1,  3, -1);
    244.     ps_line( 3, -1,  3,  1);
    245.     ps_line( 3,  1,  3, -1);
    246.     ps_line( 3, -1, -1, -1);
    247.  
    248.     ps_line(-1, 1, 3, 1);
    249.     for (double au = 1; au <= 10 + epsilon; au += 1)  {
    250.     ps_line(log10(au/10), 1, log10(au/10), .95);
    251.     ps_line(log10(au), 1, log10(au), .95);
    252.     ps_line(log10(au*10), 1, log10(au*10), .95);
    253.     }
    254.  
    255.     ps_text(-1, 1, ".1");
    256.     ps_text( 0, 1, "1");
    257.     ps_text( 1, 1, "10");
    258.     ps_text( 2, 1, "100");
    259.  
    260.     ps_text(2.3, 1, xlabel);
    261.     ps_text(-1, .9, ylabel);
    262. }
    263.  
    264. struct Nucleus {
    265.     double
    266.     axis,        // semimajor axis of the nuclei orbit
    267.     eccen,        // eccentricity of the nuclei orbit
    268.     mass,        // mass of the nuclei
    269.     pRad,        // orbital radius at perigee
    270.     aRad,        // orbital radius at apogee
    271.     pAttr,        // grav attract dist at perigee
    272.     aAttr;        // grav attract dist at apogee
    273.     enum {
    274.     Rock, GasGiant
    275.     }    type;        // type of planet
    276.          
    277.     Nucleus();
    278.     void dump(int, ostream &, DoleParams *);
    279.     friend int nucleusCompare(void *, void *);
    280.     double lowbound(DoleParams *params);
    281.     double highbound(DoleParams *params);
    282. };
    283.          
    284. Nucleus::Nucleus() {
    285.     axis = eccen = mass = 0;
    286.     pRad = aRad = 0;
    287.     pAttr = aAttr = 0;
    288.     type = Rock;
    289. }
    290.  
    291. double Nucleus::lowbound(DoleParams *params) {
    292.     return params->lowbound(pRad, pAttr);
    293. }
    294.  
    295. double Nucleus::highbound(DoleParams *params) {
    296.     return params->highbound(aRad, aAttr);
    297. }
    298.  
    299. // Comparison function used by qsort()
    300. #ifdef __GNUG__
    301. int nucleusCompare(void *p1, void *p2) {    // g++ is broken again
    302. #else
    303. int nucleusCompare(const void *p1, const void *p2) {
    304. #endif
    305.     double  r1 = ((const Nucleus *)p1)->axis,
    306.         r2 = ((const Nucleus *)p2)->axis;
    307.     return (r1 < r2) ? -1 : (r1 > r2) ? 1 : 0;
    308. }
    309.  
    310. // Dump nucleus stats to specified stream
    311. void Nucleus::dump(int num, ostream &o, DoleParams *params) {
    312.     double
    313.     xplimit = pRad - pAttr,
    314.     xalimit = aRad + aAttr,
    315.     lowrange = lowbound(params),
    316.     highrange = highbound(params),
    317.     massCrit = params->masscritical(pRad);
    318.  
    319.     o << "Nucleus " << num << '\n';
    320.     o << "\tRadius\t\t" << axis << '\n';
    321.     o << "\tEccentricity\t" << eccen << '\n';
    322.     o << "\tMass\t\t" << mass << '\n';
    323.     o << "\tType\t\t" << ((type == GasGiant) ? "Gas giant" : "Rocky") << '\n';
    324.  
    325.     o << "\tRange          = [" << lowrange << "," << highrange << "]\n";
    326.     o << "\tX[pa]limit     = [" << xplimit << "," << xalimit << "]\n";
    327.     o << "\tX{peri,apo}gee = [" << pAttr << "," << aAttr << "]\n";
    328.     o << "\tR{peri,apo}gee = [" << pRad << "," << aRad << "]\n";
    329.  
    330.     o << "\tCritical Mass  = " << massCrit << ' '
    331.       << '(' << mass / massCrit << "% of Critical)\n";
    332. }
    333.  
    334. main(int ac, char *av[])
    335. {
    336.     int dumpflag = 0, nplanet = 0;
    337.     long seed = time(0);
    338.  
    339.     for (int i = 0; i < ac; i++) {
    340.     if (!strcmp(av[i], "-dump"))
    341.         dumpflag = 1;
    342.     else if (!strcmp(av[i], "-seed"))
    343.         seed = atoi(av[++i]);
    344.     }
    345.  
    346.     double  nucleieccent, nucleiradius, masslast, eccent, mass, rperigee,
    347.         rapogee, radius, mu, xperigee, xapogee, masscritical,
    348.         density, bw, volume, da, dp, masspass;
    349.  
    350.     int     n, lowband, highband, dustcheck, iterate, hit;
    351.  
    352.     // Bands are measured in 1/5 au intervals from 0 to 50 AU
    353.     const int
    354.     MaxBand = 50 * 5,
    355.     MaxNuclei = 1000;   // Max # of nuclei to inject
    356.  
    357.     enum { Mixed, Gas, Empty }
    358.         band[MaxBand+1];    // Contents of band
    359.  
    360.     int MaxPlanets = 20;    // Size of dynamic array containing generated planets
    361.     Nucleus *nuclei = new Nucleus[MaxPlanets];
    362.     DoleParams *params = new DoleParams;
    363.  
    364.     randomize(seed);
    365.  
    366.     ps_window(-1, -1, 2, 1);
    367.     ps_begin("planets.ps");
    368.  
    369.     // Set up primordial cloud
    370.     // Dust within .3 au is swept out by radiation pressure
    371.  
    372.     for (n = 1; n <= MaxBand; n++)
    373.     band[n] = Mixed;
    374.  
    375.     // Draw scale on figure
    376.     logscale("AU");
    377.  
    378.     // Plot density function; space samples even on log scale
    379.     double max = 1.1 * params->density(0.3), lastau, lastrho,
    380.        logaumin = log10(0.3),
    381.        logaumax = log10(50),
    382.        logaustep = (logaumax - logaumin) / 100;
    383.  
    384.     for (double logau = logaumin; logau <= logaumax; logau += logaustep) {
    385.     double rho = params->density(pow(10,logau)) / max;
    386.  
    387.     if (logau > logaumin)
    388.         ps_line(lastau, lastrho, logau, rho);
    389.     else {
    390.         char buf[100];
    391.         sprintf(buf, "density [max = %g]", max);
    392.         ps_text(logau, rho, buf);
    393.     }
    394.  
    395.     lastau = logau;
    396.     lastrho = rho;
    397.     }
    398.  
    399.     double yBand = 0.9;
    400.     // Inject nuclei into the cloud and acrete gas and dust
    401.     for (n = 0; n < MaxNuclei; n++) {
    402.     // Check to see if all bands are taken
    403.     int bandfull = 0;
    404.     for (int i = 1; i <= MaxBand; i++) {
    405.         if (band[i] == Mixed)  {
    406.         bandfull = 1;
    407.         break;
    408.         }
    409.     }
    410.  
    411.     if (bandfull == 0)
    412.         break;
    413.  
    414.     nucleieccent = 1 - pow(1 - rand01(), .077);
    415.     while ((nucleiradius = rand01() * 50) < .3)
    416.         ;
    417.  
    418.     Nucleus body;
    419.     body.axis = nucleiradius;
    420.     body.eccen = nucleieccent;
    421.     body.mass = params->MassNuclei;
    422.  
    423.     // This is only used on first pass of nuclei
    424.     masslast = 0;
    425.     iterate = 0;
    426.  
    427.     // Mass accumulation loop - continue until minimal accumulation
    428.     while (1) {
    429.         radius = body.axis;
    430.         eccent = body.eccen;
    431.         mass = body.mass;
    432.         body.pRad = rperigee = nucleiradius * (1 - nucleieccent);
    433.         body.aRad = rapogee = nucleiradius * (1 + nucleieccent);
    434.         if (mass == 0)
    435.         mass = 1e-15;
    436.         mu = pow(mass / (1 + mass), .25);
    437.         body.pAttr = xperigee = rperigee * mu;
    438.         body.aAttr = xapogee = rapogee * mu;
    439.      
    440.         // Nuclei sweeps up band
    441.         // Establish bounds on swept volume
    442.         lowband = cint(body.lowbound(params) * 5);
    443.         if (lowband < 1)
    444.         lowband = 1;
    445.      
    446.         highband = cint(body.highbound(params) * 5);
    447.         if (highband < 1)
    448.         highband = 1;
    449.         else if (highband > MaxBand)
    450.         highband = MaxBand;
    451.  
    452.         if (lowband == highband)
    453.         highband++;
    454.  
    455.         // calculate critical mass limits
    456.         masscritical = params->masscritical(rperigee);
    457.      
    458.         /* check for bands with dust within range */
    459.         if (iterate == 0) {
    460.         dustcheck = 0;
    461.      
    462.         for (int bandno = lowband; bandno <= highband; bandno++) {
    463.             if (masslast > 0 || band[bandno] == Mixed) {
    464.             dustcheck = 1;
    465.             break;
    466.             }
    467.         }
    468.      
    469.         // If no bands have dust in them (band[bandno] == Mixed), then dud
    470.         // dustcheck == 1 means dust is in some band
    471.      
    472.         if (dustcheck == 0) {
    473.             // cout << "no dust; " << n << "is a dud" << endl;
    474.             if (masslast == 0) {
    475.             body.axis = 0;
    476.             body.eccen = 0;
    477.             body.mass = 0;
    478.             }
    479.             break;  // exit mass accumulation loop
    480.         }
    481.         }
    482.      
    483.         // Calculate mass using Dole donut approximation
    484.         if (mass == 0)
    485.         mass = 1e-15;
    486.      
    487.         if (mass < masscritical)
    488.         density = params->density(body.axis);
    489.         else {
    490.         // Sweeps dust and gas
    491.         density = params->gasdensity(body.axis, masscritical / mass);
    492.         }
    493.  
    494.         bw = 2 * body.axis * body.eccen +
    495.          xapogee + xperigee +
    496.          params->W * (rapogee + xapogee) / (1 - params->W) +
    497.          params->W * (rperigee - xperigee) / (1 + params->W);
    498.      
    499.         volume = 2 * M_PI * body.axis * bw * (xapogee + xperigee);
    500.      
    501.         double sweepmass = volume * density;    // unused
    502.      
    503.         dp = body.lowbound(params);
    504.         da = body.highbound(params);
    505.      
    506.         lowband = cint(dp * 5);
    507.         highband = cint(da * 5);
    508.      
    509.         if (lowband == highband)
    510.         highband++;
    511.         if (highband > MaxBand)
    512.         highband = MaxBand;
    513.         masspass = 0;
    514.      
    515.         for (int bandno = lowband; bandno <= highband; bandno++) {
    516.         double
    517.             au = bandno / 5.0,
    518.             xpnow, xanow,
    519.             bandvol, bandvol2;
    520.  
    521.         // Calculate mass of wedge of doughnut
    522.         xpnow = xperigee + (xapogee - xperigee) *
    523.             (bandno - lowband) / (double)(highband - lowband);
    524.         xanow = xperigee + (xapogee - xperigee) *
    525.             (bandno + 1 - lowband) / (double)(highband - lowband);
    526.      
    527.         bandvol = 2 * M_PI * au * 0.2 * (xpnow + xanow);
    528.      
    529.         for (i = 0; i < nplanet; i++) {
    530.             double
    531.             dp2 = nuclei[i].lowbound(params),
    532.             da2 = nuclei[i].highbound(params);
    533.      
    534.             if (da2 < au || dp2 > au + 0.2)
    535.             continue;
    536.      
    537.             // Overlap exists, find bounds on overlap volume
    538.             double
    539.             bw2 = 2 * nuclei[i].axis * nuclei[i].eccen +
    540.               nuclei[i].pAttr +
    541.               nuclei[i].aAttr +
    542.               params->W * (nuclei[i].aRad + nuclei[i].aAttr) / (1 - params->W) +
    543.               params->W * (nuclei[i].pRad - nuclei[i].pAttr) / (1 + params->W);
    544.      
    545.             // At au, overlap has xp2now and xa2now as heights
    546.             double xp2now = nuclei[i].pAttr +
    547.                  (nuclei[i].aAttr - nuclei[i].pAttr) *
    548.                   (au - dp2) / (da2 - dp2);
    549.             double xa2now = nuclei[i].pAttr +
    550.                  (nuclei[i].aAttr - nuclei[i].pAttr) *
    551.                   (au + 0.2 - dp2) / (da2 - dp2);
    552.             bandvol2 = 2 * M_PI * au * 0.2 * (xp2now + xa2now);
    553.      
    554.             // If previously swept band larger than this swept, no dust
    555.             //    swept up.
    556.      
    557.             if (bandvol2 >= bandvol)
    558.             bandvol = 0;
    559.             else
    560.             bandvol -= bandvol2;
    561.             break;
    562.         }
    563.      
    564.         masspass += bandvol * density;
    565.         }
    566.      
    567.         body.mass = mass = masspass;
    568.      
    569.         // cout << mass << ' ' << n << endl;
    570.  
    571.         // check for mass growth convergence
    572.         if (mass == 0)
    573.         mass = 1e-15;
    574.         if (mass >= masscritical) {
    575.         body.type = Nucleus::GasGiant;
    576.         }
    577.         if (fabs(masslast / mass - 1) < 0.01)
    578.         break;
    579.         masslast = mass;
    580.      
    581.         iterate = 1;
    582.     }   // end mass accumulation loop
    583.  
    584.     // Clear out bands emptied of dust
    585.     if (dustcheck == 1) {
    586.         cout << "event " << n << " completed mass growth; swept from "
    587.            << dp << " to " << da;
    588.         if (mass > masscritical)
    589.         cout << "(gas giant)";
    590.         cout << endl;
    591.     }
    592.     if (lowband == highband)
    593.         highband++;
    594.  
    595.     for (int bandno = lowband; bandno <= highband; bandno++) {
    596.         if (mass < masscritical) {
    597.         if (band[bandno] == Mixed)
    598.             band[bandno] = Gas;
    599.         }
    600.  
    601.         if (mass >= masscritical) {
    602.         // Clear out bands emptied by gas
    603.         body.type = Nucleus::GasGiant;
    604.         if (band[bandno] == Gas || band[bandno] == Mixed)
    605.             band[bandno] = Empty;
    606.         }
    607.     }
    608.  
    609.     // cout << "check nucleus " << n << " for overlap and coalescence\n";
    610.  
    611.     // check for orbital and  gravitational overlap and coalescence
    612.     // cout << body.pRad - body.pAttr
    613.     //    << body.aRad+body.aAttr << endl;
    614.  
    615.     if (body.axis == 0)
    616.         continue;
    617.  
    618.     hit = 0;
    619.     for (i = 0; i < nplanet; i++) {
    620.         double  newradius, newmass, neweccent, term1, term2, term3, munew;
    621.  
    622.         // cout << n << '\t' << i << '\t'
    623.         //        << nuclei[i].aRad << '\t' << rperigee-xperigee << '\t'
    624.         //        << nuclei[i].pRad << '\t' << rapogee+xapogee << endl;
    625.  
    626.         if ((nuclei[i].aRad < (rperigee - xperigee) &&
    627.          (nuclei[i].aRad + nuclei[i].aAttr) < rperigee) ||
    628.         ((rapogee + xapogee) < nuclei[i].pRad &&
    629.          rapogee < (nuclei[i].pRad - nuclei[i].pAttr)))
    630.         continue;
    631.  
    632.         cout << "coalesence of nuclei  " << n << ' ' << i << endl;
    633.  
    634.         hit = 1;
    635.         newradius = (body.mass + nuclei[i].mass) /
    636.             (body.mass / body.axis + nuclei[i].mass / nuclei[i].axis);
    637.  
    638.         term1 = body.mass * sqrt(body.axis) * sqrt(1 - pow(body.eccen, 2));
    639.         term2 = nuclei[i].mass * sqrt(nuclei[i].axis) *
    640.                      sqrt(1 - pow(nuclei[i].eccen, 2));
    641.         term3 = (body.mass + nuclei[i].mass) * sqrt(newradius);
    642.  
    643.         neweccent = sqr(abs(1 - pow((term1 + term2) / term3, 2)));
    644.         newmass = body.mass + nuclei[i].mass;
    645.  
    646.         // cerr << "Nuking body " << i << endl;
    647.         nuclei[i].axis = 0;
    648.         nuclei[i].eccen = 0;
    649.         nuclei[i].mass = 0;
    650.         body.axis = newradius;
    651.         body.eccen = neweccent;
    652.         body.mass = newmass;
    653.         body.pRad = newradius * (1 - neweccent);
    654.         body.aRad = newradius * (1 + neweccent);
    655.         munew = pow(newmass / (1 + newmass), .25);
    656.         body.pAttr = body.pRad * munew;
    657.         body.aAttr = body.aRad * munew;
    658.  
    659.         //    cout << "new mass = " << newmass << " new radius = " << newradius
    660.         //         << " new eccentricity = " << neweccent << endl;
    661.     }
    662.  
    663.     mass = body.mass;
    664.  
    665.     // Show depletion of bands
    666.     int
    667.         lowband2 = cint(body.lowbound(params) * 5),
    668.         highband2 = cint(body.highbound(params) * 5);
    669.  
    670.     lowband2 = imax(lowband2, 6, lowband);
    671.     highband2 = imax(imin(highband2, MaxBand, highband), 6);
    672.     if (lowband2 == highband2)
    673.         highband2++;
    674.  
    675.     ps_line(log10(lowband2 * 0.2), yBand, log10(highband2 * 0.2), yBand);
    676.     yBand -= 0.01;
    677.  
    678.     // iterate for mass captured
    679.     //  cout << "Start of iteration for mass" << N
    680.     //     << " mass = " << mass
    681.     //     << " masslast = " << masslast << endl;
    682.  
    683.     if (body.mass >= masscritical)
    684.         body.type = Nucleus::GasGiant;
    685.  
    686.     // Add new planet
    687.     if (nplanet >= MaxPlanets) {
    688.         Nucleus *newplanet = new Nucleus[MaxPlanets*2];
    689.  
    690.         // Copy old planets to new
    691.         for (int i = 0; i < nplanet; i++)
    692.         newplanet[i] = nuclei[i];
    693.      
    694. #ifdef __GNUG__
    695.         // Guess what? G++ doesn't implement this right either.
    696.         delete [MaxPlanets] nuclei;
    697. #else
    698.         delete [] nuclei;        // Get rid of old planets
    699. #endif
    700.         nuclei = newplanet;     // Use new planet array
    701.         MaxPlanets *= 2;        // New array size
    702.     }
    703.     nuclei[nplanet++] = body;
    704.  
    705.     // Sweep planet array, removing merged bodies
    706.     for (i = 0; i < nplanet; i++) {
    707.         if (nuclei[i].axis == 0) {
    708.         //for (int j = i+1; j < nplanet; j++)
    709.         //    nuclei[j-1] = nuclei[j];
    710.         //nplanet--;
    711. cerr << "Nuking body " << i << "; " << nplanet << " remaining" << endl;
    712.           nuclei[i] = nuclei[nplanet-1];
    713.           nuclei[nplanet-1].axis = 0;
    714.           nplanet--;
    715.         }
    716.     }
    717.     }
    718.  
    719.     cout << " all bands taken....." << n << "   nuclei used     " << endl;
    720.     ps_text(2.2, 0.9, "emptied bands");
    721.  
    722.     // Sort nuclei by radius
    723.     qsort((void *)nuclei, nplanet, sizeof(Nucleus), &nucleusCompare);
    724.  
    725.     // Draw planets, gas giants as filled circles
    726.     double massSum = 0;
    727.     for (i = 0; i < nplanet; i++) {
    728.     massSum += nuclei[i].mass;
    729.  
    730.     double au = log10(nuclei[i].axis), r = pow(nuclei[i].mass, 1/3.0);
    731.  
    732.     ps_circle(au, 0, r, nuclei[i].type == Nucleus::GasGiant);
    733.     }
    734.     ps_showpage();
    735.  
    736.     cout << "Total mass = " << massSum << endl;
    737.  
    738.     if (dumpflag) {
    739.     cout << "Random number seed =" << seed << endl;
    740.  
    741.     for (i = 0; i < nplanet; i++) {
    742.         if (nuclei[i].axis > 0)
    743.         nuclei[i].dump(i, cout, params);
    744.     }
    745.  
    746.     cout << "Bands with dust still in them:\n";
    747.     for (i = 1; i <= MaxBand; i++)
    748.         if (band[i] == Mixed)
    749.         cout << i << ' ';
    750.     cout << endl;
    751.     }
    752.  
    753.     ps_end();
    754.  
    755.     return (0);
    756. }
    757.  

    My Conversion:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public enum BANDCONTENTS { MIXED, GAS, EMPTY }
    6.  
    7. public class CONSTANTS
    8. {
    9.     public const float  Q           = 0.077f,
    10.                         NUCLEIMASS  = 1E-15f,
    11.                         EPSILON     = 0.00001f;
    12.  
    13.     public const int    MAXBAND     = 50 * 5,
    14.                         MAXNUCLEI   = 1000,
    15.                         MAXPLANETS  = 20,
    16.                         MAXINT      = 32767;
    17. }
    18.  
    19. public class DoleParams
    20. {
    21.     public float    A,
    22.                     B,
    23.                     alpha,
    24.                     beta,
    25.                     e,      //eccentricity
    26.                     G,      //gamma
    27.                     K,
    28.                     mS,     //mass of the sun
    29.                     mN,     //mass of the nucleus
    30.                     W;
    31.  
    32.     public DoleParams(float solarMass)
    33.     {
    34.         //Defaults...
    35.         A       = 0.0015f;
    36.         alpha   = 5.0f;
    37.         beta    = 0.5f;
    38.         e       = 0.15f;
    39.         G       = 1.0f / 3.0f;
    40.         K       = 50.0f;
    41.         mS      = solarMass;
    42.         mN      = mS * CONSTANTS.NUCLEIMASS;
    43.         B       = mS * 1.2E-5f;
    44.         W       = 0.2f;
    45.     }
    46.  
    47.     public float LowBound(float rP, float aP)
    48.     {
    49.         return rP - aP - W * (rP - aP) / (1 + W);
    50.     }
    51.  
    52.     public float HighBound(float rA, float aA)
    53.     {
    54.         return rA + aA + W * (rA + aA) / (1 - W);
    55.     }
    56.  
    57.     public float CriticalMass(float au)
    58.     {
    59.         return B * Mathf.Pow(au, -0.75f);
    60.     }
    61.  
    62.     public float Density(float au)
    63.     {
    64.         return A * Mathf.Exp(-alpha * Mathf.Pow(au, G));
    65.     }
    66.  
    67.     public float GasDensity(float au, float massRatio)
    68.     {
    69.         return (K * Density(au)) / (1 + Mathf.Sqrt(massRatio) * (K - 1));
    70.     }
    71. }
    72.  
    73. public class Nucleus
    74. {
    75.     public float    a,  //semi-major axis
    76.                     e,  //eccentricity
    77.                     m,  //mass
    78.                     rP, //perihelion distance
    79.                     rA, //aphelion distance
    80.                     aP, //perihelion atttraction limit
    81.                     aA; //aphelion attraction limit
    82.     public enum NUCLEUSTYPE { ROCK, GASGIANT };
    83.     public NUCLEUSTYPE nucleusType;
    84.  
    85.     public Nucleus()
    86.     {
    87.         a = e = m = 0.0f;
    88.         rP = rA = 0.0f;
    89.         aP = aA = 0.0f;
    90.         nucleusType = NUCLEUSTYPE.ROCK;
    91.     }
    92.  
    93.     public float LowBound(DoleParams param)
    94.     {
    95.         return param.LowBound(rP, aP);
    96.     }
    97.  
    98.     public float HighBound(DoleParams param)
    99.     {
    100.         return param.HighBound(rA, aA);
    101.     }
    102. }
    103.  
    104. public class SolarSystem : MonoBehaviour
    105. {
    106.     public int seed = 41;
    107.  
    108.     public float solarMass = 1.0f;
    109.  
    110.     void Start()
    111.     {
    112.         float   nE  = 0,     //nucleus eccentricity
    113.                 nA  = 0,     //nucleus semi-major axis
    114.                 mL  = 0,     //mass last
    115.                 e   = 0,     //eccentricity
    116.                 m   = 0,     //mass
    117.                 rP  = 0,     //perihelion distance, au
    118.                 rA  = 0,     //aphelion distance, au
    119.                 a   = 0,     //semi-major axis
    120.                 mu  = 0,     //mass-unit
    121.                 aP  = 0,
    122.                 aA  = 0,
    123.                 mC  = 0,     //critical mass
    124.                 rho = 0,     //density
    125.                 bw  = 0,     //bandwidth
    126.                 v   = 0,     //volume
    127.                 dA  = 0,
    128.                 dP  = 0,
    129.                 mP  = 0;     //mass pass
    130.  
    131.         int     nPlanet     = 0,
    132.                 n,
    133.                 lowBand,
    134.                 highBand,
    135.                 dustCheck   = 0,
    136.                 iterate,
    137.                 hit;
    138.  
    139.         int MaxPlanets = CONSTANTS.MAXNUCLEI;
    140.         Nucleus[] nuclei = new Nucleus[MaxPlanets];
    141.         DoleParams param = new DoleParams(solarMass);
    142.  
    143.         BANDCONTENTS[] band = new BANDCONTENTS[CONSTANTS.MAXBAND + 1];
    144.  
    145.         Randomize(seed);
    146.  
    147.         //Initialize the Bands...
    148.         for (n = 1; n <= CONSTANTS.MAXBAND; n++)
    149.         {
    150.             band[n] = BANDCONTENTS.MIXED;
    151.         }
    152.         //Inject nuclei into the cloud and acrete gas and dust
    153.         for (n = 0; n < CONSTANTS.MAXNUCLEI; n++)
    154.         {
    155.             //Check to see if all bands are taken
    156.             int bandFull = 0;
    157.  
    158.             for (int i = 1; i <= CONSTANTS.MAXBAND; i++)
    159.             {
    160.                 if (band[i] == BANDCONTENTS.MIXED)
    161.                 {
    162.                     bandFull = 1;
    163.                     break;
    164.                 }
    165.             }
    166.  
    167.             if (bandFull == 0)
    168.             {
    169.                 break;
    170.             }
    171.  
    172.             //If the Semi-Major Axis of the Nucleus is < 0.3f, then iterate until it isn't.
    173.             while ((nA = 50.0f * RandomNumber()) < 0.3f) ;
    174.             //Calculate the Eccentricity of the Nucleus
    175.             nE = 1.0f - (1.0f - Mathf.Pow(1.0f - RandomNumber(), CONSTANTS.Q));
    176.  
    177.             Nucleus body = new Nucleus();
    178.             body.a = nA;
    179.             body.e = nE;
    180.             body.m = param.mN;
    181.  
    182.             mL = 0.0f;
    183.             iterate = 0;
    184.  
    185.             while (true) //while Loop
    186.             {
    187.                 a = body.a;
    188.                 e = body.e;
    189.                 m = body.m;
    190.                 body.rP = rP = nA - (nA * nE);
    191.                 body.rA = rA = nA + (nA * nE);
    192.  
    193.                 if (m == 0)
    194.                 {
    195.                     m = CONSTANTS.NUCLEIMASS;
    196.                 }
    197.  
    198.                 mu = Mathf.Pow(m / (1.0f + m), 0.25f);
    199.                 body.aP = aP = rP * mu;
    200.                 body.aA = aA = rA * mu;
    201.  
    202.                 //Nuclei sweeps up band
    203.                 //Establish the bounds on swept volume
    204.                 lowBand = cint(body.LowBound(param) * 5);
    205.                 if (lowBand < 1)
    206.                 {
    207.                     lowBand = 1;
    208.                 }
    209.  
    210.                 highBand = cint(body.HighBound(param) * 5);
    211.                 if (highBand > CONSTANTS.MAXBAND)
    212.                 {
    213.                     highBand = CONSTANTS.MAXBAND;
    214.                 }
    215.  
    216.                 if (lowBand == highBand)
    217.                 {
    218.                     highBand++;
    219.                 }
    220.  
    221.                 //Calculate critical mass limits
    222.                 mC = param.CriticalMass(rP);
    223.  
    224.                 //Check for bands with dust within range
    225.                 if (iterate == 0)
    226.                 {
    227.                     dustCheck = 0;
    228.  
    229.                     for (int bandno = lowBand; bandno <= highBand; bandno++)
    230.                     {
    231.                         if (mL > 0 || band[bandno] == BANDCONTENTS.MIXED)
    232.                         {
    233.                             //Debug.Log("Band #" + bandno + " has dust...");
    234.                             dustCheck = 1;
    235.                             break;
    236.                         }
    237.                     }
    238.  
    239.                     if (dustCheck == 0)
    240.                     {
    241.                         //Debug.Log("No dust; " + n + " is a dud...");
    242.                         if (mL == 0)
    243.                         {
    244.                             body.a = 0.0f;
    245.                             body.e = 0.0f;
    246.                             body.m = 0.0f;
    247.                         }
    248.                         break;
    249.                     }
    250.                 }
    251.  
    252.                 //Calculate mass using Dole donut approximation
    253.                 if (m == 0)
    254.                 {
    255.                     m = CONSTANTS.NUCLEIMASS;
    256.                 }
    257.  
    258.                 if (m < mC)
    259.                 {
    260.                     //Debug.Log("param.Density()");
    261.                     rho = param.Density(body.a);
    262.                 }
    263.                 else
    264.                 {
    265.                     //Debug.Log("param.GasDensity()");
    266.                     rho = param.GasDensity(body.a, mC / m);
    267.                 }
    268.  
    269.                 bw =        2 * body.a * body.e
    270.                         +   aA + aP
    271.                         +   param.W * (rA + aA) / (1 - param.W)
    272.                         +   param.W * (rP - aP) / (1 + param.W);
    273.  
    274.                 v = 2 * Mathf.PI * body.a * bw * (aA + aP);
    275.  
    276.                 dP = body.LowBound(param);
    277.                 dA = body.HighBound(param);
    278.  
    279.                 lowBand = cint(dP * 5);
    280.                 highBand = cint(dA * 5);
    281.  
    282.                 if (lowBand == highBand)
    283.                 {
    284.                     highBand++;
    285.                 }
    286.  
    287.                 if (highBand > CONSTANTS.MAXBAND)
    288.                 {
    289.                     highBand = CONSTANTS.MAXBAND;
    290.                 }
    291.  
    292.                 mP = 0.0f;
    293.  
    294.                 for (int bandno = lowBand; bandno <= highBand; bandno++)
    295.                 {
    296.                     float au = bandno / 5.0f,
    297.                             xPnow,
    298.                             xAnow,
    299.                             bandVol,
    300.                             bandVol2;
    301.  
    302.                     //Calculate mass of wedge of doughnut
    303.                     xPnow =     aP + (aA - aP)
    304.                             *   (bandno - lowBand) / (float)(highBand - lowBand);
    305.                     //Debug.Log("xPnow: " + xPnow);
    306.                     xAnow =     aP + (aA - aP)
    307.                             *   (bandno + 1 - lowBand) / (float)(highBand - lowBand);
    308.                     //Debug.Log("xAnow: " + xAnow);
    309.  
    310.                     bandVol = 2 * Mathf.PI * au * 0.2f * (xPnow + xAnow);
    311.                     //Debug.Log("Band Volume = " + bandVol);
    312.  
    313.                     for (int i = 0; i < nPlanet; i++)
    314.                     {
    315.                         float dP2 = nuclei[i].LowBound(param);
    316.                         //Debug.Log("dP2 = " + dP2);
    317.                         float dA2 = nuclei[i].HighBound(param);
    318.                         //Debug.Log("dA2 = " + dA2);
    319.  
    320.                         if (dA2 < au || dP2 > au + 0.2f)
    321.                         {
    322.                             //Debug.Log("dA2 < au OR dP2 > au + 0.2f");
    323.                             continue;
    324.                         }
    325.  
    326.                         //Overlap exists, find bounds on the overlap volume
    327.                         float bw2 =     2 * nuclei[i].a * nuclei[i].e
    328.                                     +   nuclei[i].aP + nuclei[i].aA
    329.                                     +   param.W * (nuclei[i].rA + nuclei[i].aA) / (1 - param.W)
    330.                                     +   param.W * (nuclei[i].rP - nuclei[i].aP) / (1 + param.W);
    331.                         //Debug.Log("Bandwidth 2 = " + bw2);
    332.  
    333.                         //At au, overlap has xP2now and xA2now as heights
    334.                         float xP2now =      nuclei[i].aP + (nuclei[i].aA - nuclei[i].aP)
    335.                                         *   (au - dP2) / (dA2 - dP2);
    336.                         //Debug.Log("xP2now = " + xP2now);
    337.                         float xA2now =      nuclei[i].aP + (nuclei[i].aA - nuclei[i].aP)
    338.                                         *   (au + 0.2f - dP2) / (dA2 - dP2);
    339.                         //Debug.Log("xA2now = " + xA2now);
    340.  
    341.                         bandVol2 = 2 * Mathf.PI * au * 0.2f * (xP2now + xA2now);
    342.                         //Debug.Log("Band Volume 2 = " + bandVol2);
    343.  
    344.                         //If previously swept band larger than this swept, no dust swept
    345.                         if (bandVol2 >= bandVol)
    346.                         {
    347.                             //Debug.Log("bandVol2 >= bandVol");
    348.                             bandVol = 0.0f;
    349.                         }
    350.                         else
    351.                         {
    352.                             //Debug.Log("bandVol2 ! >= bandVol");
    353.                             bandVol -= bandVol2;
    354.                         }
    355.                         break;
    356.                     }
    357.                     mP += bandVol * rho;
    358.                     //Debug.Log("Mass pass = " + mP);
    359.                 }
    360.                 //Debug.Log("--------------------");
    361.  
    362.                 body.m = m = mP;
    363.  
    364.                 //Check for mass growth convergence
    365.                 if (m == 0)
    366.                 {
    367.                     //Debug.Log("Mass is 0!");
    368.                     m = CONSTANTS.NUCLEIMASS;
    369.                 }
    370.  
    371.                 if (m >= mC)
    372.                 {
    373.                     //Debug.Log("Nucleus " + n + " is a Gas Giant!");
    374.                     body.nucleusType = Nucleus.NUCLEUSTYPE.GASGIANT;
    375.                 }
    376.  
    377.                 if (Mathf.Abs(mL / m - 1) < 0.01f)
    378.                 {
    379.                     //Debug.Log("(mL / m - 1) < 0.01");
    380.                     break;
    381.                 }
    382.  
    383.                 mL = m;
    384.                 //Debug.Log("Mass Last = " + mL);
    385.  
    386.                 iterate = 1;
    387.             }
    388.  
    389.             //Clear out bands emptied of dust
    390.             if (dustCheck == 1)
    391.             {
    392.                 Debug.Log("Event #" + n + " Completed mass growth; swept from " + dP + " to " + dA);
    393.                 if (m > mC)
    394.                 {
    395.                     Debug.Log("Gas Giant!");
    396.                 }
    397.             }
    398.  
    399.             if (lowBand == highBand)
    400.             {
    401.                 highBand++;
    402.             }
    403.  
    404.             for (int bandno = lowBand; bandno <= highBand; bandno++)
    405.             {
    406.                 if (m < mC)
    407.                 {
    408.                     if (band[bandno] == BANDCONTENTS.MIXED)
    409.                     {
    410.                         //Debug.Log("Band #" + bandno + " contains GAS only...");
    411.                         band[bandno] = BANDCONTENTS.GAS;
    412.                     }
    413.                 }
    414.  
    415.                 if (m >= mC)
    416.                 {
    417.                     //Clear the bands emptied by gas
    418.                     body.nucleusType = Nucleus.NUCLEUSTYPE.GASGIANT;
    419.                     if (band[bandno] == BANDCONTENTS.GAS || band[bandno] == BANDCONTENTS.MIXED)
    420.                     {
    421.                         //Debug.Log("Band #" + bandno + " is EMPTY...");
    422.                         band[bandno] = BANDCONTENTS.EMPTY;
    423.                     }
    424.                 }
    425.             }
    426.  
    427.             if (body.a == 0.0f)
    428.             {
    429.                 //Debug.Log("Body #" + n + " Axis was 0...");
    430.                 continue;
    431.             }
    432.  
    433.             /*hit = 0;
    434.             for (int i = 0; i < nPlanet; i++)
    435.             {
    436.                 float aNew,
    437.                         mNew,
    438.                         eNew,
    439.                         term1,
    440.                         term2,
    441.                         term3,
    442.                         muNew;
    443.  
    444.                 if ((nuclei[i].rA < (rP - aP) && (nuclei[i].rA + nuclei[i].aA) < rP) || ((rA + aA) < nuclei[i].rP && rA < (nuclei[i].rP - nuclei[i].aP)))
    445.                 {
    446.                     continue;
    447.                 }
    448.  
    449.                 Debug.Log("Coalescence of nuclei #" + n + " " + i);
    450.  
    451.                 hit = 1;
    452.                 aNew = (body.m + nuclei[i].m) / (body.m / body.a + nuclei[i].m / nuclei[i].a);
    453.                 //Debug.Log("aNew = " + aNew);
    454.                 term1 = body.m * Mathf.Sqrt(body.a) * Mathf.Sqrt(1 - Mathf.Pow(body.e, 2.0f));
    455.                 //Debug.Log("term1 = " + term1);
    456.                 term2 = nuclei[i].m * Mathf.Sqrt(nuclei[i].a) * Mathf.Sqrt(1 - Mathf.Pow(nuclei[i].e, 2.0f));
    457.                 //Debug.Log("term2 = " + term1);
    458.                 term3 = (body.m + nuclei[i].m) * Mathf.Sqrt(aNew);
    459.                 //Debug.Log("term3 = " + term1);
    460.  
    461.                 eNew = Mathf.Sqrt(Mathf.Abs(1 - Mathf.Pow((term1 + term2) / term3, 2.0f)));
    462.                 //Debug.Log("New Eccentricity = " + eNew);
    463.                 mNew = body.m + nuclei[i].m;
    464.                 //Debug.Log("New Mass = " + mNew);
    465.  
    466.                 nuclei[i].a = 0.0f;
    467.                 nuclei[i].e = 0.0f;
    468.                 nuclei[i].m = 0.0f;
    469.                 body.a = aNew;
    470.                 body.e = eNew;
    471.                 body.m = mNew;
    472.                 //body.rP = aNew - (aNew * eNew);
    473.                 body.rP = aNew * (1 - eNew);
    474.                 //Debug.Log("body.rP = " + body.rP);
    475.                 body.rA = aNew * (1 + eNew);
    476.                 //body.rA = aNew + (aNew * eNew);
    477.                 //Debug.Log("body.rA = " + body.rA);
    478.                 muNew = Mathf.Pow(mNew / (1 + mNew), 0.25f);
    479.                 body.aP = body.rP * muNew;
    480.                 body.aA = body.rA * muNew;
    481.             } //End: (int i = 0; i < nPlanet; i++)
    482.  
    483.             m = body.m;
    484.  
    485.             int lowBand2 = cint(body.LowBound(param) * 5),
    486.                 highBand2 = cint(body.HighBound(param) * 5);
    487.  
    488.             lowBand2 = Mathf.Max(lowBand2, 6, lowBand);
    489.             //lowBand2 = imax(lowBand2, 6, lowBand);
    490.             //Debug.Log("lowBand2 = " + lowBand2);
    491.             highBand2 = Mathf.Max(Mathf.Min(highBand2, CONSTANTS.MAXBAND, highBand), 6);
    492.             //highBand2 = imax(imin(highBand2, CONSTANTS.MAXBAND, highBand), 6);
    493.             //Debug.Log("highBand2 = " + highBand2);
    494.  
    495.             if (lowBand2 == highBand2)
    496.             {
    497.                 highBand2++;
    498.             }
    499.  
    500.             if (body.m >= mC)
    501.             {
    502.                 body.nucleusType = Nucleus.NUCLEUSTYPE.GASGIANT;
    503.             }
    504.  
    505.             //Add new Planet
    506.             if (nPlanet >= MaxPlanets)
    507.             {
    508.                 Nucleus[] newNuclei = new Nucleus[MaxPlanets * 2];
    509.  
    510.                 //Copy old planets to new
    511.                 for (int i = 0; i < nPlanet; i++)
    512.                 {
    513.                     newNuclei[i] = nuclei[i];
    514.                 }
    515.  
    516.                 nuclei = null;
    517.  
    518.                 nuclei = newNuclei;
    519.                 MaxPlanets *= 2;
    520.                 //Debug.Log("Max Planets = " + MaxPlanets);
    521.             }
    522.  
    523.             nuclei[nPlanet++] = body;
    524.             //Debug.Log("nPlanet: " + nPlanet);
    525.  
    526.             //Sweep planet array, removing merged bodies
    527.             for (int i = 0; i < nPlanet; i++)
    528.             {
    529.                 if (nuclei[i].a == 0)
    530.                 {
    531.                     Debug.Log("Nuking body " + i + "; " + nPlanet + " remaining");
    532.                     nuclei[i] = nuclei[nPlanet - 1];
    533.                     nuclei[nPlanet - 1].a = 0.0f;
    534.                     nPlanet--;
    535.                 }
    536.             }*/
    537.  
    538.         } //End: for (n = 0; n < CONSTANTS.MAXNUCLEI; n++)
    539.  
    540.         Debug.Log("--------------------");
    541.         Debug.Log(" all bands taken..." + n + " nuclei used ");
    542.  
    543.         //OuputPlanetData(nuclei, band);
    544.     }
    545.  
    546.     //Seed the Random Number Generator
    547.     void Randomize(int seed)
    548.     {
    549.         Random.seed = seed;
    550.     }
    551.     //Return a Random Number
    552.     float RandomNumber()
    553.     {
    554.         return Random.value;
    555.     }
    556.  
    557.     int cint(float arg)
    558.     {
    559.         return (int)(Mathf.Floor(arg + 0.5f));
    560.     }
    561.  
    562.     int imax(int a, int b = -CONSTANTS.MAXINT, int c = -CONSTANTS.MAXINT)
    563.     {
    564.         if (a > b)
    565.             return (a > c) ? a : c;
    566.         else
    567.             return (b > c) ? b : c;
    568.     }
    569.  
    570.     int imin(int a, int b = CONSTANTS.MAXINT, int c = CONSTANTS.MAXINT)
    571.     {
    572.         if (a < b)
    573.             return (a < c) ? a : c;
    574.         else
    575.             return (b < c) ? b : c;
    576.     }
    577.  
    578.     void OuputPlanetData(Nucleus[] nuclei, BANDCONTENTS[] band)
    579.     {
    580.         //for (int i = 0; i < nuclei.Length; i++)
    581.         //{
    582.         //    Debug.Log("Planet #" + i + " Semi-Major Axis: " + nuclei[i].a);
    583.         //    Debug.Log("Planet #" + i + " Type: " + nuclei[i].nucleusType);
    584.         //}
    585.  
    586.         for (int i = 0; i < CONSTANTS.MAXBAND; i++)
    587.         {
    588.             Debug.Log("Band #" + i + ", Contents =" + band[i]);
    589.         }
    590.     }
    591. }

    Definitely need the smart guys to help trouble shoot why this isn't working as it should be. I imagine it has something to do with my limited understanding of C# and some kind of stupid error on my part.

    I've re-read the paper that the code is based on, and I'm pretty sure things are ordered right...just, something is breaking in the coalescence and event handling...
     
  18. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Bump...anyone see anything obvious or anything at all?
     
  19. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    As this accretion stuff is different to the original topic of this thread, I suggest you start a new thread. Only in the hope that you get more traffic and help regarding these conversions, as regulars who have seen this title will probably skip over and not see your new question (or just scan the page and see the planet mesh stuff). There are plenty of clever people here, just want you to get their attention for the optimal amount of help! Including C++ to C# in the title will also help.
    Best of Luck.

    EDIT :
    Add the link to the accretion code conversion question in your first post so people can jump straight to the relevant post (found by clicking the #number next to the like/reply) : http://forum.unity3d.com/threads/c-to-c-planetary-accretion.402362/#post-2654010
     
    Last edited: May 29, 2016
    BradMick likes this.
  20. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Done and done! I'm not abandoning the noise work, just need to get the Solar System generation working before I go back to the noise bits, so...stay tuned Alucardj! You've been a great help!
     
  21. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Bump to add a hyperlink to the Accretion Code....
     
  22. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Well, had to more or less stop tinkering as I'm under a deadline and need to get back on track...so, here's the start of some all new procedural solar system code using a simple seed mechanism. It's a very, very rough start so far...I need to come up with some more rules to manage the planets, but for now it works well enough I guess. It does more or less what I want. I think the one thing I may do next is adjust the semi-major axis based on the titus-bode law...use the titus-bode to determine a reasonable idea of where the planets should be and then apply the random number generator to shift the planets forward/backward from there. My concern is that the planet distribution will then become way to uniform...but, given that I'm trying to replicate more or less 'realistic' looking solar systems it may not be a bad thing...

    But, code!

    This is the Manager...

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class Star
    6. {
    7.     public string   n,          //The name of the star
    8.                     h_d;        //The hiparcos catalogue designation
    9.     public float    m_s,        //Solar mass, 1 = Sol
    10.                     l_s,        //Solar luminosity, 1 = Sol
    11.                     r_s,        //Solar radius, 1 = Sol
    12.                     //a_e,      //Semi-major axis of the Ecosphere, AU
    13.                     a_ei,       //Semi-major axis of the inner habitability zone, AU
    14.                     a_eo,       //Semi-major axis of the outer habilibility zone, AU
    15.                     a_np,       //Semi-major axis of the nearest planet, AU
    16.                     a_fp;       //Semi-major axis of the furthest planet, AU
    17.     public Vector3  p;          //Star coordinates
    18.     public int      s;          //Seed
    19.     public bool     IsHabitable;
    20.  
    21.     public Star(string name, string hiparcosDesignation, float mass, float luminosity, float radius, Vector3 position, int seed, bool habitable)
    22.     {
    23.         //Set the name
    24.         n = name;
    25.         //Set the Designation
    26.         h_d = hiparcosDesignation;
    27.         //Set the mass
    28.         m_s = mass;
    29.         //If the luminsoity is 0, generate a luminosity value
    30.         if (luminosity == 0)
    31.             CalculateLuminosity(mass);
    32.         else
    33.             l_s = luminosity;
    34.         //Set the solar radius
    35.         r_s = radius;
    36.         //Calculate the inner habitability limit
    37.         CalculateInnerHabitabilityLimit(luminosity);
    38.         //Calculate the outer habitability limit
    39.         CalculateOuterHabitabilityLimit(luminosity);
    40.         //Calculate the inner planet limit
    41.         CalculateInnerPlanetLimit(mass);
    42.         //Calculate the outer planet limit
    43.         CalculateOuterPlanetLimit(mass);
    44.         //Set the stars position in the world
    45.         p = position;
    46.         //Set the seed
    47.         s = seed;
    48.         //Set the habitability
    49.         IsHabitable = habitable;
    50.     }
    51.  
    52.     void CalculateLuminosity(float mass)
    53.     {
    54.         if (mass < 1.0f)
    55.             l_s = Mathf.Pow(m_s, 1.75f * (m_s - 0.1f) + 3.325f);
    56.         else
    57.             l_s = Mathf.Pow(m_s, 0.5f * (2.0f - m_s) + 4.4f);
    58.     }
    59.  
    60.     void CalculateInnerHabitabilityLimit(float luminosity)
    61.     {
    62.         a_ei = Mathf.Sqrt(l_s / 1.1f);
    63.     }
    64.  
    65.     void CalculateOuterHabitabilityLimit(float luminosity)
    66.     {
    67.         a_eo = Mathf.Sqrt(l_s / 0.53f);
    68.     }
    69.  
    70.     void CalculateInnerPlanetLimit(float mass)
    71.     {
    72.         a_np = 0.3f * Mathf.Pow(mass, 1.0f / 3.0f);
    73.     }
    74.  
    75.     void CalculateOuterPlanetLimit(float mass)
    76.     {
    77.         a_fp = 50.0f * Mathf.Pow(mass, 1.0f / 3.0f);
    78.     }
    79. }
    80.  
    81. public class SysGen : MonoBehaviour
    82. {
    83.     public int universeSeed = 1234567890;
    84.  
    85.     void Start()
    86.     {
    87.         SeedRandomNumberGenerator(universeSeed);
    88.                                 //Name                      HIP Cat ID      Mass        Lum         Radius      Position                                            Seed                        Habitable?        
    89.         Star[] starTable = {    new Star("The Throne",      "HIP 20899",    1.084f,     1.38f,      1.066f,     new Vector3(0.0f,       0.0f,       0.0f),          GenerateRandomInteger(),    true),
    90.                                 new Star("---------1",      "HIP 20146",    0.932f,     0.755f,     0.945f,     new Vector3(5.166f,     -3.653f,    0.288f),        GenerateRandomInteger(),    false),
    91.                                 new Star("---------2",      "HIP 20205",    2.832f,     64.351f,    2.299f,     new Vector3(5.605f,     -1.717f,    -4.449f),       GenerateRandomInteger(),    true)    };
    92.  
    93.         OutputStarData(starTable);
    94.  
    95.         GenerateStarSystems(starTable);
    96.     }
    97.  
    98.     //1. Seed the random number generator
    99.     void SeedRandomNumberGenerator(int seed)
    100.     {
    101.         Random.seed = seed;
    102.     }
    103.     //3. Return a random number between 0 and 1
    104.     int GenerateRandomInteger()
    105.     {
    106.         return (int)Mathf.Round(Random.value * 1000);
    107.     }
    108.  
    109.     void GenerateStarSystems(Star[] star)
    110.     {
    111.         for (int i = 0; i < star.Length; i++)
    112.         {
    113.             GameObject newSystem = new GameObject(star[i].n);
    114.             newSystem.transform.parent = transform;
    115.             newSystem.transform.position = star[i].p;
    116.  
    117.             newSystem.gameObject.AddComponent<PlanetarySystem>();
    118.             newSystem.gameObject.GetComponent<PlanetarySystem>().GeneratePlanetarySystem(star[i].s, star[i]);
    119.  
    120.         }//End system generation loop
    121.     }
    122.  
    123.     void OutputStarData(Star[] star)
    124.     {
    125.         for (int i = 0; i < star.Length; i++)
    126.         {
    127.             Debug.Log("Name:       " + star[i].n);
    128.             Debug.Log("Mass:       " + star[i].m_s);
    129.             Debug.Log("Luminosity: " + star[i].l_s);
    130.             Debug.Log("Radius:     " + star[i].r_s);
    131.             Debug.Log("Inner Habitable Zone: " + star[i].a_ei + " AU");
    132.             Debug.Log("Outer Habitable Zone: " + star[i].a_eo + " AU");
    133.             Debug.Log("Near Planet Limit:    " + star[i].a_np + " AU");
    134.             Debug.Log("Far Planet Limit:     " + star[i].a_fp + " AU");
    135.             Debug.Log("--------------------");
    136.         }
    137.     }
    138. }
    139.  

    The Planetary System Code
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Planet
    5. {
    6.     public GameObject   planetAxis;
    7.     public GameObject   planetObject;
    8.  
    9.     public float    a,  //Semi-major axis, AU
    10.                     e,  //Eccentricity
    11.                     m,  //Mass, Solar Masses = 0.00003
    12.                     r,  //Equitorial radius, km
    13.                     t;  //Axial tilt in Degrees, Earth = 23.5
    14. }
    15.  
    16.  
    17. public class PlanetarySystem : MonoBehaviour
    18. {
    19.     const float Q = 0.077f; //Eccentricity constant
    20.  
    21.     private int NumberOfPlanets;
    22.  
    23.     private Star star;
    24.     private Planet[] planets;
    25.     private GameObject[] planetAxis;
    26.  
    27.     public void GeneratePlanetarySystem(int seed, Star parent)
    28.     {
    29.         //set the
    30.         star = parent;
    31.  
    32.         //1. Seed the random number generator
    33.         SeedRandomNumberGenerator(seed);
    34.         GeneratePlanetObjects();
    35.     }
    36.  
    37.     //2. Generate the number of planets based on the random seed up to a maximum of 20
    38.     void GeneratePlanetObjects()
    39.     {
    40.         //3. Generate a random number between 0 and 20
    41.         NumberOfPlanets = (int)Mathf.Round(20.0f * GenerateRandomFloat());
    42.  
    43.         planets = new Planet[NumberOfPlanets];
    44.         planetAxis = new GameObject[NumberOfPlanets];
    45.  
    46.         //4. First, generate the rotation point for the planets. This is the point of rotation for the
    47.         //   planets generated immediately after
    48.         for (int i = 0; i < NumberOfPlanets; i++)
    49.         {
    50.             planets[i] = new Planet();
    51.             planets[i].planetAxis   = new GameObject("PlanetAxis #" + i);
    52.             planets[i].planetAxis.transform.parent = transform;
    53.             planets[i].planetObject = GameObject.CreatePrimitive(PrimitiveType.Sphere);
    54.             planets[i].planetObject.name = "Planet #" + i;
    55.             planets[i].planetObject.transform.parent = planets[i].planetAxis.transform;
    56.         }
    57.  
    58.         //5. Generate semi-major axis
    59.         GenerateSemiMajorAxis();
    60.         //6. Generate eccentricity
    61.         GenerateEccentricity();
    62.     }
    63.    
    64.     void Update()
    65.     {
    66.         //rotation += 0.001f;
    67.  
    68.         //planets[1].planetAxis.transform.Rotate(Vector3.up, rotation);
    69.     }
    70.  
    71.     //1. Seed the random number generator
    72.     void SeedRandomNumberGenerator(int seed)
    73.     {
    74.         Random.seed = seed;
    75.     }
    76.     //3. Return a random number between 0 and 1
    77.     float GenerateRandomFloat()
    78.     {
    79.         return Random.value;
    80.     }
    81.     //5. Generate semi-major axis
    82.     void GenerateSemiMajorAxis()
    83.     {
    84.         if (star.IsHabitable == true)
    85.         {
    86.             //Place the first planet somewhere within the habitable zone
    87.             while ((planets[0].a = star.a_eo * GenerateRandomFloat()) < star.a_ei) ;
    88.             planets[0].planetObject.transform.position = new Vector3(transform.position.x, transform.position.y, transform.position.z + planets[0].a);
    89.  
    90.             //Next generate
    91.             for (int i = 1; i < NumberOfPlanets; i++)
    92.             {
    93.                 //While the semi-major axis is less than the near planet limit
    94.                 while ((planets[i].a = star.a_fp * GenerateRandomFloat()) < star.a_np) ;
    95.                 planets[i].planetObject.transform.position = new Vector3(transform.position.x, transform.position.y, transform.position.z + planets[i].a);
    96.             }
    97.         }
    98.         else
    99.         {
    100.             for (int i = 0; i < NumberOfPlanets; i++)
    101.             {
    102.                 //While the semi-major axis is less than the near planet limit
    103.                 while((planets[i].a = star.a_fp * GenerateRandomFloat()) < star.a_np);
    104.                 planets[i].planetObject.transform.position = new Vector3(transform.position.x, transform.position.y, transform.position.z + planets[i].a);
    105.             }
    106.         }
    107.     }
    108.     //6. Generate eccentricty
    109.     void GenerateEccentricity()
    110.     {
    111.         for (int i = 0; i < NumberOfPlanets; i++)
    112.         {
    113.             planets[i].e = 1.0f - (1.0f - Mathf.Pow(1.0f - GenerateRandomFloat(), Q));
    114.         }
    115.     }
    116. }
    117.  

    One really big limitation I've noticed with this is that whenever I try to rotate a single planet, it actually rotates that planet in all of the arrays...which isn't at all what I want or need them to do. Granted, this is just an experimental phase...one one system would be generated instead of all 3...so it may not be a concern...Each system would be it's own separate scene...so when you 'jump' to a new system it looks at the Star array and then loads that Star system prior to dumping you out of 'jump space'....

    Anyway, this puts me more or less back on track...still a lot to do. Hopefully this helps folks, even if it doesn't...just posting and talking about it helps me so...I guess I'll keep talking to myself ;)
     
  23. SwagRogerCoolAid

    SwagRogerCoolAid

    Joined:
    Aug 17, 2016
    Posts:
    20
    This is pretty awesome, where did you get to with this? I'm currently trying to split my gradient into different regions. See for libnoise to work with a sphere you need to generate out a icosphere and from there you can apply lib noise quite easily to be honest, they is distortion at the top pole but i think thats because the texture isn't the right size at the moment. I have what id call a comet.
     
  24. odival

    odival

    Joined:
    Jun 11, 2014
    Posts:
    57
    Woah, this level of coding is way beyond me.
    If I just add the planetary system, the manager, the planet test, shader and the accreation code, would everything work out and create a working planetary system or there are other stuff necessary that is not in this thread?
     
  25. SwagRogerCoolAid

    SwagRogerCoolAid

    Joined:
    Aug 17, 2016
    Posts:
    20

    Maybe create your own libnoise and generation and use his solar system example and improve on it yourself. Experiment with it to your needs.
     
  26. odival

    odival

    Joined:
    Jun 11, 2014
    Posts:
    57
    Chill out, I get you man. However what I'm asking for is if the scripts on this thread work as is, or if they require additional code/shaders or something beyond the content shared here. So, you see, the kind of information I want to elicit is exactly the kind of technicality that can help me further experiment with all that.
     
  27. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    I'm actually back at this project. I took a break after I graduated because I and my Family needed it. I'm making slow headway in getting the Accrete program converted to C#. I've had time to sit down and really read and understand the paper that spawned the code as well, which has helped immensely with understanding the process at work.

    In so far as answering your question....I think the majority of the code is working as is in this thread. To be honest I can't remember though, I'd have to compare the things in the thread to what's in my files.

    But once I get this thing back up and working I'll update the thread.

    Oh, and the code isn't beyond you, not even in the least. It's a matter of experience and tenacity. Google and YouTube are your friend. I've spent the last 2 years in school Googling and YouTubing and reading the Unity Programming guide like a fiend. You can do all of this stuff man, no doubt! Just give it a little time and patience and you'll do fine. Good things come to those who wait and all that... : D
     
    odival likes this.
  28. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Okay, so slowly but sure getting back into the swing of things. Currently I'm re-writing my terrain generator to allow for seamless LOD transitions. What I've worked out so far is that I have to create 9 custom vertex buffers or really 9 patterns for each of the various configurations around the origin point. At this point I'm nowhere near the dynamic LOD bits, as I'm still in the process of figuring out how to make the Vertex Buffers work right.

    I've hit a small snag as I'm going and I'm out of ideas on how to proceed. Where I'm stuck at is with the (0, 1) surface case. I've got the first row generating the way it's supposed to up to a certain point. See screenshot, and you'll also see it in the code. I've isolated the issue to line 124, which is iterating through the row based on the LOD. If I set: i <= LOD, then I get my last column (which makes sense), however as I scale this up I lose more and more of the last columns on the first row.

    Anyone have any ideas on how to fix that? Once that's complete I'll fix make the second row generate and then the third. It's actually really easy after you've drawn it all out...or, as you can see from the code hard coded the hell out of it :)

    Anyway, code! And again, problem lies at line 124, coincidentally this will also solve the issue I'll probably run into on the second row as well...and I guess also the third fourth and fifth.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class LODTest : MonoBehaviour
    5. {
    6.     public int LOD = 1; //LOD follows: 1, 3, 5, 7, 9, 11, 13, 15, etc..
    7.     public int chunkSize = 1000; //m
    8.  
    9.     private int vertsPerChunk = 3; //The base number of vertices per terrain chunk
    10.  
    11.     void Start()
    12.     {
    13.         CreateSubDivisionSurfaces();
    14.     }
    15.  
    16.     void CreateSubDivisionSurfaces()
    17.     {
    18.         //For each each Sub Division Surface, Create a new Surface
    19.         /*for (int i = 0; i < subDivisions; i++)
    20.         {
    21.             for (int j = 0; j < subDivisions; j++)
    22.             {
    23.                 GenerateSurfaceCoordinates(j, i);
    24.             }
    25.         }*/
    26.  
    27.         GenerateSurfaceCoordinates(LOD, 0, 0);
    28.  
    29.         GenerateSurfaceCoordinates(LOD, 0, 1);
    30.     }
    31.  
    32.     void GenerateSurfaceCoordinates(int LOD, int xIndex, int zIndex)
    33.     {
    34.         //Appropriately size (and create) the Vertex Array
    35.         int vertsPerLOD = vertsPerChunk * LOD;
    36.         Vector3[] vertices = new Vector3[vertsPerLOD * vertsPerLOD];
    37.         //Calculate the increment to keep the vertices constrained to chunkSize
    38.         float increment = chunkSize / ((float)vertsPerLOD - 1);
    39.      
    40.         for (int i = 0, index = 0; i < vertsPerLOD; i++)
    41.         {
    42.             for (int j = 0; j < vertsPerLOD; j++, index++)
    43.             {
    44.                 //Vertex Coordinates
    45.                 float xPos = (float)j * increment - (chunkSize / 2.0f) + (xIndex * chunkSize);
    46.                 float yPos = 0.0f;
    47.                 float zPos = (float)i * increment - (chunkSize / 2.0f) + (zIndex * chunkSize);
    48.  
    49.                 //Set the Vertex positions based on the coordinates generated above
    50.                 vertices[index] = new Vector3(xPos, yPos, zPos);
    51.             }
    52.         }
    53.  
    54.         CreateSurfaceObject(LOD, vertices, xIndex, zIndex);
    55.     }
    56.  
    57.     void CreateSurfaceObject(int LOD, Vector3[] vertexArray, int xIndex, int zIndex)
    58.     {
    59.         //Create a new GameObject
    60.         GameObject surface = new GameObject("Surface(" + xIndex + ", " + zIndex + ")");
    61.         surface.tag = gameObject.tag;
    62.         surface.layer = gameObject.layer;
    63.         surface.transform.parent = transform;
    64.         surface.transform.position = transform.position;
    65.         //Add the MeshRenderer
    66.         surface.gameObject.AddComponent<MeshRenderer>();
    67.         //Add the MeshCollider
    68.         surface.gameObject.AddComponent<MeshCollider>();
    69.         //Add the MeshFilter
    70.         surface.gameObject.AddComponent<MeshFilter>();
    71.  
    72.         //Create the Mesh
    73.         CreateSurfaceMesh(LOD, surface, vertexArray, xIndex, zIndex);
    74.     }
    75.  
    76.     void CreateSurfaceMesh(int LOD, GameObject surface, Vector3[] vertexArray, int xIndex, int zIndex)
    77.     {
    78.         //Create and size the Vertex Buffer
    79.         int vertsPerLOD = vertsPerChunk * LOD;
    80.         int vertBufferSize = vertsPerLOD - 1;
    81.         int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    82.         //Create a new Mesh object
    83.         Mesh surfaceMesh;
    84.         //Create a new Mesh using the Objects MeshFilter
    85.         surfaceMesh = surface.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    86.         surfaceMesh.name = surface.name;
    87.  
    88.         if (xIndex == 0 && zIndex == 0)
    89.         {
    90.             //Step through the Vertex Buffer
    91.             for (int triIndex = 0, vertIndex = 0, i = 0; i < vertBufferSize; i++, vertIndex++)
    92.             {
    93.                 for (int j = 0; j < vertBufferSize; j++, triIndex += 6, vertIndex++)
    94.                 {
    95.                     //  6---7---8
    96.                     //  |   |   |
    97.                     //  3---4---5
    98.                     //  |   |   |
    99.                     //  0---1---2
    100.                     //
    101.                     //  LOD 1 is represented by a 3x3 vertice grid.
    102.  
    103.                     // (3) 1
    104.                     //     |\
    105.                     //     | \
    106.                     // (0) 0--2 (1)
    107.                     vertexBuffer[triIndex]      = vertIndex;
    108.                     vertexBuffer[triIndex + 1]  = vertIndex + vertsPerLOD;
    109.                     vertexBuffer[triIndex + 2]  = vertIndex + 1;
    110.                     // (3) 1--2 (4)
    111.                     //      \ |
    112.                     //       \|
    113.                     //        0 (1)
    114.                     vertexBuffer[triIndex + 3]  = vertIndex + 1;
    115.                     vertexBuffer[triIndex + 4]  = vertIndex + vertsPerLOD;
    116.                     vertexBuffer[triIndex + 5]  = vertIndex + vertsPerLOD + 1;
    117.                 }
    118.             }
    119.         }
    120.  
    121.  
    122.         if (xIndex == 0 && zIndex == 1)
    123.         {
    124.             for (int triIndex = 0, vertIndex = 0, i = 0; i < LOD; i++, triIndex += 9, vertIndex += 2 ) //LOD works...sort of...only if i <= LOD does it do what it's supposed to do...
    125.             {
    126.                 //  6---7---8
    127.                 //  |   |   |
    128.                 //  3---4---5
    129.                 //  |   |   |
    130.                 //  0---1---2
    131.                 //
    132.                 //  LOD 1 is represented by a 3x3 vertice grid. For a Surface that is 'north' of the origin
    133.                 //  the below pattern is used.
    134.  
    135.                 //     1 (3)
    136.                 //     |\
    137.                 //     | \
    138.                 // (0) 0--2 (1)
    139.                 vertexBuffer[triIndex] = vertIndex;
    140.                 vertexBuffer[triIndex + 1] = vertIndex + vertsPerLOD;
    141.                 vertexBuffer[triIndex + 2] = vertIndex + 1;
    142.                 // (3) 1---2 (5)
    143.                 //      \ /
    144.                 //   (1) 0
    145.                 vertexBuffer[triIndex + 3] = vertIndex + 1;
    146.                 vertexBuffer[triIndex + 4] = vertIndex + vertsPerLOD;
    147.                 vertexBuffer[triIndex + 5] = vertIndex + vertsPerLOD + 2;
    148.                 //        1 (5)
    149.                 //       /|
    150.                 //      / |
    151.                 // (1) 0--2 (2)
    152.                 vertexBuffer[triIndex + 6] = vertIndex + 1;
    153.                 vertexBuffer[triIndex + 7] = vertIndex + vertsPerLOD + 2;
    154.                 vertexBuffer[triIndex + 8] = vertIndex + 2;
    155.             }
    156.             /*  
    157.             //.............
    158.             triIndex = 9;
    159.             vertIndex = 2;
    160.             //     1 (3)
    161.             //     |\
    162.             //     | \
    163.             // (0) 0--2 (1)
    164.             vertexBuffer[triIndex] = vertIndex;
    165.             vertexBuffer[triIndex + 1] = vertIndex + vertsPerLOD;
    166.             vertexBuffer[triIndex + 2] = vertIndex + 1;
    167.             // (3) 1---2 (5)
    168.             //      \ /
    169.             //   (1) 0
    170.             vertexBuffer[triIndex + 3] = vertIndex + 1;
    171.             vertexBuffer[triIndex + 4] = vertIndex + vertsPerLOD;
    172.             vertexBuffer[triIndex + 5] = vertIndex + vertsPerLOD + 2;
    173.             //        1 (5)
    174.             //       /|
    175.             //      / |
    176.             // (1) 0--2 (2)
    177.             vertexBuffer[triIndex + 6] = vertIndex + 1;
    178.             vertexBuffer[triIndex + 7] = vertIndex + vertsPerLOD + 2;
    179.             vertexBuffer[triIndex + 8] = vertIndex + 2;
    180.             //.............
    181.             triIndex = 18;
    182.             vertIndex = 4;
    183.             //     1 (3)
    184.             //     |\
    185.             //     | \
    186.             // (0) 0--2 (1)
    187.             vertexBuffer[triIndex] = vertIndex;
    188.             vertexBuffer[triIndex + 1] = vertIndex + vertsPerLOD;
    189.             vertexBuffer[triIndex + 2] = vertIndex + 1;
    190.             // (3) 1---2 (5)
    191.             //      \ /
    192.             //   (1) 0
    193.             vertexBuffer[triIndex + 3] = vertIndex + 1;
    194.             vertexBuffer[triIndex + 4] = vertIndex + vertsPerLOD;
    195.             vertexBuffer[triIndex + 5] = vertIndex + vertsPerLOD + 2;
    196.             //        1 (5)
    197.             //       /|
    198.             //      / |
    199.             // (1) 0--2 (2)
    200.             vertexBuffer[triIndex + 6] = vertIndex + 1;
    201.             vertexBuffer[triIndex + 7] = vertIndex + vertsPerLOD + 2;
    202.             vertexBuffer[triIndex + 8] = vertIndex + 2;
    203.             //.............
    204.             triIndex = 27;
    205.             vertIndex = 6;
    206.             //     1 (3)
    207.             //     |\
    208.             //     | \
    209.             // (0) 0--2 (1)
    210.             vertexBuffer[triIndex] = vertIndex;
    211.             vertexBuffer[triIndex + 1] = vertIndex + vertsPerLOD;
    212.             vertexBuffer[triIndex + 2] = vertIndex + 1;
    213.             // (3) 1---2 (5)
    214.             //      \ /
    215.             //   (1) 0
    216.             vertexBuffer[triIndex + 3] = vertIndex + 1;
    217.             vertexBuffer[triIndex + 4] = vertIndex + vertsPerLOD;
    218.             vertexBuffer[triIndex + 5] = vertIndex + vertsPerLOD + 2;
    219.             //        1 (5)
    220.             //       /|
    221.             //      / |
    222.             // (1) 0--2 (2)
    223.             vertexBuffer[triIndex + 6] = vertIndex + 1;
    224.             vertexBuffer[triIndex + 7] = vertIndex + vertsPerLOD + 2;
    225.             vertexBuffer[triIndex + 8] = vertIndex + 2;
    226.             */
    227.          
    228.             //Pattern for the First Row
    229.             /*vertexBuffer[0] = 0;
    230.             vertexBuffer[1] = 9;
    231.             vertexBuffer[2] = 1;
    232.  
    233.             vertexBuffer[3] = 1;
    234.             vertexBuffer[4] = 9;
    235.             vertexBuffer[5] = 11;
    236.  
    237.             vertexBuffer[6] = 1;
    238.             vertexBuffer[7] = 11;
    239.             vertexBuffer[8] = 2;
    240.             //Start Next Column
    241.             vertexBuffer[9]  = 2;
    242.             vertexBuffer[10] = 11;
    243.             vertexBuffer[11] = 3;
    244.  
    245.             vertexBuffer[12] = 3;
    246.             vertexBuffer[13] = 11;
    247.             vertexBuffer[14] = 13;
    248.  
    249.             vertexBuffer[15] = 3;
    250.             vertexBuffer[16] = 13;
    251.             vertexBuffer[17] = 4;
    252.             //Start Next Column
    253.             vertexBuffer[18] = 4;
    254.             vertexBuffer[19] = 13;
    255.             vertexBuffer[20] = 5;
    256.  
    257.             vertexBuffer[21] = 5;
    258.             vertexBuffer[22] = 13;
    259.             vertexBuffer[23] = 15;
    260.  
    261.             vertexBuffer[24] = 5;
    262.             vertexBuffer[25] = 15;
    263.             vertexBuffer[26] = 6;
    264.             //Start Next Column
    265.             vertexBuffer[27] = 6;
    266.             vertexBuffer[28] = 15;
    267.             vertexBuffer[29] = 7;
    268.  
    269.             vertexBuffer[30] = 7;
    270.             vertexBuffer[31] = 15;
    271.             vertexBuffer[32] = 17;
    272.  
    273.             vertexBuffer[33] = 7;
    274.             vertexBuffer[34] = 17;
    275.             vertexBuffer[35] = 8;*/
    276.          
    277.             /*//Pattern for the Second Row
    278.             vertexBuffer[36] = 9;
    279.             vertexBuffer[37] = 18;
    280.             vertexBuffer[38] = 11;
    281.  
    282.             vertexBuffer[39] = 11;
    283.             vertexBuffer[40] = 18;
    284.             vertexBuffer[41] = 20;
    285.             //Start Next Column
    286.             vertexBuffer[42] = 11;
    287.             vertexBuffer[43] = 20;
    288.             vertexBuffer[44] = 13;
    289.  
    290.             vertexBuffer[45] = 13;
    291.             vertexBuffer[46] = 20;
    292.             vertexBuffer[47] = 22;
    293.             //Start Next Column
    294.             vertexBuffer[48] = 13;
    295.             vertexBuffer[49] = 22;
    296.             vertexBuffer[50] = 15;
    297.  
    298.             vertexBuffer[51] = 15;
    299.             vertexBuffer[52] = 22;
    300.             vertexBuffer[53] = 24;
    301.             //Start Next Column
    302.             vertexBuffer[54] = 15;
    303.             vertexBuffer[55] = 24;
    304.             vertexBuffer[56] = 17;
    305.  
    306.             vertexBuffer[57] = 17;
    307.             vertexBuffer[58] = 24;
    308.             vertexBuffer[59] = 26;
    309.             //Pattern for the Third and Fourth Row
    310.             vertexBuffer[60] = 18;
    311.             vertexBuffer[61] = 36;
    312.             vertexBuffer[62] = 20;
    313.  
    314.             vertexBuffer[63] = 20;
    315.             vertexBuffer[64] = 36;
    316.             vertexBuffer[65] = 38;
    317.             //Start Next Column
    318.             vertexBuffer[66] = 20;
    319.             vertexBuffer[67] = 38;
    320.             vertexBuffer[68] = 22;
    321.  
    322.             vertexBuffer[69] = 22;
    323.             vertexBuffer[70] = 38;
    324.             vertexBuffer[71] = 40;*/
    325.  
    326.  
    327.             /*for (int triIndex = 0, i = 0; i < vertBufferSize; i++)
    328.             {
    329.                 for (int j = 0; j < vertBufferSize; j++, triIndex += 15)
    330.                 {
    331.                     //  3
    332.                     //  |\
    333.                     //  | \
    334.                     //  0--1
    335.                     vertexBuffer[triIndex] = 0;
    336.                     vertexBuffer[triIndex + 1] = vertsPerChunk;
    337.                     vertexBuffer[triIndex + 2] = 1;
    338.                     //  3---- 5
    339.                     //   \   /
    340.                     //    \ /
    341.                     //     1
    342.                     vertexBuffer[triIndex + 3] = vertsPerChunk + 1;
    343.                     vertexBuffer[triIndex + 4] = vertsPerChunk;
    344.                     vertexBuffer[triIndex + 5] = vertsPerChunk + 2;
    345.                     //        5
    346.                     //       /|
    347.                     //      / |
    348.                     //     1--2
    349.                     vertexBuffer[triIndex + 6] = 1;
    350.                     vertexBuffer[triIndex + 7] = vertsPerChunk + 2;
    351.                     vertexBuffer[triIndex + 8] = 2;
    352.                     //  6
    353.                     //  |\
    354.                     //  | \
    355.                     //  3--5
    356.                     vertexBuffer[triIndex + 9] = vertsPerChunk;
    357.                     vertexBuffer[triIndex + 10] = vertsPerChunk * 2;
    358.                     vertexBuffer[triIndex + 11] = vertsPerChunk + 2;
    359.                     //  6--8
    360.                     //   \ |
    361.                     //    \|
    362.                     //     5
    363.                     vertexBuffer[triIndex + 12] = vertsPerChunk + 2;
    364.                     vertexBuffer[triIndex + 13] = vertsPerChunk * 2;
    365.                     vertexBuffer[triIndex + 14] = (vertsPerChunk * 2) + 2;
    366.                 }
    367.             }*/
    368.         }
    369.      
    370.         //Assign the Vertex Array from Generate Surface Coordinates to the Mesh Vertex Array
    371.         surfaceMesh.vertices = vertexArray;
    372.         //Assign the Vertex Buffer
    373.         surfaceMesh.triangles = vertexBuffer;
    374.         //Recalculate the Normals
    375.         surfaceMesh.RecalculateNormals();
    376.         //Recalculate Bounds
    377.         surfaceMesh.RecalculateBounds();
    378.         //After the Mesh has been created, pass it back to the MeshCollider
    379.         surface.GetComponent<MeshCollider>().sharedMesh = surfaceMesh;
    380.     }
    381. }
    382.  
     

    Attached Files:

    Last edited: Aug 30, 2016
  29. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Well...getting a little closer. Here's the code as it stands right now. I've managed to work out three levels of rows so far...I'd like to figure out a better way to do this for sure, because hand coding each row is kind of a pain and hurts the brain...I have a lot of grids drawn for various size LOD's....slowly getting there. The trick will then be to get everything to work dynamically, which i'm not even close to sure how that's going to work. I think overall I may be approaching this incorrectly. I need to experiment with a proper Quad Tree, where it actually figures out what cell the player is in and then runs the tessellation based on that...but, I'm still going to have to have all of this stuff worked out, so maybe I'm not really headed in the wrong direction either....*shrugs* who knows! Learnin' is happenin' and I'm having fun, so...there we go.

    Now, that being said, if anyone has a better idea for optimizing (I feel like this is highly inelegant and very bruteforce so far) please share! Ain't nobody gonna hurt my feelings, I'm still an extreme beginner here, so any and all help is appreciated!

    V/R

    Brad

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class LODTest : MonoBehaviour
    5. {
    6.     public int LOD = 1; //LOD follows: 1, 3, 5, 7, 9, 11, 13, 15, etc..
    7.     public int chunkSize = 1000; //m
    8.  
    9.     private int vertsPerChunk = 3; //The base number of vertices per terrain chunk
    10.  
    11.     void Start()
    12.     {
    13.         CreateSubDivisionSurfaces();
    14.     }
    15.  
    16.     void CreateSubDivisionSurfaces()
    17.     {
    18.         //For each each Sub Division Surface, Create a new Surface
    19.         /*for (int i = 0; i < subDivisions; i++)
    20.         {
    21.             for (int j = 0; j < subDivisions; j++)
    22.             {
    23.                 GenerateSurfaceCoordinates(j, i);
    24.             }
    25.         }*/
    26.  
    27.         GenerateSurfaceCoordinates(LOD, 0, 0);
    28.  
    29.         GenerateSurfaceCoordinates(LOD, 0, 1);
    30.     }
    31.  
    32.     void GenerateSurfaceCoordinates(int LOD, int xIndex, int zIndex)
    33.     {
    34.         //Appropriately size (and create) the Vertex Array
    35.         int vertsPerLOD = vertsPerChunk * LOD;
    36.         Vector3[] vertices = new Vector3[vertsPerLOD * vertsPerLOD];
    37.         //Calculate the increment to keep the vertices constrained to chunkSize
    38.         float increment = chunkSize / ((float)vertsPerLOD - 1);
    39.        
    40.         for (int i = 0, index = 0; i < vertsPerLOD; i++)
    41.         {
    42.             for (int j = 0; j < vertsPerLOD; j++, index++)
    43.             {
    44.                 //Vertex Coordinates
    45.                 float xPos = (float)j * increment - (chunkSize / 2.0f) + (xIndex * chunkSize);
    46.                 float yPos = 0.0f;
    47.                 float zPos = (float)i * increment - (chunkSize / 2.0f) + (zIndex * chunkSize);
    48.  
    49.                 //Set the Vertex positions based on the coordinates generated above
    50.                 vertices[index] = new Vector3(xPos, yPos, zPos);
    51.             }
    52.         }
    53.  
    54.         CreateSurfaceObject(LOD, vertices, xIndex, zIndex);
    55.     }
    56.  
    57.     void CreateSurfaceObject(int LOD, Vector3[] vertexArray, int xIndex, int zIndex)
    58.     {
    59.         //Create a new GameObject
    60.         GameObject surface = new GameObject("Surface(" + xIndex + ", " + zIndex + ")");
    61.         surface.tag = gameObject.tag;
    62.         surface.layer = gameObject.layer;
    63.         surface.transform.parent = transform;
    64.         surface.transform.position = transform.position;
    65.         //Add the MeshRenderer
    66.         surface.gameObject.AddComponent<MeshRenderer>();
    67.         //Add the MeshCollider
    68.         surface.gameObject.AddComponent<MeshCollider>();
    69.         //Add the MeshFilter
    70.         surface.gameObject.AddComponent<MeshFilter>();
    71.  
    72.         //Create the Mesh
    73.         CreateSurfaceMesh(LOD, surface, vertexArray, xIndex, zIndex);
    74.     }
    75.    
    76.     void CreateSurfaceMesh(int LOD, GameObject surface, Vector3[] vertexArray, int xIndex, int zIndex)
    77.     {
    78.         int triIndex = 0;
    79.         int vertIndex = 0;
    80.  
    81.         //Create and size the Vertex Buffer
    82.         int vertsPerLOD = vertsPerChunk * LOD;
    83.         int vertBufferSize = vertsPerLOD - 1;
    84.         int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    85.         //Create a new Mesh object
    86.         Mesh surfaceMesh;
    87.         //Create a new Mesh using the Objects MeshFilter
    88.         surfaceMesh = surface.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    89.         surfaceMesh.name = surface.name;
    90.  
    91.         if (xIndex == 0 && zIndex == 0)
    92.         {
    93.             //Step through the Vertex Buffer
    94.             for (int i = 0; i < vertBufferSize; i++, vertIndex++)
    95.             {
    96.                 for (int j = 0; j < vertBufferSize; j++, triIndex += 6, vertIndex++)
    97.                 {
    98.                     //  6---7---8
    99.                     //  |   |   |
    100.                     //  3---4---5
    101.                     //  |   |   |
    102.                     //  0---1---2
    103.                     //
    104.                     //  LOD 1 is represented by a 3x3 vertice grid.
    105.  
    106.                     // (3) 1
    107.                     //     |\
    108.                     //     | \
    109.                     // (0) 0--2 (1)
    110.                     vertexBuffer[triIndex]      = vertIndex;
    111.                     vertexBuffer[triIndex + 1]  = vertIndex + vertsPerLOD;
    112.                     vertexBuffer[triIndex + 2]  = vertIndex + 1;
    113.                     // (3) 1--2 (4)
    114.                     //      \ |
    115.                     //       \|
    116.                     //        0 (1)
    117.                     vertexBuffer[triIndex + 3]  = vertIndex + 1;
    118.                     vertexBuffer[triIndex + 4]  = vertIndex + vertsPerLOD;
    119.                     vertexBuffer[triIndex + 5]  = vertIndex + vertsPerLOD + 1;
    120.                 }
    121.             }
    122.         }
    123.  
    124.         if (xIndex == 0 && zIndex == 1)
    125.         {
    126.             Debug.Log("triIdex = " + triIndex);
    127.             Debug.Log("vertIndex = " + vertIndex);
    128.  
    129.             //For the first row...
    130.             for (int i = 0; i < (vertsPerLOD / 2); i++, triIndex += 9, vertIndex += 2) //LOD works...sort of...only if i <= LOD does it do what it's supposed to do...
    131.             {
    132.                 //  6---7---8
    133.                 //  |   |   |
    134.                 //  3---4---5
    135.                 //  |   |   |
    136.                 //  0---1---2
    137.                 //
    138.                 //  LOD 1 is represented by a 3x3 vertice grid. For a Surface that is 'north' of the origin
    139.                 //  the below pattern is used.
    140.  
    141.                 //     1 (3)
    142.                 //     |\
    143.                 //     | \
    144.                 // (0) 0--2 (1)
    145.                 vertexBuffer[triIndex]     = vertIndex;
    146.                 vertexBuffer[triIndex + 1] = vertIndex + vertsPerLOD;
    147.                 vertexBuffer[triIndex + 2] = vertIndex + 1;
    148.                 // (3) 1---2 (5)
    149.                 //      \ /
    150.                 //   (1) 0
    151.                 vertexBuffer[triIndex + 3] = vertIndex + 1;
    152.                 vertexBuffer[triIndex + 4] = vertIndex + vertsPerLOD;
    153.                 vertexBuffer[triIndex + 5] = vertIndex + vertsPerLOD + 2;
    154.                 //        1 (5)
    155.                 //       /|
    156.                 //      / |
    157.                 // (1) 0--2 (2)
    158.                 vertexBuffer[triIndex + 6] = vertIndex + 1;
    159.                 vertexBuffer[triIndex + 7] = vertIndex + vertsPerLOD + 2;
    160.                 vertexBuffer[triIndex + 8] = vertIndex + 2;
    161.             }
    162.  
    163.             Debug.Log("triIdex = " + triIndex);
    164.             Debug.Log("vertIndex = " + vertIndex);
    165.            
    166.             //For the second row...
    167.             for (int i = 0; i < (vertsPerLOD / 2); i++, triIndex += 6, vertIndex += 2)
    168.             {
    169.                 //  6---7---8
    170.                 //  |   |   |
    171.                 //  3---4---5
    172.                 //  |   |   |
    173.                 //  0---1---2
    174.                 //
    175.                 //  LOD 1 is represented by a 3x3 vertice grid.
    176.  
    177.                 // (6) 1
    178.                 //     |\
    179.                 //     | \
    180.                 // (3) 0--2 (5)
    181.                 vertexBuffer[triIndex]      = vertIndex + 1;
    182.                 vertexBuffer[triIndex + 1]  = vertIndex + vertsPerLOD + 1;
    183.                 vertexBuffer[triIndex + 2]  = vertIndex + 3;
    184.                 // (6) 1--2 (8)
    185.                 //      \ |
    186.                 //       \|
    187.                 //        0 (5)
    188.                 vertexBuffer[triIndex + 3]  = vertIndex + vertsPerLOD + 1;
    189.                 vertexBuffer[triIndex + 4]  = vertIndex + vertsPerLOD + 3;
    190.                 vertexBuffer[triIndex + 5]  = vertIndex + 3;
    191.             }
    192.  
    193.             Debug.Log("triIdex = " + triIndex);
    194.             Debug.Log("vertIndex = " + vertIndex);
    195.            
    196.             //For the remaining rows...
    197.             for (int i = 0; i < vertsPerLOD; i++, triIndex += 6, vertIndex += 2)
    198.             {
    199.                 //  6---7---8
    200.                 //  |   |   |
    201.                 //  3---4---5
    202.                 //  |   |   |
    203.                 //  0---1---2
    204.                 //
    205.                 //  LOD 1 is represented by a 3x3 vertice grid.
    206.  
    207.                 // (6) 1
    208.                 //     |\
    209.                 //     | \
    210.                 // (3) 0--2 (5)
    211.                 vertexBuffer[triIndex] = vertIndex;
    212.                 vertexBuffer[triIndex + 1] = vertIndex + (vertsPerLOD * 2);
    213.                 vertexBuffer[triIndex + 2] = vertIndex + 2;
    214.                 // (6) 1--2 (8)
    215.                 //      \ |
    216.                 //       \|
    217.                 //        0 (5)
    218.                 vertexBuffer[triIndex + 3] = vertIndex + 2;
    219.                 vertexBuffer[triIndex + 4] = vertIndex + (vertsPerLOD * 2);
    220.                 vertexBuffer[triIndex + 5] = vertIndex + (vertsPerLOD * 2) + 2;
    221.             }
    222.  
    223.             /*    
    224.             //.............
    225.             triIndex = 9;
    226.             vertIndex = 2;
    227.             //     1 (3)
    228.             //     |\
    229.             //     | \
    230.             // (0) 0--2 (1)
    231.             vertexBuffer[triIndex] = vertIndex;
    232.             vertexBuffer[triIndex + 1] = vertIndex + vertsPerLOD;
    233.             vertexBuffer[triIndex + 2] = vertIndex + 1;
    234.             // (3) 1---2 (5)
    235.             //      \ /
    236.             //   (1) 0
    237.             vertexBuffer[triIndex + 3] = vertIndex + 1;
    238.             vertexBuffer[triIndex + 4] = vertIndex + vertsPerLOD;
    239.             vertexBuffer[triIndex + 5] = vertIndex + vertsPerLOD + 2;
    240.             //        1 (5)
    241.             //       /|
    242.             //      / |
    243.             // (1) 0--2 (2)
    244.             vertexBuffer[triIndex + 6] = vertIndex + 1;
    245.             vertexBuffer[triIndex + 7] = vertIndex + vertsPerLOD + 2;
    246.             vertexBuffer[triIndex + 8] = vertIndex + 2;
    247.             //.............
    248.             triIndex = 18;
    249.             vertIndex = 4;
    250.             //     1 (3)
    251.             //     |\
    252.             //     | \
    253.             // (0) 0--2 (1)
    254.             vertexBuffer[triIndex] = vertIndex;
    255.             vertexBuffer[triIndex + 1] = vertIndex + vertsPerLOD;
    256.             vertexBuffer[triIndex + 2] = vertIndex + 1;
    257.             // (3) 1---2 (5)
    258.             //      \ /
    259.             //   (1) 0
    260.             vertexBuffer[triIndex + 3] = vertIndex + 1;
    261.             vertexBuffer[triIndex + 4] = vertIndex + vertsPerLOD;
    262.             vertexBuffer[triIndex + 5] = vertIndex + vertsPerLOD + 2;
    263.             //        1 (5)
    264.             //       /|
    265.             //      / |
    266.             // (1) 0--2 (2)
    267.             vertexBuffer[triIndex + 6] = vertIndex + 1;
    268.             vertexBuffer[triIndex + 7] = vertIndex + vertsPerLOD + 2;
    269.             vertexBuffer[triIndex + 8] = vertIndex + 2;
    270.             //.............
    271.             triIndex = 27;
    272.             vertIndex = 6;
    273.             //     1 (3)
    274.             //     |\
    275.             //     | \
    276.             // (0) 0--2 (1)
    277.             vertexBuffer[triIndex] = vertIndex;
    278.             vertexBuffer[triIndex + 1] = vertIndex + vertsPerLOD;
    279.             vertexBuffer[triIndex + 2] = vertIndex + 1;
    280.             // (3) 1---2 (5)
    281.             //      \ /
    282.             //   (1) 0
    283.             vertexBuffer[triIndex + 3] = vertIndex + 1;
    284.             vertexBuffer[triIndex + 4] = vertIndex + vertsPerLOD;
    285.             vertexBuffer[triIndex + 5] = vertIndex + vertsPerLOD + 2;
    286.             //        1 (5)
    287.             //       /|
    288.             //      / |
    289.             // (1) 0--2 (2)
    290.             vertexBuffer[triIndex + 6] = vertIndex + 1;
    291.             vertexBuffer[triIndex + 7] = vertIndex + vertsPerLOD + 2;
    292.             vertexBuffer[triIndex + 8] = vertIndex + 2;
    293.             */
    294.            
    295.             //Pattern for the First Row
    296.             /*vertexBuffer[0] = 0;
    297.             vertexBuffer[1] = 9;
    298.             vertexBuffer[2] = 1;
    299.  
    300.             vertexBuffer[3] = 1;
    301.             vertexBuffer[4] = 9;
    302.             vertexBuffer[5] = 11;
    303.  
    304.             vertexBuffer[6] = 1;
    305.             vertexBuffer[7] = 11;
    306.             vertexBuffer[8] = 2;
    307.             //Start Next Column
    308.             vertexBuffer[9]  = 2;
    309.             vertexBuffer[10] = 11;
    310.             vertexBuffer[11] = 3;
    311.  
    312.             vertexBuffer[12] = 3;
    313.             vertexBuffer[13] = 11;
    314.             vertexBuffer[14] = 13;
    315.  
    316.             vertexBuffer[15] = 3;
    317.             vertexBuffer[16] = 13;
    318.             vertexBuffer[17] = 4;
    319.             //Start Next Column
    320.             vertexBuffer[18] = 4;
    321.             vertexBuffer[19] = 13;
    322.             vertexBuffer[20] = 5;
    323.  
    324.             vertexBuffer[21] = 5;
    325.             vertexBuffer[22] = 13;
    326.             vertexBuffer[23] = 15;
    327.  
    328.             vertexBuffer[24] = 5;
    329.             vertexBuffer[25] = 15;
    330.             vertexBuffer[26] = 6;
    331.             //Start Next Column
    332.             vertexBuffer[27] = 6;
    333.             vertexBuffer[28] = 15;
    334.             vertexBuffer[29] = 7;
    335.  
    336.             vertexBuffer[30] = 7;
    337.             vertexBuffer[31] = 15;
    338.             vertexBuffer[32] = 17;
    339.  
    340.             vertexBuffer[33] = 7;
    341.             vertexBuffer[34] = 17;
    342.             vertexBuffer[35] = 8;*/
    343.            
    344.             /*//Pattern for the Second Row
    345.             vertexBuffer[36] = 9;
    346.             vertexBuffer[37] = 18;
    347.             vertexBuffer[38] = 11;
    348.  
    349.             vertexBuffer[39] = 11;
    350.             vertexBuffer[40] = 18;
    351.             vertexBuffer[41] = 20;
    352.             //Start Next Column
    353.             vertexBuffer[42] = 11;
    354.             vertexBuffer[43] = 20;
    355.             vertexBuffer[44] = 13;
    356.  
    357.             vertexBuffer[45] = 13;
    358.             vertexBuffer[46] = 20;
    359.             vertexBuffer[47] = 22;
    360.             //Start Next Column
    361.             vertexBuffer[48] = 13;
    362.             vertexBuffer[49] = 22;
    363.             vertexBuffer[50] = 15;
    364.  
    365.             vertexBuffer[51] = 15;
    366.             vertexBuffer[52] = 22;
    367.             vertexBuffer[53] = 24;
    368.             //Start Next Column
    369.             vertexBuffer[54] = 15;
    370.             vertexBuffer[55] = 24;
    371.             vertexBuffer[56] = 17;
    372.  
    373.             vertexBuffer[57] = 17;
    374.             vertexBuffer[58] = 24;
    375.             vertexBuffer[59] = 26;
    376.             //Pattern for the Third and Fourth Row
    377.             vertexBuffer[60] = 18;
    378.             vertexBuffer[61] = 36;
    379.             vertexBuffer[62] = 20;
    380.  
    381.             vertexBuffer[63] = 20;
    382.             vertexBuffer[64] = 36;
    383.             vertexBuffer[65] = 38;
    384.             //Start Next Column
    385.             vertexBuffer[66] = 20;
    386.             vertexBuffer[67] = 38;
    387.             vertexBuffer[68] = 22;
    388.  
    389.             vertexBuffer[69] = 22;
    390.             vertexBuffer[70] = 38;
    391.             vertexBuffer[71] = 40;*/
    392.  
    393.  
    394.             /*for (int triIndex = 0, i = 0; i < vertBufferSize; i++)
    395.             {
    396.                 for (int j = 0; j < vertBufferSize; j++, triIndex += 15)
    397.                 {
    398.                     //  3
    399.                     //  |\
    400.                     //  | \
    401.                     //  0--1
    402.                     vertexBuffer[triIndex] = 0;
    403.                     vertexBuffer[triIndex + 1] = vertsPerChunk;
    404.                     vertexBuffer[triIndex + 2] = 1;
    405.                     //  3---- 5
    406.                     //   \   /
    407.                     //    \ /
    408.                     //     1
    409.                     vertexBuffer[triIndex + 3] = vertsPerChunk + 1;
    410.                     vertexBuffer[triIndex + 4] = vertsPerChunk;
    411.                     vertexBuffer[triIndex + 5] = vertsPerChunk + 2;
    412.                     //        5
    413.                     //       /|
    414.                     //      / |
    415.                     //     1--2
    416.                     vertexBuffer[triIndex + 6] = 1;
    417.                     vertexBuffer[triIndex + 7] = vertsPerChunk + 2;
    418.                     vertexBuffer[triIndex + 8] = 2;
    419.                     //  6
    420.                     //  |\
    421.                     //  | \
    422.                     //  3--5
    423.                     vertexBuffer[triIndex + 9] = vertsPerChunk;
    424.                     vertexBuffer[triIndex + 10] = vertsPerChunk * 2;
    425.                     vertexBuffer[triIndex + 11] = vertsPerChunk + 2;
    426.                     //  6--8
    427.                     //   \ |
    428.                     //    \|
    429.                     //     5
    430.                     vertexBuffer[triIndex + 12] = vertsPerChunk + 2;
    431.                     vertexBuffer[triIndex + 13] = vertsPerChunk * 2;
    432.                     vertexBuffer[triIndex + 14] = (vertsPerChunk * 2) + 2;
    433.                 }
    434.             }*/
    435.         }
    436.        
    437.         //Assign the Vertex Array from Generate Surface Coordinates to the Mesh Vertex Array
    438.         surfaceMesh.vertices = vertexArray;
    439.         //Assign the Vertex Buffer
    440.         surfaceMesh.triangles = vertexBuffer;
    441.         //Recalculate the Normals
    442.         surfaceMesh.RecalculateNormals();
    443.         //Recalculate Bounds
    444.         surfaceMesh.RecalculateBounds();
    445.         //After the Mesh has been created, pass it back to the MeshCollider
    446.         surface.GetComponent<MeshCollider>().sharedMesh = surfaceMesh;
    447.     }
    448. }
    449.  
     

    Attached Files:

  30. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    Well I must admit I haven't read the entire thread. Whats a short synopsis of what this code should be doing that your trying to optimize. What does LOD stand for? It looks like your just generating a list of faces, then chopping those faces up into 2 triangles each? VertexBuffer in CreateSurfaceMesh is suppose to hold the indices of these triangles?
    Also why are you caring if x=0&& z==0 is a different case than x==0 and z==1?

    I recently have been writing some Geometry Generator code for a DirectX11 engine I have. I've gotten a pretty good handle on creating vertex and index buffers (Note: Most people would call your VertexBuffer an IndexBuffer since it stores indices of your Vertices for the triangles.).

    I was also curious why you are offsetting your entire grid by -1/2 * chunksize. basically chunk zero (x=0,z=0) will start at -500,-500. chunk 1 (x=0,z=1) will be at -500,0
     
  31. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    I'm creating a procedural terrain structure, that's the short answer. I would think if you've been working on terrain generation yourself it'd be obvious why I care about special cases like (0, 1) or (-1, 1) and the like: Tears or Seams.

    What I'm coding (which this part isn't terribly difficult, just time consuming) is how the terrain will be subdivided outside of the central player area. This is why the terrain is offset like it is. The assumption is that you want the terrain to look the best in area with the player at the origin and then the detail drops off with distance.

    Attached is an image of what I'm trying to do as well as a link to the KSP article the inspired the work I'm doing:

    http://kerbalspace.tumblr.com/post/9056986834/on-quadtrees-and-why-they-are-awesome

    Ultimately I can subdivide easy enough, but then the edges from one terrain tile don't match up with the edges of another and you get tears/seams in the terrain as an example there's another image from the web that shows what happens when two different LOD tiles are next to each other. I'm developing a system that will eliminate that.

    The method I'm developing will require the tiles surrounding the origin tile to know their locations (-1, 1), (1, 1) etc...so that they can use the appropriate function to draw the triangles and avoid the tears/seams.
     

    Attached Files:

  32. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    So as I understand it: You getting a large grid of terrain and creating some verticices/triangles. Then the next chunk would be right next to it and so on. This would work perfectly fine if you never applied a height map. However, whenever you do the far right edge of chunk one, might not line up perfectly with the far left edge of chunk 2, and you get those tears.

    I don't know if it this makes sense but I would create a generic form of CreateSurfaceMesh that works the same on all chunks. Then I would write a function called FixSeam(chunk1,chunk2) that figures out where the seam is between chunk1 and 2, and just goes down those vertices and stitches them together. I think this would make the code much easier to write and read. I believe in the long run that system will make it so you can extend to as many chunks as you want, with whatever deatail you want. It will also be much easier to write at high LOD levels.. Since if your doing say 100 vertex in a chunk 99% of them won't be on a seam so you generic CreateSurfaceMesh will create them just fine.. Then only that 1% on the edge needs to be fixed In FixSeam.

    Also could make the code more extendable to differnt kinds of chunk configurations. If you went with kerbals 6 Chunk system, your CreateSurfaceMesh woudln't change, you would just modify how FixSeam determines how chunk1 and chunk2 are connected
     
    Last edited: Aug 31, 2016
  33. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Yup. I stumbled across that after reading a little more into the QuadTree concept. So, essentially, the face of the cube will serve as the root surface. Using...probably a ray cast from the center of the world to the camera the system will determine what side of the face the player is on. From there, a 'subdivideSurface' function will be called up to the maximum depth allowed. The ray will be used to further isolate where on the face the player is located. These new subdivision surfaces will be held in different branches of the tree, each one consisting of 4 faces. The subdivisions will be allowed to continue up to a maximum level (to be determined still...arbitrarily I'm going to go with 8. altitude will also be a consideration too...don't really need the max of 8 subdivisions below the player if the player is 10000ft + above the surface...or it may be altitude driven entirely. Not sure yet.

    I was going about this the wrong way from the beginning. Granted, stitching the sides together using different index configurations is going to be required to avoid seams/tears, but I first have to subdivide the root to create the tree that is then used for the LOD. The keys are knowing where to start the subdivisions and then run them to the desired level

    If I use 3D Noise (which thanks to AlucardJ) the issues of seams not lining up are pretty much NIL. It gets trickier when you're trying to use a texture versus 3D noise, because now you have to project that point onto the flat image and it's a lot more challenging.
     
  34. SwagRogerCoolAid

    SwagRogerCoolAid

    Joined:
    Aug 17, 2016
    Posts:
    20
    I'm totally confused lol like are you making a procedural planet? if so, why not use Libnoise man? I'm not an expert at it but I managed to create this in the image attached. I did how ever have to generate out my own Icosphere. Nothing deforms and the mesh looks like it works once I generate it.



    My main issue at the moment is trying to get my terrain height level to be higher than my water level. Just lost on how I'd do this as I'm using a gradient I've only ever done regions with Color before. For my LOD as you can see in the image I have Low, Medium, High this changes the amount of perlin that is put onto my sphere. I should be able to use that as my LOD once I've got my chunks down (I haven't cut up my sphere yet)

    Or are you making a flat terrain? if so then Follow Sebs tutorial on Youtube. I only managed to get as far as I have watching his tutorials and converting it to work with LibNoise.
     
  35. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    I've already done what you're talking about, to include deformation of the planet surface. I'm re-writing it to have dynamic LOD.

    End goal, a procedurally generated Cube Sphere planet with Dynamic LOD and no tears/seams visible.
     
  36. SwagRogerCoolAid

    SwagRogerCoolAid

    Joined:
    Aug 17, 2016
    Posts:
    20
    Are you generating a cube and then upping the vertices an then normalizing them? as I have to normalize my vertices on my icosphere so the way i do it is...

    Grab Vertices, then apply noise, normalize them, apply more noise, and normalize the result.
    Don't know if that would help for all your seams. Can you post a picture of your planet so far...
     
  37. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Yes.

    I'm past that, already worked all of that out. I actually use the smoothVertex method from CatLikeCoding.

    The seams aren't an issue until you begin incorporating LOD...which is where i'm at. I've already generated the planet, applied noise to it, even generated textures versus using Vertex Colors (which produces a much better result in my opinion).

    I'm now solely focused on converting my planet which is static at the moment (i.e. it generates the exact same number of verts per cube face) over into an LOD system, so that as you fly closer to the planet it will up the vert count closest to the player and gradually lower the vert count farther away from the player.

    That's where I'm at. The planet code is good. If you grab the code from thread #12, you'll see my planet generator in action with a slight modification for noise by AlucardJ.

    Implementing a dynamic LOD system using a quad tree structure is what I'm not attempting to accomplish.
     
  38. SwagRogerCoolAid

    SwagRogerCoolAid

    Joined:
    Aug 17, 2016
    Posts:
    20
    Yeah I get the feeling man. I've tried almost everything I can to try break my gradient colors into values and then set my vertices height based on what key the gradient is. No luck. Think I'm going to call it quits soon because they isn't much reference to this type of stuff - gets boring going round in circles lol.

    Heck I even just tried doing a animation curve to see if I could do it that way. Didn't work.
    Goodluck though man, hopefully you sort it out soon.
     
  39. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Okay, so the more modular pre-quad tree version of the planet is complete. It's now three separate scripts and I'm posting it here for anyone following along. This basically takes me back to where I was minus textures/uvs/etc...(I'll get them back in later, the focus now is just the LOD system). Enjoy! (and I'm sure I can do a lot of this better/more efficiently too...)


    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public enum quadFace { TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK }
    5.  
    6. public class GeneratePlanet : MonoBehaviour
    7. {
    8.     public int LOD = 1;
    9.     public int RADIUS = 10; //m, will eventually be km
    10.  
    11.     private QuadSphere planet;
    12.  
    13.     void Start()
    14.     {
    15.         planet = new QuadSphere("Planet1", LOD, RADIUS);
    16.     }
    17.  
    18.     //This is meant for the Solar System Generator in the future...
    19.     /*public GeneratePlanet(string name, int lod, int radius)
    20.     {
    21.         planet = new QuadSphere(name, lod, radius);
    22.     }*/
    23. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class QuadSphere : MonoBehaviour
    5. {
    6.     public int baseSubdivisions = 8;
    7.  
    8.     GameObject      planet;
    9.     QuadSurface[]   surfaces;
    10.  
    11.  
    12.     public QuadSphere(string name, int lod, int radius)
    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.             surfaces[i] = new QuadSurface(lod, radius, (quadFace)i);
    21.         }
    22.  
    23.             planet = new GameObject(name);
    24.         for (int i = 0; i < surfaces.Length; i++)
    25.             surfaces[i].surface.transform.parent = planet.transform;
    26.     }
    27. }
    28.  
    29.  

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class QuadSurface
    5. {
    6.     private QuadSurface root;
    7.  
    8.     public GameObject surface { get; private set; }
    9.  
    10.     public QuadSurface(QuadSurface root, int lod, int radius, quadFace face)
    11.     {
    12.         GenerateSurfaceCoordinates(root, lod, radius, face);
    13.     }
    14.  
    15.     public QuadSurface(int lod, int radius, quadFace face)
    16.     {
    17.         GenerateSurfaceCoordinates(null, lod, radius, face);
    18.     }
    19.  
    20.     void GenerateSurfaceCoordinates(QuadSurface root, int lod, int radius, quadFace face)
    21.     {
    22.         //The base number of faces is 2, which means we need +1 vert to make that happen.
    23.         int vertsPerQuad = (int)Mathf.Pow(2, lod) + 1;
    24.  
    25.         Vector3[] vertices = new Vector3[vertsPerQuad * vertsPerQuad];
    26.  
    27.         //Calculate the increment to keep the vertices constrained to a 1x1 cube.
    28.         float increment = 1.0f / Mathf.Pow(2, lod);
    29.  
    30.         for (int i = 0, index = 0; i < vertsPerQuad; i++)
    31.         {
    32.             for (int j = 0; j < vertsPerQuad; j++, index++)
    33.             {
    34.                 float xPos = (float)j * increment - 0.5f;
    35.                 float yPos = 0.5f;
    36.                 float zPos = (float)i * increment - 0.5f;
    37.  
    38.                 switch (face)
    39.                 {
    40.                     case quadFace.TOP:
    41.                         //Assign Vertex Coordinates
    42.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    43.                         break;
    44.  
    45.                     case quadFace.BOTTOM:
    46.                         //Assign Vertex Coordinates
    47.                         vertices[index] = new Vector3(xPos, -yPos, -zPos);
    48.                         break;
    49.  
    50.                     case quadFace.LEFT:
    51.                         //Assign Vertex Coordinates
    52.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    53.                         break;
    54.  
    55.                     case quadFace.RIGHT:
    56.                         //Assign Vertex Coordinates
    57.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    58.                         break;
    59.  
    60.                     case quadFace.FRONT:
    61.                         //Assign Vertex Coordinates
    62.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    63.                         break;
    64.  
    65.                     case quadFace.BACK:
    66.                         //Assign Vertex Coordinates
    67.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    68.                         break;
    69.                 }
    70.  
    71.                 //This is where we spherify!
    72.                 vertices[index] = SmoothVertices(vertices[index]);
    73.  
    74.                 vertices[index] *= radius;
    75.  
    76.             }
    77.         }
    78.         //Create the Surface Object
    79.         CreateSurfaceObject(root, face, vertsPerQuad, vertices);
    80.     }
    81.  
    82.     void CreateSurfaceObject(QuadSurface root, quadFace face, int vertsPerQuad, Vector3[] vertexArray)
    83.     {
    84.         this.root = root;
    85.  
    86.         surface = new GameObject(face + " FACE");
    87.         //Add the mesh Renderer
    88.         surface.gameObject.AddComponent<MeshRenderer>();
    89.         //Add the MeshCollider
    90.         surface.gameObject.AddComponent<MeshCollider>();
    91.         //Add the MeshFilter
    92.         surface.gameObject.AddComponent<MeshFilter>();
    93.  
    94.         //Initialize the Renderer
    95.         surface.GetComponent<Renderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    96.         surface.GetComponent<Renderer>().receiveShadows = true;
    97.         surface.GetComponent<Renderer>().enabled = true;
    98.  
    99.         if (root != null)
    100.             surface.transform.parent = root.surface.transform;
    101.  
    102.         CreateSurfaceMesh(face, vertsPerQuad, vertexArray);
    103.     }
    104.  
    105.     void CreateSurfaceMesh(quadFace face, int vertsPerQuad, Vector3[] vertexArray)
    106.     {
    107.         int vertBufferSize = vertsPerQuad - 1;
    108.         //Size the vertex buffer
    109.         int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    110.         //Create a new Mesh object
    111.         Mesh surfaceMesh;
    112.         //Create a new Mesh using the Objects MeshFilter
    113.         surfaceMesh = surface.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    114.         surfaceMesh.name = surface.name;
    115.  
    116.         //Step through the Vertex Buffer
    117.         for (int triIndex = 0, vertIndex = 0, i = 0; i < vertBufferSize; i++, vertIndex++)
    118.         {
    119.             for (int j = 0; j < vertBufferSize; j++, triIndex += 6, vertIndex++)
    120.             {
    121.                 //  1
    122.                 //  | \
    123.                 //  |  \
    124.                 //  |   \
    125.                 //  0----2
    126.                 vertexBuffer[triIndex] = vertIndex;
    127.                 vertexBuffer[triIndex + 1] = vertIndex + vertsPerQuad;
    128.                 vertexBuffer[triIndex + 2] = vertIndex + 1;
    129.                 //  1----3
    130.                 //    \  |
    131.                 //     \ |
    132.                 //      \|
    133.                 //       2
    134.                 vertexBuffer[triIndex + 3] = vertIndex + vertsPerQuad;
    135.                 vertexBuffer[triIndex + 4] = vertIndex + vertsPerQuad + 1;
    136.                 vertexBuffer[triIndex + 5] = vertIndex + 1;
    137.             }
    138.         }
    139.         //Assign the Vertex Array from Generate Surface Coordinates to the Mesh Vertex Array
    140.         surfaceMesh.vertices = vertexArray;
    141.         //Assign the Vertex Buffer
    142.         surfaceMesh.triangles = vertexBuffer;
    143.         //Recalculate the Normals
    144.         surfaceMesh.RecalculateNormals();
    145.         //Recalculate Bounds
    146.         surfaceMesh.RecalculateBounds();
    147.         //After the Mesh has been created, pass it back to the MeshCollider
    148.         surface.GetComponent<MeshCollider>().sharedMesh = surfaceMesh;
    149.     }
    150.    
    151.     //From Catlike Coding!
    152.     public Vector3 SmoothVertices(Vector3 vertex)
    153.     {
    154.         Vector3 v = vertex * 2f;
    155.         float x2 = v.x * v.x;
    156.         float y2 = v.y * v.y;
    157.         float z2 = v.z * v.z;
    158.  
    159.         Vector3 s;
    160.         s.x = v.x * Mathf.Sqrt(1f - y2 / 2f - z2 / 2f + y2 * z2 / 3f);
    161.         s.y = v.y * Mathf.Sqrt(1f - x2 / 2f - z2 / 2f + x2 * z2 / 3f);
    162.         s.z = v.z * Mathf.Sqrt(1f - x2 / 2f - y2 / 2f + x2 * y2 / 3f);
    163.  
    164.         return s;
    165.     }
    166. }
    167.  
     
  40. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    I'm actually surprised that code works because you're creating a MonoBehaviour instance via a constructor. I'd suggest getting rid of that and just using a factory-style create method

    Code (csharp):
    1.  
    2. public static QuadSphere Create(string planetName, int lod, int radius)
    3. {
    4.     GameObject go = new GameObject(planetName);
    5.     QuadSphere qs = go.AddComponent<QuadSphere>();
    6.     // set up surfaces
    7.     return qs;
    8. }
    9.  
     
    Kurt-Dekker likes this.
  41. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    KelsoMRK, not sure...works like a champ though.

    Okay, so I plugged texture generation back into this rig and also terrain deformation. Only problem now is that the textures and the height adjustments don't match...even though their both calling the same utility function...well, maybe it is, I just didn't have the gradient set correctly....here's the updated code and a screen shot showing what's happening.

    Now to get the LOD system started and working!

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public enum quadFace { TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK }
    5.  
    6. public class GeneratePlanet : MonoBehaviour
    7. {
    8.     [Header("Planet Info")]
    9.     [Range(3.0f, 7.0f)] public int   LOD = 3;
    10.     [Range(0.0f, 1.0f)] public float WATERLEVEL = 0.5f;
    11.     public float PLANETRADIUS = 10; //m, will eventually be km
    12.     public float WORLDHEIGHT = 0.65f;
    13.     [Header("Texture and Noise Variables")]
    14.     public int   TEXTURESIZE = 256;
    15.     public float NOISESCALE = 1.45f;
    16.     public float NOISEOFFSET = 1.56f;
    17.     public int   OCTAVES = 8;
    18.     [Header("Planet Colors")]
    19.     public Gradient PLANETCOLORS;
    20.  
    21.     //The planet!
    22.     private QuadSphere planet;
    23.     //Textures!
    24.     private TextureGenerator textureGenerator;
    25.     //Will eventually be made private...need to see what's going on for now...
    26.     public Texture2D[] textures;
    27.  
    28.     void Start()
    29.     {
    30.         //PLANETCOLORS = PlanetUtilities.CreateColourGradient(PLANETCOLORS);
    31.         //Generate textures!
    32.         textureGenerator = new TextureGenerator(TEXTURESIZE, NOISESCALE, NOISEOFFSET, OCTAVES, 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.     //This is meant for the Solar System Generator in the future...
    40.     /*public GeneratePlanet(string name, int lod, int radius)
    41.     {
    42.         planet = new QuadSphere(name, lod, radius);
    43.     }*/
    44. }
    45.  

    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.         List<Color> colorList = new List<Color>();
    35.         Color color;
    36.  
    37.         Vector3[] vertices = new Vector3[textureSize * textureSize];
    38.  
    39.         // Calculate the increment to keep the vertices constrained to a 1x1 cube.
    40.         float increment = 1.0f / ((float)textureSize - 1);
    41.  
    42.         for (int i = 0, index = 0; i < textureSize; i++)
    43.         {
    44.             for (int j = 0; j < textureSize; j++, index++)
    45.             {
    46.                 // Vertex Coordinates
    47.                 float xPos = (float)j * increment - 0.5f;
    48.                 float yPos = 0.5f;
    49.                 float zPos = (float)i * increment - 0.5f;
    50.  
    51.                 // Assign Vertex Coordinates
    52.                 switch (face)
    53.                 {
    54.                     case quadFace.TOP:
    55.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    56.                         break;
    57.  
    58.                     case quadFace.BOTTOM:
    59.                         vertices[index] = new Vector3(-xPos, -yPos, zPos);
    60.                         break;
    61.  
    62.                     case quadFace.LEFT:
    63.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    64.                         break;
    65.  
    66.                     case quadFace.RIGHT:
    67.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    68.                         break;
    69.  
    70.                     case quadFace.FRONT:
    71.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    72.                         break;
    73.  
    74.                     case quadFace.BACK:
    75.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    76.                         break;
    77.                 }
    78.  
    79.                 //Spherify the Vertices!
    80.                 vertices[index] = PlanetUtilities.SmoothVertices(vertices[index]);
    81.  
    82.                 //Get noise!
    83.                 float noise = PlanetUtilities.GetNoise(vertices[index], noiseOffset, octaves, noiseScale);
    84.  
    85.                 // get Colour
    86.                 /*if (useGreyscale)
    87.                 {
    88.                     colour = new Color(noise, noise, noise, 1f);
    89.                 }
    90.                 else
    91.                 {
    92.                     colour = planetColours.Evaluate(noise);
    93.                 }*/
    94.  
    95.                 color = planetColors.Evaluate(noise);
    96.  
    97.                 // Add Colour to Texture Colour Array
    98.                 colorList.Add(color);
    99.             }
    100.         }
    101.  
    102.         // Apply Texture Colour Array to the Texture
    103.         texture.SetPixels(colorList.ToArray());
    104.         texture.wrapMode = TextureWrapMode.Clamp;
    105.         texture.Apply();
    106.  
    107.         surfaceTextures[(int)face] = texture;
    108.     }
    109. }
    110.  

    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.             surfaces[i] = new QuadSurface(lod, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, (quadFace)i);
    18.         }
    19.  
    20.             planet = new GameObject(name);
    21.         for (int i = 0; i < surfaces.Length; i++)
    22.             surfaces[i].surface.transform.parent = planet.transform;
    23.     }
    24. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class QuadSurface
    5. {
    6.     private QuadSurface root;
    7.     public GameObject surface { get; private set; }
    8.  
    9.     public QuadSurface(QuadSurface root, int lod, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    10.     {
    11.         GenerateSurfaceCoordinates(root, lod, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, face);
    12.     }
    13.  
    14.     public QuadSurface(int lod, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    15.     {
    16.         GenerateSurfaceCoordinates(null, lod, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, face);
    17.     }
    18.  
    19.     void GenerateSurfaceCoordinates(QuadSurface root, int lod, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    20.     {
    21.         //The base number of faces is 2, which means we need +1 vert to make that happen.
    22.         int vertsPerQuad = (int)Mathf.Pow(2, lod) + 1;
    23.  
    24.         //Appropriately size (and create) the Vertex Array
    25.         Vector3[] vertices = new Vector3[vertsPerQuad * vertsPerQuad];
    26.         //Appropriately size (and create) the UV Array
    27.         Vector2[] uvs = new Vector2[vertices.Length];
    28.  
    29.         //Calculate the increment to keep the vertices constrained to a 1x1 cube.
    30.         float increment     = 1.0f / Mathf.Pow(2, lod);
    31.         float uvIncrement   = 1.0f / Mathf.Pow(2, lod);
    32.  
    33.         for (int i = 0, index = 0; i < vertsPerQuad; i++)
    34.         {
    35.             for (int j = 0; j < vertsPerQuad; j++, index++)
    36.             {
    37.                 //Vertex Coordinates
    38.                 float xPos = (float)j * increment - 0.5f;
    39.                 float yPos = 0.5f;
    40.                 float zPos = (float)i * increment - 0.5f;
    41.                 //UV Coordinates
    42.                 float xUV = (float)j * uvIncrement;
    43.                 float zUV = (float)i * uvIncrement;
    44.  
    45.                 switch (face)
    46.                 {
    47.                     case quadFace.TOP:
    48.                         //Assign Vertex Coordinates
    49.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    50.                         //Assign UV Coordinates
    51.                         uvs[index] = new Vector2(xUV, zUV);
    52.                         break;
    53.  
    54.                     case quadFace.BOTTOM:
    55.                         //Assign Vertex Coordinates
    56.                         vertices[index] = new Vector3(xPos, -yPos, -zPos);
    57.                         //Assign UV Coordinates
    58.                         uvs[index] = new Vector2(xUV, zUV);
    59.                         break;
    60.  
    61.                     case quadFace.LEFT:
    62.                         //Assign Vertex Coordinates
    63.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    64.                         //Assign UV Coordinates
    65.                         uvs[index] = new Vector2(xUV, zUV);
    66.                         break;
    67.  
    68.                     case quadFace.RIGHT:
    69.                         //Assign Vertex Coordinates
    70.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    71.                         //Assign UV Coordinates
    72.                         uvs[index] = new Vector2(xUV, zUV);
    73.                         break;
    74.  
    75.                     case quadFace.FRONT:
    76.                         //Assign Vertex Coordinates
    77.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    78.                         //Assign UV Coordinates
    79.                         uvs[index] = new Vector2(xUV, zUV);
    80.                         break;
    81.  
    82.                     case quadFace.BACK:
    83.                         //Assign Vertex Coordinates
    84.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    85.                         //Assign UV Coordinates
    86.                         uvs[index] = new Vector2(xUV, zUV);
    87.                         break;
    88.                 }
    89.                 //Spherify the vertices!
    90.                 vertices[index] = PlanetUtilities.SmoothVertices(vertices[index]);
    91.  
    92.                 float noise = PlanetUtilities.GetNoise(vertices[index], noiseOffset, octaves, noiseScale);
    93.  
    94.                 //Keep the oceans flat! - thanks alucardj!
    95.                 if (noise > waterLevel)
    96.                     noise = (noise - waterLevel) * (1.0f / (1.0f - waterLevel));
    97.                 else
    98.                     noise = 0.0f;
    99.  
    100.                 vertices[index] *= radius + (noise * worldHeight);
    101.  
    102.             }
    103.         }
    104.         //Create the Surface Object
    105.         CreateSurfaceObject(root, textures, face, vertsPerQuad, vertices, uvs);
    106.     }
    107.  
    108.     void CreateSurfaceObject(QuadSurface root, Texture2D[] textures, quadFace face, int vertsPerQuad, Vector3[] vertexArray, Vector2[] uvArray)
    109.     {
    110.         this.root = root;
    111.  
    112.         surface = new GameObject(face + " FACE");
    113.         //Add the mesh Renderer
    114.         surface.gameObject.AddComponent<MeshRenderer>();
    115.         //Add the MeshCollider
    116.         surface.gameObject.AddComponent<MeshCollider>();
    117.         //Add the MeshFilter
    118.         surface.gameObject.AddComponent<MeshFilter>();
    119.  
    120.         //Initialize the Renderer
    121.         surface.GetComponent<Renderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    122.         surface.GetComponent<Renderer>().receiveShadows = true;
    123.         surface.GetComponent<Renderer>().enabled = true;
    124.  
    125.         //Assign the generated textures to the quadSphere
    126.         surface.GetComponent<Renderer>().material.mainTexture = textures[(int)face];
    127.  
    128.         if (root != null)
    129.             surface.transform.parent = root.surface.transform;
    130.  
    131.         CreateSurfaceMesh(face, vertsPerQuad, vertexArray, uvArray);
    132.     }
    133.  
    134.     void CreateSurfaceMesh(quadFace face, int vertsPerQuad, Vector3[] vertexArray, Vector2[] uvArray)
    135.     {
    136.         int vertBufferSize = vertsPerQuad - 1;
    137.         //Size the vertex buffer
    138.         int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    139.         //Create a new Mesh object
    140.         Mesh surfaceMesh;
    141.         //Create a new Mesh using the Objects MeshFilter
    142.         surfaceMesh = surface.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    143.         surfaceMesh.name = surface.name;
    144.  
    145.         //Step through the Vertex Buffer
    146.         for (int triIndex = 0, vertIndex = 0, i = 0; i < vertBufferSize; i++, vertIndex++)
    147.         {
    148.             for (int j = 0; j < vertBufferSize; j++, triIndex += 6, vertIndex++)
    149.             {
    150.                 //  1
    151.                 //  | \
    152.                 //  |  \
    153.                 //  |   \
    154.                 //  0----2
    155.                 vertexBuffer[triIndex] = vertIndex;
    156.                 vertexBuffer[triIndex + 1] = vertIndex + vertsPerQuad;
    157.                 vertexBuffer[triIndex + 2] = vertIndex + 1;
    158.                 //  1----3
    159.                 //    \  |
    160.                 //     \ |
    161.                 //      \|
    162.                 //       2
    163.                 vertexBuffer[triIndex + 3] = vertIndex + vertsPerQuad;
    164.                 vertexBuffer[triIndex + 4] = vertIndex + vertsPerQuad + 1;
    165.                 vertexBuffer[triIndex + 5] = vertIndex + 1;
    166.             }
    167.         }
    168.         //Assign the Vertex Array from Generate Surface Coordinates to the Mesh Vertex Array
    169.         surfaceMesh.vertices = vertexArray;
    170.         //Assign the UV Coordinates
    171.         surfaceMesh.uv = uvArray;
    172.         //Assign the Vertex Buffer
    173.         surfaceMesh.triangles = vertexBuffer;
    174.         //Recalculate the Normals
    175.         surfaceMesh.RecalculateNormals();
    176.         //Recalculate Bounds
    177.         surfaceMesh.RecalculateBounds();
    178.         //After the Mesh has been created, pass it back to the MeshCollider
    179.         surface.GetComponent<MeshCollider>().sharedMesh = surfaceMesh;
    180.     }
    181. }

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

    Attached Files:

  42. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    Well shucks, I almost feel like my thread was stolen from underneath me. This is cool though, gotta admit.
     
  43. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
  44. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
  45. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Ah, yeah. I've been working on this for a while now! Hope it helps on your endeavors. All of this stuff has been done before and I hate that there's no real 'this is a way to go about it' out there...So I post my stumbling attempts in the hopes it helps others out! And I learn stuff along the way too. I'd like to one day convert to a full voxel system, but that's a bit beyond me at the moment.
     
  46. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Final update for the night. Added some functionality for switching between vertex colored and texture mapped versions of the sphere...Currently the texture maps in no way line up with the vertex colored version...which is no good, because they should stay the same. Though, I guess in the end it's not a big deal because vertex coloring (which will eventually draw from textures) will be the way to go I think...still, would be nice if they both lined up just for the convenience of it.

    Updated code! Oh and the screenshot is of the vertex colored version...now LOD! (finally....)

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

    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.         List<Color> colorList = new List<Color>();
    35.         Color color;
    36.  
    37.         Vector3[] vertices = new Vector3[textureSize * textureSize];
    38.  
    39.         // Calculate the increment to keep the vertices constrained to a 1x1 cube.
    40.         float increment = 1.0f / ((float)textureSize - 1);
    41.  
    42.         for (int i = 0, index = 0; i < textureSize; i++)
    43.         {
    44.             for (int j = 0; j < textureSize; j++, index++)
    45.             {
    46.                 // Vertex Coordinates
    47.                 float xPos = (float)j * increment - 0.5f;
    48.                 float yPos = 0.5f;
    49.                 float zPos = (float)i * increment - 0.5f;
    50.  
    51.                 // Assign Vertex Coordinates
    52.                 switch (face)
    53.                 {
    54.                     case quadFace.TOP:
    55.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    56.                         break;
    57.  
    58.                     case quadFace.BOTTOM:
    59.                         vertices[index] = new Vector3(-xPos, -yPos, zPos);
    60.                         break;
    61.  
    62.                     case quadFace.LEFT:
    63.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    64.                         break;
    65.  
    66.                     case quadFace.RIGHT:
    67.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    68.                         break;
    69.  
    70.                     case quadFace.FRONT:
    71.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    72.                         break;
    73.  
    74.                     case quadFace.BACK:
    75.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    76.                         break;
    77.                 }
    78.  
    79.                 //Spherify the Vertices!
    80.                 vertices[index] = PlanetUtilities.SmoothVertices(vertices[index]);
    81.  
    82.                 //Get noise!
    83.                 float noise = PlanetUtilities.GetNoise(vertices[index], noiseOffset, octaves, noiseScale);
    84.  
    85.                 // get Colour
    86.                 /*if (useGreyscale)
    87.                 {
    88.                     colour = new Color(noise, noise, noise, 1f);
    89.                 }
    90.                 else
    91.                 {
    92.                     colour = planetColours.Evaluate(noise);
    93.                 }*/
    94.  
    95.                 color = planetColors.Evaluate(noise);
    96.  
    97.                 // Add Colour to Texture Colour Array
    98.                 colorList.Add(color);
    99.             }
    100.         }
    101.  
    102.         // Apply Texture Colour Array to the Texture
    103.         texture.SetPixels(colorList.ToArray());
    104.         texture.wrapMode = TextureWrapMode.Clamp;
    105.         texture.Apply();
    106.  
    107.         surfaceTextures[(int)face] = texture;
    108.     }
    109. }

    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.             surfaces[i] = new QuadSurface(lod, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, (quadFace)i);
    18.         }
    19.  
    20.             planet = new GameObject(name);
    21.         for (int i = 0; i < surfaces.Length; i++)
    22.             surfaces[i].surface.transform.parent = planet.transform;
    23.     }
    24. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class QuadSurface
    5. {
    6.     private QuadSurface root;
    7.     public GameObject surface { get; private set; }
    8.    
    9.     //Vertex Colors Testing
    10.     GameObject     planetGenerator;
    11.     GeneratePlanet generator;
    12.     Material       planetMaterial;
    13.     Color[]        planetColors;
    14.     Gradient       planetGradient;
    15.  
    16.     public QuadSurface(QuadSurface root, int lod, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    17.     {
    18.         //Vertex Colors Testing
    19.         planetGenerator = GameObject.Find("PlanetGenerator");
    20.         generator = planetGenerator.GetComponent<GeneratePlanet>();
    21.         planetMaterial = generator.vertexColors;
    22.         planetGradient = generator.PLANETCOLORS;
    23.  
    24.         GenerateSurfaceCoordinates(root, lod, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, face);
    25.     }
    26.  
    27.     public QuadSurface(int lod, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    28.     {
    29.         //Vertex Colors Testing
    30.         planetGenerator = GameObject.Find("PlanetGenerator");
    31.         generator = planetGenerator.GetComponent<GeneratePlanet>();
    32.         planetMaterial = generator.vertexColors;
    33.         planetGradient = generator.PLANETCOLORS;
    34.  
    35.         GenerateSurfaceCoordinates(null, lod, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, face);
    36.     }
    37.  
    38.     void GenerateSurfaceCoordinates(QuadSurface root, int lod, float waterLevel, float radius, float noiseOffset, float octaves, float noiseScale, float worldHeight, Texture2D[] textures, quadFace face)
    39.     {
    40.         //The base number of faces is 2, which means we need +1 vert to make that happen.
    41.         int vertsPerQuad = (int)Mathf.Pow(2, lod) + 1;
    42.  
    43.         //Appropriately size (and create) the Vertex Array
    44.         Vector3[] vertices = new Vector3[vertsPerQuad * vertsPerQuad];
    45.         //Appropriately size (and create) the UV Array
    46.         Vector2[] uvs = new Vector2[vertices.Length];
    47.         //Vertex Colors Testing
    48.         Color[] colors = new Color[vertices.Length];
    49.         planetColors = colors;
    50.  
    51.         //Calculate the increment to keep the vertices constrained to a 1x1 cube.
    52.         float increment     = 1.0f / Mathf.Pow(2, lod);
    53.         float uvIncrement   = 1.0f / Mathf.Pow(2, lod);
    54.  
    55.         for (int i = 0, index = 0; i < vertsPerQuad; i++)
    56.         {
    57.             for (int j = 0; j < vertsPerQuad; j++, index++)
    58.             {
    59.                 //Vertex Coordinates
    60.                 float xPos = (float)j * increment - 0.5f;
    61.                 float yPos = 0.5f;
    62.                 float zPos = (float)i * increment - 0.5f;
    63.                 //UV Coordinates
    64.                 float xUV = (float)j * uvIncrement;
    65.                 float zUV = (float)i * uvIncrement;
    66.  
    67.                 switch (face)
    68.                 {
    69.                     case quadFace.TOP:
    70.                         //Assign Vertex Coordinates
    71.                         vertices[index] = new Vector3(xPos, yPos, zPos);
    72.                         //Assign UV Coordinates
    73.                         uvs[index] = new Vector2(xUV, zUV);
    74.                         break;
    75.  
    76.                     case quadFace.BOTTOM:
    77.                         //Assign Vertex Coordinates
    78.                         vertices[index] = new Vector3(xPos, -yPos, -zPos);
    79.                         //Assign UV Coordinates
    80.                         uvs[index] = new Vector2(xUV, zUV);
    81.                         break;
    82.  
    83.                     case quadFace.LEFT:
    84.                         //Assign Vertex Coordinates
    85.                         vertices[index] = new Vector3(-yPos, zPos, -xPos);
    86.                         //Assign UV Coordinates
    87.                         uvs[index] = new Vector2(xUV, zUV);
    88.                         break;
    89.  
    90.                     case quadFace.RIGHT:
    91.                         //Assign Vertex Coordinates
    92.                         vertices[index] = new Vector3(yPos, zPos, xPos);
    93.                         //Assign UV Coordinates
    94.                         uvs[index] = new Vector2(xUV, zUV);
    95.                         break;
    96.  
    97.                     case quadFace.FRONT:
    98.                         //Assign Vertex Coordinates
    99.                         vertices[index] = new Vector3(-xPos, zPos, yPos);
    100.                         //Assign UV Coordinates
    101.                         uvs[index] = new Vector2(xUV, zUV);
    102.                         break;
    103.  
    104.                     case quadFace.BACK:
    105.                         //Assign Vertex Coordinates
    106.                         vertices[index] = new Vector3(xPos, zPos, -yPos);
    107.                         //Assign UV Coordinates
    108.                         uvs[index] = new Vector2(xUV, zUV);
    109.                         break;
    110.                 }
    111.                 //Spherify the vertices!
    112.                 vertices[index] = PlanetUtilities.SmoothVertices(vertices[index]);
    113.  
    114.                 float noise = PlanetUtilities.GetNoise(vertices[index], noiseOffset, octaves, noiseScale);
    115.                 //Vertex Colors Testing
    116.                 colors[index] = planetGradient.Evaluate(noise);
    117.                
    118.                 //Keep the oceans flat! - thanks alucardj!
    119.                 if (noise > waterLevel)
    120.                     noise = (noise - waterLevel) * (1.0f / (1.0f - waterLevel));
    121.                 else
    122.                     noise = 0.0f;
    123.  
    124.                 vertices[index] *= radius + (noise * worldHeight);
    125.             }
    126.         }
    127.         //Create the Surface Object
    128.         CreateSurfaceObject(root, textures, face, vertsPerQuad, vertices, uvs);
    129.     }
    130.  
    131.     void CreateSurfaceObject(QuadSurface root, Texture2D[] textures, quadFace face, int vertsPerQuad, Vector3[] vertexArray, Vector2[] uvArray)
    132.     {
    133.         this.root = root;
    134.  
    135.         surface = new GameObject(face + " FACE");
    136.         //Add the mesh Renderer
    137.         surface.gameObject.AddComponent<MeshRenderer>();
    138.         //Add the MeshCollider
    139.         surface.gameObject.AddComponent<MeshCollider>();
    140.         //Add the MeshFilter
    141.         surface.gameObject.AddComponent<MeshFilter>();
    142.  
    143.         //Initialize the Renderer
    144.         surface.GetComponent<Renderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
    145.         surface.GetComponent<Renderer>().receiveShadows = true;
    146.         surface.GetComponent<Renderer>().enabled = true;
    147.  
    148.         if (generator.USEVERTEXCOLORS == false)
    149.             //Assign the generated textures to the quadSphere
    150.             surface.GetComponent<Renderer>().material.mainTexture = textures[(int)face];
    151.         else
    152.             //Vertex Colors testing
    153.             surface.GetComponent<Renderer>().material = planetMaterial;
    154.  
    155.         if (root != null)
    156.             surface.transform.parent = root.surface.transform;
    157.  
    158.         CreateSurfaceMesh(face, vertsPerQuad, vertexArray, uvArray);
    159.     }
    160.  
    161.     void CreateSurfaceMesh(quadFace face, int vertsPerQuad, Vector3[] vertexArray, Vector2[] uvArray)
    162.     {
    163.         int vertBufferSize = vertsPerQuad - 1;
    164.         //Size the vertex buffer
    165.         int[] vertexBuffer = new int[(vertBufferSize * vertBufferSize) * 6];
    166.         //Create a new Mesh object
    167.         Mesh surfaceMesh;
    168.         //Create a new Mesh using the Objects MeshFilter
    169.         surfaceMesh = surface.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    170.         surfaceMesh.name = surface.name;
    171.  
    172.         //Step through the Vertex Buffer
    173.         for (int triIndex = 0, vertIndex = 0, i = 0; i < vertBufferSize; i++, vertIndex++)
    174.         {
    175.             for (int j = 0; j < vertBufferSize; j++, triIndex += 6, vertIndex++)
    176.             {
    177.                 //  1
    178.                 //  | \
    179.                 //  |  \
    180.                 //  |   \
    181.                 //  0----2
    182.                 vertexBuffer[triIndex] = vertIndex;
    183.                 vertexBuffer[triIndex + 1] = vertIndex + vertsPerQuad;
    184.                 vertexBuffer[triIndex + 2] = vertIndex + 1;
    185.                 //  1----3
    186.                 //    \  |
    187.                 //     \ |
    188.                 //      \|
    189.                 //       2
    190.                 vertexBuffer[triIndex + 3] = vertIndex + vertsPerQuad;
    191.                 vertexBuffer[triIndex + 4] = vertIndex + vertsPerQuad + 1;
    192.                 vertexBuffer[triIndex + 5] = vertIndex + 1;
    193.             }
    194.         }
    195.         //Assign the Vertex Array from Generate Surface Coordinates to the Mesh Vertex Array
    196.         surfaceMesh.vertices = vertexArray;
    197.         //Assign the UV Coordinates
    198.         surfaceMesh.uv = uvArray;
    199.         //Assign the Vertex Buffer
    200.         surfaceMesh.triangles = vertexBuffer;
    201.         //Vertex Colors Testing
    202.         surfaceMesh.colors = planetColors;
    203.         //Recalculate the Normals
    204.         surfaceMesh.RecalculateNormals();
    205.         //Recalculate Bounds
    206.         surfaceMesh.RecalculateBounds();
    207.         //After the Mesh has been created, pass it back to the MeshCollider
    208.         surface.GetComponent<MeshCollider>().sharedMesh = surfaceMesh;
    209.     }
    210. }

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

    Attached Files:

  47. ob103ninja

    ob103ninja

    Joined:
    Nov 19, 2014
    Posts:
    45
    I'd say I would be surprised that No Man's Sky doesn't have a source code, but they aren't exactly, well, open source. If you could get your hands on that code specifically you'd probably be in legal trouble LOL

    Good idea to just stick with what you've got for now though.
     
  48. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Yeah...I mean, I have an idea of how to do the voxel thing with cubes, but what I'd really like to implement is what's presented in GPU Gems 3, Chapter 1 http://http.developer.nvidia.com/GPUGems3/gpugems3_ch01.html

    I bought the book because I love hard bound books. My level of understanding so far is how to create cubes and randomly place them with noise, but not make them interact with one another. I think I'm doin' alright to be where I'm at now. Between CatLike coding and a host of other web resources (probably need to compile the list one of these days) I got to where I'm at. The biggest thing with it all is understanding it all. It's one thing to go running with some source code...another thing entirely to have ZERO understanding what it's doing and why, or the assumptions that went into the hows and why's.

    But, it's late and I'm off to bed. Will start tinkering with the LOD system tomorrow.
     
  49. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    Ok, found the problem with the textures. When you initialize the textures, you are sending the noise parameters in the wrong order :
    Code (csharp):
    1. // GENERATEPLANET says:
    2. textureGenerator = new TextureGenerator(TEXTURESIZE, NOISESCALE, NOISEOFFSET, OCTAVES, PLANETCOLORS);
    3. // TEXTUREGENERATOR expects:
    4. public TextureGenerator(int size, float offset, int oct, float scale, Gradient colors)
    So while you are refactoring, it might be a good idea to have all the constructor parameters in the same order.
    Note: octaves defines how many times a loop iterates, therefore it should only ever be an integer and be typecast as an int.

    I also found a mismatched case in TextureGenerator :
    Code (csharp):
    1. case quadFace.BOTTOM:
    2. //vertices[index] = new Vector3(-xPos,-yPos,zPos);
    3. vertices[index] = new Vector3(xPos,-yPos,-zPos); // this is the line in QuadSurface
    Note: consider manually calculating the normals. You can just normalize the vertex as the overall shape is a sphere. While not a perfect solution, it looks much better than autocalculated normals. You can even just add it in the QuadSurface CreateSurfaceMesh function, right before you assign the arrays to the mesh :
    Code (csharp):
    1. Vector3[] normals = new Vector3[ vertexArray.Length ];
    2. for ( int i = 0; i < vertexArray.Length; i++ )
    3. {
    4.    normals[ i ] = vertexArray[ i ].normalized;
    5. }
    6.  
    7. // then assign these to the mesh
    8. //Recalculate the Normals
    9. //surfaceMesh.RecalculateNormals();
    10. surfaceMesh.normals = normals;
    manualnormals.jpg
     
    BradMick likes this.
  50. BradMick

    BradMick

    Joined:
    Apr 2, 2014
    Posts:
    113
    Hah! Figured it was something simple and innocuous. I've been a mad man in this rewrite about standardizing the order of arguments...looks like I missed one! Gah...You're the man! Textures and Vertex coloring now BOTH work in this updated version.

    Also, I incorporated the Manual normals calc in this revision.

    There are a lot of changes for ease of use now too, particularly in QuadSurface.

    So, the LOD system is now in progress, and I've hit another small hiccup. The LOD code is working, but it's drawing stuff in the wrong spot at the higher levels of detail. The attached image shows what's happening. I suspect it's in how the quad surfaces are drawn, I think the issue is that it's not inheriting the parent objects positions which...I just need to experiment more. It's time for me to hop off here for a bit and let my brain rest.

    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.             surfaces[i] = new QuadSurface(lod, 0, 0, 0, waterLevel, radius, noiseOffset, octaves, noiseScale, worldHeight, textures, (quadFace)i);
    18.         }
    19.  
    20.         //Testing...
    21.         surfaces[0].SubDivide();
    22.         surfaces[0].tree[0].SubDivide();
    23.         surfaces[0].tree[0].tree[3].SubDivide();
    24.         //surfaces[0].tree[0].tree[0].tree[0].SubDivide();
    25.  
    26.  
    27.             planet = new GameObject(name);
    28.         for (int i = 0; i < surfaces.Length; i++)
    29.             surfaces[i].surface.transform.parent = planet.transform;
    30.     }
    31. }

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

    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