Calneon

Irregular relaxed icosphere grid

Oct 20th, 2021 (edited)
347
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 18.19 KB | None | 0 0
  1. protected override void CreateInternal()
  2. {
  3.     // Create base set of verts from which all tris are made
  4.     float t = (1.0f + Mathf.Sqrt(5.0f)) / 2.0f;
  5.     _verts.Add(new Vector3(-1, -t, 0));
  6.     _verts.Add(new Vector3(1, -t, 0));
  7.     _verts.Add(new Vector3(-1, t, 0));
  8.     _verts.Add(new Vector3(1, t, 0));
  9.     _verts.Add(new Vector3(0, 1, t));
  10.     _verts.Add(new Vector3(0, -1, t));
  11.     _verts.Add(new Vector3(0, 1, -t));
  12.     _verts.Add(new Vector3(0, -1, -t));
  13.     _verts.Add(new Vector3(t, 0, -1));
  14.     _verts.Add(new Vector3(t, 0, 1));
  15.     _verts.Add(new Vector3(-t, 0, -1));
  16.     _verts.Add(new Vector3(-t, 0, 1));
  17.  
  18.     // Create base tris for the icosphere
  19.     _quads.Add(Poly.Create(new int[] { 5, 11, 0}));
  20.     _quads.Add(Poly.Create(new int[] { 1, 5, 0 }));
  21.     _quads.Add(Poly.Create(new int[] { 7, 1, 0 }));
  22.     _quads.Add(Poly.Create(new int[] { 10, 7, 0 }));
  23.     _quads.Add(Poly.Create(new int[] { 11, 10, 0 }));
  24.  
  25.     _quads.Add(Poly.Create(new int[] { 9, 5, 1 }));
  26.     _quads.Add(Poly.Create(new int[] { 4, 11, 5 }));
  27.     _quads.Add(Poly.Create(new int[] { 2, 10, 11 }));
  28.     _quads.Add(Poly.Create(new int[] { 6, 7, 10 }));
  29.     _quads.Add(Poly.Create(new int[] { 8, 1, 7 }));
  30.  
  31.     _quads.Add(Poly.Create(new int[] { 4, 9, 3 }));
  32.     _quads.Add(Poly.Create(new int[] { 2, 4, 3 }));
  33.     _quads.Add(Poly.Create(new int[] { 6, 2, 3 }));
  34.     _quads.Add(Poly.Create(new int[] { 8, 6, 3 }));
  35.     _quads.Add(Poly.Create(new int[] { 9, 8, 3 }));
  36.  
  37.     _quads.Add(Poly.Create(new int[] { 5, 9, 4 }));
  38.     _quads.Add(Poly.Create(new int[] { 11, 4, 2 }));
  39.     _quads.Add(Poly.Create(new int[] { 10, 2, 6 }));
  40.     _quads.Add(Poly.Create(new int[] { 7, 6, 8 }));
  41.     _quads.Add(Poly.Create(new int[] { 1, 8, 9 }));
  42.  
  43.     // At this stage we connect up neighbors, and we need to remember update neighbors as
  44.     // we modify the icosphere since calculating neighbors again later will be expensive
  45.     foreach(Poly t1 in _quads)
  46.     {
  47.         // To get a tri's neighbors, find all other tris that share two verts with this tri
  48.         foreach (Poly t2 in _quads)
  49.         {
  50.             if (t1 == t2) continue;
  51.  
  52.             int sharedCount = 0;
  53.             foreach (int v1 in t1.verts)
  54.             {
  55.                 foreach (int v2 in t2.verts)
  56.                 {
  57.                     if (v1 == v2)
  58.                         sharedCount += 1;
  59.                 }
  60.             }
  61.             if (sharedCount >= 2)
  62.                 t1.neighbors.Add(t2);
  63.         }
  64.         if (t1.neighbors.Count != 3)
  65.         {
  66.             Debug.LogError("Error in neighbor calculation #1.");
  67.         }
  68.     }
  69.  
  70.     // This is where we subdivide the original icosphere as many times as we want
  71.     for (int i = 0; i < Subdivisions; i++)
  72.     {
  73.         List<Poly> newTris = new List<Poly>();
  74.         foreach (Poly tri in _quads)
  75.         {
  76.             // Split each tri into 4
  77.             int a = AddMiddleVert(tri.verts[0], tri.verts[1]);
  78.             int b = AddMiddleVert(tri.verts[1], tri.verts[2]);
  79.             int c = AddMiddleVert(tri.verts[2], tri.verts[0]);
  80.  
  81.             Poly newMidTri = AddTriangle(a, b, c, newTris);
  82.             List<Poly> triforce = new List<Poly>();
  83.             triforce.Add(AddTriangle(tri.verts[0], a, c, newTris));
  84.             triforce.Add(AddTriangle(tri.verts[1], b, a, newTris));
  85.             triforce.Add(AddTriangle(tri.verts[2], c, b, newTris));
  86.  
  87.             // Update neighbors on the new tris
  88.             // We know the 3 neighbors for the middle tri
  89.             newMidTri.neighbors.Add(triforce[0]);
  90.             newMidTri.neighbors.Add(triforce[1]);
  91.             newMidTri.neighbors.Add(triforce[2]);
  92.  
  93.             // The others are a bit more complicated. we know we're neighbored to the middle tri
  94.             triforce[0].neighbors.Add(newMidTri);
  95.             triforce[1].neighbors.Add(newMidTri);
  96.             triforce[2].neighbors.Add(newMidTri);
  97.  
  98.             // Other neighbors are in other parent triangles
  99.             foreach (Poly t1 in triforce)
  100.             {
  101.                 foreach (Poly n in tri.neighbors)
  102.                 {
  103.                     foreach (Poly t2 in n.children)
  104.                     {
  105.                         // Search neighbor children for tris that match 2 verts in our children
  106.                         if (t1.IsAdjacent(t2))
  107.                         {
  108.                             // add to our new tri
  109.                             t1.neighbors.Add(t2);
  110.                             // also add our new tri to neighbor
  111.                             t2.neighbors.Add(t1);
  112.                         }
  113.                     }
  114.                 }
  115.                 if (t1.neighbors.Count > 3)
  116.                     Debug.LogError("Tri has too many neighbors.");
  117.             }
  118.             // Add our new tris as children
  119.             tri.children.Add(triforce[0]);
  120.             tri.children.Add(triforce[1]);
  121.             tri.children.Add(triforce[2]);
  122.             tri.children.Add(newMidTri);
  123.         }
  124.         _quads = newTris;
  125.     }
  126.  
  127.     // Check neighbors are set correctly
  128.     if (_doChecks)
  129.     {
  130.         foreach (Poly tri in _quads)
  131.         {
  132.             foreach (Poly n in tri.neighbors)
  133.             {
  134.                 if (!tri.IsAdjacent(n))
  135.                     Debug.LogError("Error in neighbor calculation #2.");
  136.             }
  137.  
  138.             if (tri.neighbors.Count != 3)
  139.                 Debug.LogError("Error in neighbor calculation #2.");
  140.         }
  141.     }
  142.  
  143.     // We're now going to remove edges randomly so we get a grid of quads and tris
  144.     List<Vector2Int> usedEdges = new List<Vector2Int>();
  145.     List<Poly> polys = new List<Poly>();
  146.     foreach (Poly tri in _quads)
  147.     {
  148.         bool cont = false;
  149.         foreach (Poly poly in polys)
  150.         {
  151.             if (poly.verts.Length == 4)
  152.             {
  153.                 if ((poly.t1 != null && poly.t1 == tri) || (poly.t2 != null && poly.t2 == tri))
  154.                 {
  155.                     cont = true;
  156.                     break;
  157.                 }
  158.             }
  159.         }
  160.         if (cont)
  161.             continue;
  162.  
  163.         List<Vector2Int> edges = new List<Vector2Int>();
  164.         if (!usedEdges.Contains(Poly.GetEdgeID(new Vector2Int(tri.verts[0], tri.verts[1]))))
  165.             edges.Add(new Vector2Int(tri.verts[0], tri.verts[1]));
  166.         if (!usedEdges.Contains(Poly.GetEdgeID(new Vector2Int(tri.verts[1], tri.verts[2]))))
  167.             edges.Add(new Vector2Int(tri.verts[1], tri.verts[2]));
  168.         if (!usedEdges.Contains(Poly.GetEdgeID(new Vector2Int(tri.verts[2], tri.verts[0]))))
  169.             edges.Add(new Vector2Int(tri.verts[2], tri.verts[0]));
  170.  
  171.         if (edges.Count == 0)
  172.         {
  173.             polys.Add(tri);
  174.             continue;
  175.         }
  176.  
  177.         int randEdgeIdx = Random.Range(0, edges.Count - 1);
  178.         Vector2Int randEdge = edges[randEdgeIdx];
  179.  
  180.         // Find the tri opposite the edge
  181.         // This will be the other tri in tris that matches the two indices in the edge
  182.         Poly opposite = null;
  183.         for (int j = 0; j < tri.neighbors.Count; j++)
  184.         {  
  185.             if (tri.neighbors[j].HasEdge(randEdge))
  186.             {
  187.                 opposite = tri.neighbors[j];
  188.                 break;
  189.             }
  190.         }
  191.         if (opposite == null)
  192.             return;
  193.  
  194.         List<int> oppositeVerts = new List<int> { opposite.verts[0], opposite.verts[1], opposite.verts[2] };
  195.         oppositeVerts.Remove(randEdge.x);
  196.         oppositeVerts.Remove(randEdge.y);
  197.         int oppositeVert = oppositeVerts[0];
  198.  
  199.         // Merge the two tris between the edge.
  200.         int[] quadVerts = new int[4];
  201.         int idx = 0;
  202.         for (int j = 0; j < 3; j++)
  203.         {
  204.             Vector2Int edge = new Vector2Int(tri.verts[j], tri.verts[(j + 1) % 3]);
  205.             if (edge == randEdge)
  206.             {
  207.                 quadVerts[idx++] = oppositeVert;
  208.                 quadVerts[idx++] = tri.verts[(j + 1) % 3];
  209.                 quadVerts[idx++] = tri.verts[(j + 2) % 3];
  210.                 quadVerts[idx++] = tri.verts[(j + 3) % 3];
  211.             }
  212.         }
  213.  
  214.         Poly quad = Poly.Create(quadVerts);
  215.         quad.t1 = tri;
  216.         quad.t2 = opposite;
  217.  
  218.         // Mark the 6 edges as used so that we don't further break up this poly (we don't want
  219.         // anything with more that 4 verts.
  220.         // add and test in ascending order so we only have to add an edge once
  221.         usedEdges.Add(Poly.GetEdgeID(new Vector2Int(tri.verts[0], tri.verts[1])));
  222.         usedEdges.Add(Poly.GetEdgeID(new Vector2Int(tri.verts[1], tri.verts[2])));
  223.         usedEdges.Add(Poly.GetEdgeID(new Vector2Int(tri.verts[2], tri.verts[0])));
  224.         usedEdges.Add(Poly.GetEdgeID(new Vector2Int(opposite.verts[0], opposite.verts[1])));
  225.         usedEdges.Add(Poly.GetEdgeID(new Vector2Int(opposite.verts[1], opposite.verts[2])));
  226.         usedEdges.Add(Poly.GetEdgeID(new Vector2Int(opposite.verts[2], opposite.verts[0])));
  227.  
  228.         // Setup neighbors
  229.         for (int n = 0; n < tri.neighbors.Count; n++)
  230.         {
  231.             Poly neighbor = tri.neighbors[n];
  232.             if (neighbor == opposite)
  233.                 continue;
  234.  
  235.             quad.neighbors.Add(neighbor);
  236.             neighbor.neighbors.Remove(tri);
  237.             neighbor.neighbors.Add(quad);
  238.         }
  239.  
  240.         for (int n = 0; n < opposite.neighbors.Count; n++)
  241.         {
  242.             Poly neighbor = opposite.neighbors[n];
  243.             if (neighbor == tri)
  244.                 continue;
  245.  
  246.             quad.neighbors.Add(neighbor);
  247.             neighbor.neighbors.Remove(opposite);
  248.             neighbor.neighbors.Add(quad);
  249.         }
  250.  
  251.         polys.Add(quad);
  252.     }
  253.  
  254.     // Check neighbors are set correctly.
  255.     foreach (Poly tri in polys)
  256.     {
  257.         foreach (Poly n in tri.neighbors)
  258.         {
  259.             int sharedCount = 0;
  260.             foreach (int v1 in tri.verts)
  261.                 foreach (int v2 in n.verts)
  262.                     if (v1 == v2)
  263.                         sharedCount++;
  264.  
  265.             if (sharedCount != 2)
  266.                 Debug.LogError("Error in neighbor calculation #3.");
  267.         }
  268.  
  269.         if (tri.neighbors.Count != tri.verts.Length)
  270.             Debug.LogError("Error in neighbor calculation #3.");
  271.     }
  272.  
  273.     // Next, split each quad into 4, and each tri into 3 (to make quads)
  274.     List<Poly> newPolys = new List<Poly>();
  275.     foreach (Poly poly in polys)
  276.     {
  277.         if (poly.verts.Length == 4)
  278.         {
  279.             Poly parentQuad = poly;
  280.             int a = AddMiddleVert(parentQuad.verts[0], parentQuad.verts[1]);
  281.             int b = AddMiddleVert(parentQuad.verts[1], parentQuad.verts[2]);
  282.             int c = AddMiddleVert(parentQuad.verts[2], parentQuad.verts[3]);
  283.             int d = AddMiddleVert(parentQuad.verts[3], parentQuad.verts[0]);
  284.             int m = AddMiddleVert4(parentQuad.verts[0], parentQuad.verts[1], parentQuad.verts[2], parentQuad.verts[3]);
  285.  
  286.             Poly[] quads = new Poly[4] { Poly.Create(new int[] { parentQuad.verts[0], a, m, d }),
  287.                 Poly.Create(new int[] { parentQuad.verts[1], b, m, a }),
  288.                 Poly.Create(new int[] { parentQuad.verts[2], c, m, b }),
  289.                 Poly.Create(new int[] { parentQuad.verts[3], d, m, c }) };
  290.  
  291.             newPolys.Add(quads[0]);
  292.             newPolys.Add(quads[1]);
  293.             newPolys.Add(quads[2]);
  294.             newPolys.Add(quads[3]);
  295.  
  296.             quads[0].neighbors = new List<Poly> { quads[3], quads[1] };
  297.             quads[1].neighbors = new List<Poly> { quads[0], quads[2] };
  298.             quads[2].neighbors = new List<Poly> { quads[1], quads[3] };
  299.             quads[3].neighbors = new List<Poly> { quads[2], quads[0] };
  300.  
  301.             // Setup surrounding neighbors
  302.             foreach (Poly q1 in quads)
  303.             {
  304.                 foreach (Poly n in parentQuad.neighbors)
  305.                 {
  306.                     foreach (Poly q2 in n.children)
  307.                     {
  308.                         // For each possible neighbor type, find those that share 2 verts
  309.                         int sharedCount = 0;
  310.                         foreach (int v1 in q1.verts)
  311.                             foreach (int v2 in q2.verts)
  312.                                 if (v1 == v2)
  313.                                     sharedCount++;
  314.  
  315.                         if (sharedCount == 2)
  316.                         {
  317.                             // Add to our new tri
  318.                             q1.neighbors.Add(q2);
  319.                             // Also add our new tri to neighbor
  320.                             q2.neighbors.Add(q1);
  321.                         }
  322.                     }
  323.                 }
  324.                 if (q1.neighbors.Count > 4)
  325.                 {
  326.                     Debug.LogError("Quad has too many neighbors.");
  327.                     return;
  328.                 }
  329.             }
  330.             poly.children = new List<Poly> { quads[0], quads[1], quads[2], quads[3] };
  331.         }
  332.         else if (poly.verts.Length == 3)
  333.         {
  334.             Poly parentTri = poly;
  335.             int a = AddMiddleVert(parentTri.verts[0], parentTri.verts[1]);
  336.             int b = AddMiddleVert(parentTri.verts[1], parentTri.verts[2]);
  337.             int c = AddMiddleVert(parentTri.verts[2], parentTri.verts[0]);
  338.             int m = AddMiddleVert3(parentTri.verts[0], parentTri.verts[1], parentTri.verts[2]);
  339.  
  340.             Poly[] quads = new Poly[3] { Poly.Create(new int[] { parentTri.verts[0], a, m, c }),
  341.                 Poly.Create(new int[] { parentTri.verts[1], b, m, a }),
  342.                 Poly.Create(new int[] { parentTri.verts[2], c, m, b }) };
  343.  
  344.             newPolys.Add(quads[0]);
  345.             newPolys.Add(quads[1]);
  346.             newPolys.Add(quads[2]);
  347.  
  348.             quads[0].neighbors = new List<Poly> { quads[1], quads[2] };
  349.             quads[1].neighbors = new List<Poly> { quads[0], quads[2] };
  350.             quads[2].neighbors = new List<Poly> { quads[0], quads[1] };
  351.  
  352.             // Setup surrounding neighbors
  353.             foreach (Poly q1 in quads)
  354.             {
  355.                 foreach (Poly n in parentTri.neighbors)
  356.                 {
  357.                     foreach (Poly q2 in n.children)
  358.                     {
  359.                         // For each possible neighbor type, find those that share 2 verts
  360.                         int sharedCount = 0;
  361.                         foreach (int v1 in q1.verts)
  362.                             foreach (int v2 in q2.verts)
  363.                                 if (v1 == v2)
  364.                                     sharedCount++;
  365.  
  366.                         if (sharedCount == 2)
  367.                         {
  368.                             // Add to our new tri
  369.                             q1.neighbors.Add(q2);
  370.                             // Also add our new tri to neighbor
  371.                             q2.neighbors.Add(q1);
  372.                         }
  373.                     }
  374.                 }
  375.                 if (q1.neighbors.Count > 4)
  376.                 {
  377.                     Debug.LogError("Quad has too many neighbors.");
  378.                     return;
  379.                 }
  380.             }
  381.             poly.children = new List<Poly> { quads[0], quads[1], quads[2] };
  382.         }
  383.         else
  384.         {
  385.             Debug.LogError("Malformed poly.");
  386.         }
  387.     }
  388.     polys = newPolys;
  389.  
  390.     // Check neighbors are set correctly.
  391.     if (_doChecks)
  392.     {
  393.         foreach (Poly poly in polys)
  394.         {
  395.             if (poly.neighbors.Count != poly.verts.Length)
  396.                 Debug.LogError("Error in neighbor calculation #3.");
  397.         }
  398.     }
  399.  
  400.     // Fix neighbor winding so that the the first neighbor is always the one adjacent to v[0] and v[1] etc.
  401.     foreach (Poly poly in polys)
  402.     {
  403.         List<Poly> newNeighbors = new List<Poly>();
  404.         for (int v = 0; v < 4; v++)
  405.         {
  406.             foreach (Poly n in poly.neighbors)
  407.             {
  408.                 if (n.HasVert(poly.verts[v]) && n.HasVert(poly.verts[(v + 1) % 4]))
  409.                 {
  410.                     newNeighbors.Add(n);
  411.                     break;
  412.                 }
  413.             }
  414.         }
  415.         poly.neighbors = newNeighbors;
  416.     }
  417.  
  418.     _quads = polys;
  419.     for (int i = 0; i < _verts.Count; i++)
  420.     {
  421.         _verts[i] = _verts[i].normalized * Radius;
  422.     }
  423.     Relax(_quads);
  424. }
  425.  
  426. int AddMiddleVert(int v1, int v2)
  427. {
  428.     Vector2Int key = new Vector2Int(Mathf.Min(v1, v2), Mathf.Max(v1, v2));
  429.     if (_middleVertCache.ContainsKey(key))
  430.         return _middleVertCache[key];
  431.  
  432.     Vector3 middleVec = (_verts[v1] + _verts[v2]) / 2.0f;
  433.     _verts.Add(middleVec);
  434.     _middleVertCache[key] = _verts.Count - 1;
  435.     return _verts.Count - 1;
  436. }
  437.  
  438. int AddMiddleVert3(int v1, int v2, int v3)
  439. {
  440.     Vector3 middleVec = (_verts[v1] + _verts[v2] + _verts[v3]) / 3.0f;
  441.     _verts.Add(middleVec);
  442.     return _verts.Count - 1;
  443. }
  444.  
  445. int AddMiddleVert4(int v1, int v2, int v3, int v4)
  446. {
  447.     Vector3 middleVec = (_verts[v1] + _verts[v2] + _verts[v3] + _verts[v4]) / 4.0f;
  448.     _verts.Add(middleVec);
  449.     return _verts.Count - 1;
  450. }
  451.  
  452. Poly AddTriangle(int v1, int v2, int v3, List<Poly> list)
  453. {
  454.     Poly newTri = Poly.Create(new int[] { v1, v2, v3 });
  455.     list.Add(newTri);
  456.     return newTri;
  457. }
  458.  
  459. protected void Relax(List<Poly> polys)
  460. {
  461.     int iterations = _relaxIterations;
  462.     while (iterations > 0)
  463.     {
  464.         iterations -= 1;
  465.         List<Vector3> forces = new List<Vector3>();
  466.         foreach (Vector3 vert in _verts)
  467.         {
  468.             forces.Add(new Vector3());
  469.         }
  470.  
  471.         foreach (Poly poly in polys)
  472.         {
  473.             Vector3 force = new Vector3();
  474.  
  475.             // Get centroid
  476.             Vector3 centre = new Vector3();
  477.             foreach (int v in poly.verts)
  478.             {
  479.                 centre += _verts[v];
  480.             }
  481.             centre /= 4.0f;
  482.  
  483.             // Collect forces
  484.             foreach (int v in poly.verts)
  485.             {
  486.                 force += _verts[v] - centre;
  487.                 force = Quaternion.AngleAxis(90.0f, centre) * force;
  488.             }
  489.             force /= 4.0f;
  490.  
  491.             // Store forces
  492.             foreach (int v in poly.verts)
  493.             {
  494.                 forces[v] += centre + force - _verts[v];
  495.                 force = Quaternion.AngleAxis(90.0f, centre) * force;
  496.             }
  497.         }
  498.  
  499.         // Apply all accumulated forces on every vert
  500.         for (int i = 0; i < _verts.Count; i++)
  501.         {
  502.             _verts[i] = (_verts[i] + forces[i] * _relaxDelta).normalized * Radius;
  503.         }
  504.     }
  505. }
Add Comment
Please, Sign In to add comment