Search Unity

Help with weird positioning when creating 3D grid

Discussion in 'Scripting' started by mezkhalin, Oct 23, 2019.

  1. mezkhalin

    mezkhalin

    Joined:
    Dec 6, 2015
    Posts:
    11
    EDIT :: I've removed the attached files for now, as there's no interest in the thread at this time. Should anyone be interested in solving this and have a look at the code, let me know and I'll re-attach them.

    Hi there!
    What I'm doing, or trying to do, is I have a factory class, that creates spheres of different sizes and positions, based on an algorithm. Another debugger class is then responsible for creating a grid of octants, only creating one if it has any sphere inside it. This debugger finally collects the octants and creates 3D textures for each one. These textures are basically density fields calculated from the sphere data.

    Debugging only the sphere data is fine, as you can see here. It properly shows spheres, octants and properly highlights the associated octants for each sphere selected in the inspector. I only use one sphere here for debugging purposes.



    However, and this is where my problem lies, when I do the "render", calculating and displaying the 3D textures for each octant, some of them are mirrored, or rotated. Displaced either way.



    To make it even stranger, they're only flipped on the x- and z-axis. The y-axis seems fine?


    I've looked through the code to see if any coordinates or loops are screwed up, but everything seems fine from what I can tell. I could be wrong however and missed something. I'll post the relevant code here, as well as all the necessary files for trying it out.
    Some of the code is commented, but I'm happy to clear up any questions, should there be any. Thanks.


    Debugger/renderer

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEditor;
    4. using UnityEngine;
    5.  
    6. public struct OctantDescriptor
    7. {
    8.     public Vector3 Position;
    9.     public List<Sphere> Spheres;
    10.     public Material Mat;
    11.  
    12.     public OctantDescriptor (Vector3 pos, List<Sphere> spheres, Material mat)
    13.     {
    14.         Position = pos;
    15.         Spheres = spheres;
    16.         Mat = mat;
    17.     }
    18. }
    19.  
    20. [ExecuteInEditMode]
    21. public class SphereGridDebugger : MonoBehaviour
    22. {
    23.     public Shader VolumeShader;
    24.     public Mesh cube;
    25.  
    26.     [Space(15)]
    27.     public int NumSpheres = 100;
    28.     public float StartRad = 1f;
    29.     public float DiamFactor = .9f;
    30.     public float DiamExp = .9f;
    31.     public float SpreadAngle = 135f;
    32.     [Space(15)]
    33.     public bool DrawExtents = true;
    34.     public bool DrawSpheres = true;
    35.     public bool DrawGrid = true;
    36.     public int TargetSphere = 0;
    37.     public bool HighlightOctants = true;
    38.     [Space(15)]
    39.     public int NumRenders = 0;
    40.  
    41.     private SphereFactory factory;
    42.     private List<Sphere>[,,] grid;
    43.  
    44.     private static List<OctantDescriptor> RenderQueue;
    45.  
    46.     void Start()
    47.     {
    48.         Create();
    49.     }
    50.  
    51.     /// <summary>
    52.     /// Called in inspector
    53.     /// Creates the data for the density calculations
    54.     /// </summary>
    55.     public void Create ()
    56.     {
    57.         factory = new SphereFactory(NumSpheres, StartRad, DiamFactor, DiamExp, SpreadAngle);
    58.         factory.CreateCloud();
    59.         grid = factory.GetGrid();
    60.  
    61.         SceneView.RepaintAll();
    62.     }
    63.  
    64.     /// <summary>
    65.     /// Called in inspector
    66.     /// Collects all octants with spheres in them,
    67.     /// requests their density fields and builds a render queue
    68.     /// </summary>
    69.     public void Render()
    70.     {
    71.         RenderQueue = new List<OctantDescriptor>();
    72.  
    73.         // collect all octants with spheres
    74.         for (int x = 0; x < factory.Extents.x; x++)
    75.         {
    76.             for(int y = 0; y < factory.Extents.y; y++)
    77.             {
    78.                 for(int z = 0; z < factory.Extents.z; z++)
    79.                 {
    80.                     if (factory.Grid[x, y, z] != null)  // theres something here
    81.                     {
    82.                         // set up material and the density texture
    83.                         Vector3 pos = new Vector3(x, y, z);
    84.                         List<Sphere> spheres = factory.Grid[x, y, z];
    85.  
    86.                         Material mat = new Material(VolumeShader);
    87.                         mat.SetTexture("_Volume", getVolume(pos, spheres));
    88.  
    89.                         RenderQueue.Add(new OctantDescriptor(pos, spheres, mat));
    90.                     }
    91.                 }
    92.             }
    93.         }
    94.     }
    95.  
    96.     /// <summary>
    97.     /// Calculates the distances to all provided spheres
    98.     /// and returns a density field in the form of a 3d tex
    99.     /// </summary>
    100.     /// <param name="position"></param>
    101.     /// <param name="spheres"></param>
    102.     /// <returns></returns>
    103.     private Texture3D getVolume (Vector3 position, List<Sphere> spheres)
    104.     {
    105.         int size = 64;
    106.         int index = 0;
    107.  
    108.         Color[] colors = new Color[size * size * size];
    109.         Texture3D tex = new Texture3D(size, size, size, TextureFormat.RGBAFloat, false);
    110.         tex.wrapMode = TextureWrapMode.Clamp;
    111.  
    112.         for(int x = 0; x < size; x++)
    113.         {
    114.             for(int y = 0; y < size; y++)
    115.             {
    116.                 for(int z = 0; z < size; z++)
    117.                 {
    118.                     Vector3 pos = new Vector3(x / (float)size, y / (float)size, z / (float)size) + position;
    119.                     float val = float.MaxValue, dist;
    120.                     foreach (Sphere sphere in spheres)
    121.                     {
    122.                         Vector3 delta = sphere.Position - pos;
    123.                         dist = Mathf.Sqrt(delta.x * delta.x + delta.y * delta.y + delta.z * delta.z);
    124.                         val = dist - sphere.Radius;
    125.                         //val = Mathf.Min(val, dist);
    126.                     }
    127.                     val = 1f + Mathf.Clamp(val, 0f, -1f);
    128.                     colors[index++] = new Color(1f, 1f, 1f, val);
    129.                 }
    130.             }
    131.         }
    132.  
    133.         tex.SetPixels(colors);
    134.         tex.Apply();
    135.         return tex;
    136.     }
    137.  
    138.     private void OnDrawGizmosSelected()
    139.     {
    140.         if (factory == null) return;
    141.  
    142.         Color originColor = Gizmos.color;
    143.         Vector3 extents = factory.Extents;
    144.         Vector3 halfExt = extents * .5f;
    145.  
    146.         if (DrawExtents)
    147.         {
    148.             Gizmos.color = new Color(.1f, .1f, .23f);
    149.             Gizmos.DrawWireCube(halfExt, extents);
    150.         }
    151.  
    152.         if (DrawSpheres)
    153.         {
    154.             for(int i = 0; i < factory.Spheres.Count; i++)
    155.             {
    156.                 if(i == TargetSphere)
    157.                 {
    158.                     Gizmos.color = new Color(.25f, .65f, .25f);
    159.                 }
    160.                 else
    161.                 {
    162.                     Gizmos.color = new Color(.1f, .23f, .1f);
    163.                 }
    164.                 Gizmos.DrawWireSphere(factory.Spheres[i].Position, factory.Spheres[i].Radius);
    165.             }
    166.         }
    167.      
    168.         if (factory.Grid != null)
    169.         {
    170.             for(int x = 0; x < extents.x; x++)
    171.             {
    172.                 for(int y = 0; y < extents.y; y++)
    173.                 {
    174.                     for(int z = 0; z < extents.z; z++)
    175.                     {
    176.                         if(factory.Grid[x,y,z] != null)
    177.                         {
    178.                             if(HighlightOctants && TargetSphere >= 0 && TargetSphere < factory.Spheres.Count)
    179.                             {
    180.                                 Vector3 pos = factory.Spheres[TargetSphere].Position;
    181.                                 if (factory.Grid[x, y, z].Find(d => d.Position == pos).Radius != 0)
    182.                                 {
    183.                                     Gizmos.color = new Color(.33f, .1f, .1f);
    184.                                     if (!DrawGrid)
    185.                                         Gizmos.DrawWireCube(new Vector3(x, y, z) + Vector3.one * .5f, Vector3.one);
    186.                                 }
    187.                                 else
    188.                                     Gizmos.color = new Color(.1f, .1f, .33f);
    189.                             }
    190.  
    191.                             if(DrawGrid)
    192.                                 Gizmos.DrawWireCube(new Vector3(x, y,z) + Vector3.one * .5f, Vector3.one);
    193.                         }
    194.                     }
    195.                 }
    196.             }
    197.         }
    198.         Gizmos.color = originColor;
    199.  
    200.         if (RenderQueue == null) return;
    201.         if (RenderQueue.Count == 0) return;
    202.  
    203.         for (int i = 0; i < RenderQueue.Count; i++)
    204.         {
    205.                 RenderQueue[i].Mat.SetPass(0);
    206.                 Graphics.DrawMeshNow(cube,
    207.                     Matrix4x4.TRS(RenderQueue[i].Position + Vector3.one * .5f, Quaternion.identity, Vector3.one));
    208.          
    209.         }
    210.     }
    211. }
    212.  

    Sphere data factory


    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public struct Sphere
    6. {
    7.     public Vector3 Position;
    8.     public float Radius;
    9.     public Quaternion? Direction;
    10.  
    11.     public Sphere(Vector3 pos, float rad, Quaternion? dir)
    12.     {
    13.         Position = pos;
    14.         Radius = rad;
    15.         Direction = dir;
    16.     }
    17. }
    18.  
    19. public class SphereFactory
    20. {
    21.     public Vector3 ExtentsMin
    22.     {
    23.         get
    24.         {
    25.             return new Vector3(
    26.                 Mathf.Floor(m_extentMin.x),
    27.                 Mathf.Floor(m_extentMin.y),
    28.                 Mathf.Floor(m_extentMin.z)
    29.                 );
    30.         }
    31.     }
    32.  
    33.     public Vector3 ExtentsMax
    34.     {
    35.         get
    36.         {
    37.             return new Vector3(
    38.                 Mathf.Ceil(m_extentMax.x),
    39.                 Mathf.Ceil(m_extentMax.y),
    40.                 Mathf.Ceil(m_extentMax.z)
    41.                 );
    42.         }
    43.     }
    44.  
    45.     public Vector3 Extents { get { return ExtentsMax - ExtentsMin; } }
    46.     public Vector3 Center { get { return (ExtentsMin + ExtentsMax) * .5f; } }
    47.  
    48.     public List<Sphere> Spheres { get; private set; }
    49.     public List<Sphere>[,,] Grid { get; private set; }
    50.  
    51.     private int m_numSpheres;
    52.     private float m_startRadius;
    53.     private float m_diameterFactor;
    54.     private float m_diameterExponent;
    55.     private float m_spreadAngle;
    56.     private Vector3 m_extentMin;
    57.     private Vector3 m_extentMax;
    58.  
    59.     public SphereFactory (int numSpheres, float startRad, float diamFactor, float diamExponent, float spreadAngle)
    60.     {
    61.         m_numSpheres = numSpheres;
    62.         m_startRadius = startRad;
    63.         m_diameterFactor = diamFactor;
    64.         m_diameterExponent = diamExponent;
    65.         m_spreadAngle = spreadAngle;
    66.     }
    67.  
    68.     public void CreateCloud ()
    69.     {
    70.         Spheres = new List<Sphere>();
    71.         System.Random rand = new System.Random();
    72.         m_extentMin = new Vector3(
    73.             float.MaxValue,
    74.             float.MaxValue,
    75.             float.MaxValue
    76.             );
    77.         m_extentMax = new Vector3(
    78.             float.MinValue,
    79.             float.MinValue,
    80.             float.MinValue
    81.             );
    82.  
    83.         Spheres.Add(new Sphere(Vector3.zero, m_startRadius, null));
    84.         checkExtents(Vector3.zero, m_startRadius);  // ensure we got extents if number of spheres is 0
    85.  
    86.         for (int i = 0; i < m_numSpheres; i++)
    87.         {
    88.             int idx = rand.Next(Spheres.Count);
    89.             Sphere target = Spheres[idx];
    90.             Vector3 pos = Vector3.zero;
    91.  
    92.             if (target.Direction == null)
    93.             {
    94.                 pos = (Random.onUnitSphere * target.Radius) + target.Position;
    95.             }
    96.             else
    97.             {
    98.                 pos = (MathHelper.GetPointOnUnitSphereCap((Quaternion)target.Direction, m_spreadAngle, rand) * target.Radius) + target.Position;
    99.             }
    100.  
    101.             float radius = Mathf.Pow(target.Radius * m_diameterFactor, m_diameterExponent);
    102.             Vector3 dir = pos - target.Position;
    103.  
    104.             checkExtents(pos, radius);
    105.             Spheres.Add(new Sphere(pos, radius, Quaternion.LookRotation(dir)));
    106.         }
    107.  
    108.         // pad the volume, to prevent spheres touching the edges
    109.         m_extentMin -= Vector3.one * .5f;
    110.         m_extentMax += Vector3.one * .5f;
    111.  
    112.         // adjust positions to positive space
    113.         for (int i = 0; i < Spheres.Count; i++)
    114.         {
    115.             Sphere sph = Spheres[i];
    116.             sph.Position += (Extents * .5f) - Center;
    117.             Spheres[i] = sph;
    118.         }
    119.     }
    120.  
    121.     /// <summary>
    122.     /// Sorts the sphere depending on which octants it intersects
    123.     /// and puts them in a list at that octants grid position
    124.     /// </summary>
    125.     /// <returns></returns>
    126.     public List<Sphere>[,,] GetGrid ()
    127.     {
    128.         Vector3 size = Extents, sphGridPos, curPos; // get a buffer value
    129.         Grid = new List<Sphere>[(int)size.x, (int)size.y, (int)size.z]; // should be a local variable, change later
    130.  
    131.         foreach(Sphere sphere in Spheres)
    132.         {
    133.             sphGridPos = new Vector3(Mathf.Floor(sphere.Position.x), Mathf.Floor(sphere.Position.y), Mathf.Floor(sphere.Position.z));
    134.             addSphere(Grid, sphGridPos, sphere);    // we know the sphere will be in the same octant it's position is in, obviously
    135.  
    136.             for(int x = (int)sphGridPos.x - 1; x <= (int)sphGridPos.x + 1; x++ )
    137.             {
    138.                 for (int y = (int)sphGridPos.y - 1; y <= (int)sphGridPos.y + 1; y++)
    139.                 {
    140.                     for (int z = (int)sphGridPos.z - 1; z <= (int)sphGridPos.z + 1; z++)
    141.                     {
    142.                         if (x < 0 || x > size.x) continue; // -check- *respect* bounds lol
    143.                         if (y < 0 || y > size.y) continue;
    144.                         if (z < 0 || z > size.z) continue;
    145.                         if (x == 0 && y == 0 && z == 0) continue;   // we've already added this position
    146.  
    147.                         curPos = new Vector3(x, y, z);
    148.                         if (isWithinOctant(curPos, sphere))
    149.                             addSphere(Grid, curPos, sphere);
    150.                     }
    151.                 }
    152.             }
    153.         }
    154.  
    155.         return Grid;
    156.     }
    157.  
    158.     /// <summary>
    159.     /// Checks if a sphere is within the octant at the given position
    160.     /// Code by Ben Voigt (thanks mate)
    161.     /// </summary>
    162.     /// <param name="c1"></param>
    163.     /// <param name="sphere"></param>
    164.     /// <returns></returns>
    165.     private bool isWithinOctant (Vector3 c1, Sphere sphere)
    166.     {
    167.         Vector3 c2 = c1 + Vector3.one;
    168.         Vector3 p = sphere.Position;
    169.         float dist_sqrd = sphere.Radius * sphere.Radius;
    170.  
    171.         if (p.x < c1.x) dist_sqrd -= (p.x - c1.x) * (p.x - c1.x);
    172.         else if (p.x > c2.x) dist_sqrd -= (p.x - c2.x) * (p.x - c2.x);
    173.  
    174.         if (p.y < c1.y) dist_sqrd -= (p.y - c1.y) * (p.y - c1.y);
    175.         else if (p.y > c2.y) dist_sqrd -= (p.y - c2.y) * (p.y - c2.y);
    176.  
    177.         if (p.z < c1.z) dist_sqrd -= (p.z - c1.z) * (p.z - c1.z);
    178.         else if (p.z > c2.z) dist_sqrd -= (p.z - c2.z) * (p.z - c2.z);
    179.  
    180.         return dist_sqrd > 0;
    181.     }
    182.  
    183.     /// <summary>
    184.     /// Adds a sphere to the provided grid
    185.     /// and automatically creates a list for that position
    186.     /// if none exist
    187.     /// </summary>
    188.     /// <param name="grid"></param>
    189.     /// <param name="pos"></param>
    190.     /// <param name="sphere"></param>
    191.     private void addSphere (List<Sphere>[,,] grid, Vector3 pos, Sphere sphere)
    192.     {
    193.         int x = (int)pos.x, y = (int)pos.y, z = (int)pos.z;
    194.  
    195.         if (grid[x, y, z] == null)
    196.             grid[x, y, z] = new List<Sphere>();
    197.         grid[x, y, z].Add(sphere);
    198.     }
    199.  
    200.     private void checkExtents (Vector3 pos, float rad)
    201.     {
    202.         if (pos.x - rad < m_extentMin.x) m_extentMin.x = pos.x - rad;
    203.         if (pos.y - rad < m_extentMin.y) m_extentMin.y = pos.y - rad;
    204.         if (pos.z - rad < m_extentMin.z) m_extentMin.z = pos.z - rad;
    205.  
    206.         if (pos.x + rad > m_extentMax.x) m_extentMax.x = pos.x + rad;
    207.         if (pos.y + rad > m_extentMax.y) m_extentMax.y = pos.y + rad;
    208.         if (pos.z + rad > m_extentMax.z) m_extentMax.z = pos.z + rad;
    209.     }
    210. }
    211.  

     
    Last edited: Oct 27, 2019