| 1 | using System; | 
 
 
 
 
 | 2 | using System.Collections.Generic; | 
 
 
 
 
 | 3 |  | 
 
 
 
 
 | 4 | namespace Oni.Akira | 
 
 
 
 
 | 5 | { | 
 
 
 
 
 | 6 | internal class PolygonQuadrangulate | 
 
 
 
 
 | 7 | { | 
 
 
 
 
 | 8 | private readonly PolygonMesh mesh; | 
 
 
 
 
 | 9 |  | 
 
 
 
 
 | 10 | public PolygonQuadrangulate(PolygonMesh mesh) | 
 
 
 
 
 | 11 | { | 
 
 
 
 
 | 12 | this.mesh = mesh; | 
 
 
 
 
 | 13 | } | 
 
 
 
 
 | 14 |  | 
 
 
 
 
 | 15 | public void Execute() | 
 
 
 
 
 | 16 | { | 
 
 
 
 
 | 17 | GenerateAdjacency(); | 
 
 
 
 
 | 18 |  | 
 
 
 
 
 | 19 | var candidates = new List<QuadCandidate>(); | 
 
 
 
 
 | 20 | var polygons = mesh.Polygons; | 
 
 
 
 
 | 21 |  | 
 
 
 
 
 | 22 | var newPolygons = new Polygon[polygons.Count]; | 
 
 
 
 
 | 23 | var marked = new bool[polygons.Count]; | 
 
 
 
 
 | 24 | int quadCount = 0; | 
 
 
 
 
 | 25 |  | 
 
 
 
 
 | 26 | for (int i = 0; i < polygons.Count; i++) | 
 
 
 
 
 | 27 | { | 
 
 
 
 
 | 28 | var p1 = polygons[i]; | 
 
 
 
 
 | 29 |  | 
 
 
 
 
 | 30 | if (marked[i] || p1.Edges.Length != 3) | 
 
 
 
 
 | 31 | continue; | 
 
 
 
 
 | 32 |  | 
 
 
 
 
 | 33 | candidates.Clear(); | 
 
 
 
 
 | 34 |  | 
 
 
 
 
 | 35 | foreach (PolygonEdge e1 in p1.Edges) | 
 
 
 
 
 | 36 | { | 
 
 
 
 
 | 37 | foreach (PolygonEdge e2 in e1.Adjancency) | 
 
 
 
 
 | 38 | { | 
 
 
 
 
 | 39 | if (marked[polygons.IndexOf(e2.Polygon)]) | 
 
 
 
 
 | 40 | continue; | 
 
 
 
 
 | 41 |  | 
 
 
 
 
 | 42 | if (QuadCandidate.IsQuadCandidate(e1, e2)) | 
 
 
 
 
 | 43 | candidates.Add(new QuadCandidate(e1, e2)); | 
 
 
 
 
 | 44 | } | 
 
 
 
 
 | 45 | } | 
 
 
 
 
 | 46 |  | 
 
 
 
 
 | 47 | if (candidates.Count > 0) | 
 
 
 
 
 | 48 | { | 
 
 
 
 
 | 49 | candidates.Sort(); | 
 
 
 
 
 | 50 | newPolygons[i] = candidates[0].CreateQuad(mesh); | 
 
 
 
 
 | 51 |  | 
 
 
 
 
 | 52 | int k = polygons.IndexOf(candidates[0].Polygon2); | 
 
 
 
 
 | 53 |  | 
 
 
 
 
 | 54 | marked[i] = true; | 
 
 
 
 
 | 55 | marked[k] = true; | 
 
 
 
 
 | 56 |  | 
 
 
 
 
 | 57 | quadCount++; | 
 
 
 
 
 | 58 | } | 
 
 
 
 
 | 59 | } | 
 
 
 
 
 | 60 |  | 
 
 
 
 
 | 61 | var newPolygonList = new List<Polygon>(polygons.Count - quadCount); | 
 
 
 
 
 | 62 |  | 
 
 
 
 
 | 63 | for (int i = 0; i < polygons.Count; i++) | 
 
 
 
 
 | 64 | { | 
 
 
 
 
 | 65 | if (newPolygons[i] != null) | 
 
 
 
 
 | 66 | newPolygonList.Add(newPolygons[i]); | 
 
 
 
 
 | 67 | else if (!marked[i]) | 
 
 
 
 
 | 68 | newPolygonList.Add(polygons[i]); | 
 
 
 
 
 | 69 | } | 
 
 
 
 
 | 70 |  | 
 
 
 
 
 | 71 | polygons = newPolygonList; | 
 
 
 
 
 | 72 | } | 
 
 
 
 
 | 73 |  | 
 
 
 
 
 | 74 | #region private class QuadCandidate | 
 
 
 
 
 | 75 |  | 
 
 
 
 
 | 76 | private class QuadCandidate : IComparable<QuadCandidate> | 
 
 
 
 
 | 77 | { | 
 
 
 
 
 | 78 | private PolygonEdge e1; | 
 
 
 
 
 | 79 | private PolygonEdge e2; | 
 
 
 
 
 | 80 | private float l; | 
 
 
 
 
 | 81 |  | 
 
 
 
 
 | 82 | public static bool IsQuadCandidate(PolygonEdge e1, PolygonEdge e2) | 
 
 
 
 
 | 83 | { | 
 
 
 
 
 | 84 | // | 
 
 
 
 
 | 85 | // To merge 2 polygons into one the following must be true: | 
 
 
 
 
 | 86 | // - both must be triangles | 
 
 
 
 
 | 87 | // - they must share the same plane | 
 
 
 
 
 | 88 | // - they must use the same material | 
 
 
 
 
 | 89 | // - TODO: the resulting polygon must be convex!!! | 
 
 
 
 
 | 90 | // - TODO: must have the same texture coordinates | 
 
 
 
 
 | 91 | // | 
 
 
 
 
 | 92 |  | 
 
 
 
 
 | 93 | Polygon p1 = e1.Polygon; | 
 
 
 
 
 | 94 | Polygon p2 = e2.Polygon; | 
 
 
 
 
 | 95 |  | 
 
 
 
 
 | 96 | return ( | 
 
 
 
 
 | 97 | p1.Edges.Length == 3 | 
 
 
 
 
 | 98 | && p2.Edges.Length == 3 | 
 
 
 
 
 | 99 | && p1.Plane == p2.Plane | 
 
 
 
 
 | 100 | && p1.Material == p2.Material | 
 
 
 
 
 | 101 | //&& e1.Point0Index == e2.Point0Index | 
 
 
 
 
 | 102 | //&& e1.Point1Index == e2.Point1Index | 
 
 
 
 
 | 103 | ); | 
 
 
 
 
 | 104 | } | 
 
 
 
 
 | 105 |  | 
 
 
 
 
 | 106 | public QuadCandidate(PolygonEdge e1, PolygonEdge e2) | 
 
 
 
 
 | 107 | { | 
 
 
 
 
 | 108 | this.e1 = e1; | 
 
 
 
 
 | 109 | this.e2 = e2; | 
 
 
 
 
 | 110 |  | 
 
 
 
 
 | 111 | List<Vector3> points = e1.Polygon.Mesh.Points; | 
 
 
 
 
 | 112 | this.l = Vector3.DistanceSquared(points[e1.Point0Index], points[e1.Point1Index]); | 
 
 
 
 
 | 113 | } | 
 
 
 
 
 | 114 |  | 
 
 
 
 
 | 115 | public Polygon Polygon1 => e1.Polygon; | 
 
 
 
 
 | 116 | public Polygon Polygon2 => e2.Polygon; | 
 
 
 
 
 | 117 |  | 
 
 
 
 
 | 118 | public int CompareTo(QuadCandidate other) | 
 
 
 
 
 | 119 | { | 
 
 
 
 
 | 120 | return l.CompareTo(other.l); | 
 
 
 
 
 | 121 | } | 
 
 
 
 
 | 122 |  | 
 
 
 
 
 | 123 | public Polygon CreateQuad(PolygonMesh mesh) | 
 
 
 
 
 | 124 | { | 
 
 
 
 
 | 125 | int[] newPoints = new int[4]; | 
 
 
 
 
 | 126 | int[] newTexCoords = new int[4]; | 
 
 
 
 
 | 127 | int l = 0; | 
 
 
 
 
 | 128 |  | 
 
 
 
 
 | 129 | newPoints[l] = e1.Polygon.PointIndices[e1.EndIndex]; | 
 
 
 
 
 | 130 | newTexCoords[l] = e1.Polygon.TexCoordIndices[e1.EndIndex]; | 
 
 
 
 
 | 131 | l++; | 
 
 
 
 
 | 132 |  | 
 
 
 
 
 | 133 | for (int k = 0; k < 3; k++) | 
 
 
 
 
 | 134 | { | 
 
 
 
 
 | 135 | if (k != e1.Index && k != e1.EndIndex) | 
 
 
 
 
 | 136 | { | 
 
 
 
 
 | 137 | newPoints[l] = e1.Polygon.PointIndices[k]; | 
 
 
 
 
 | 138 | newTexCoords[l] = e1.Polygon.TexCoordIndices[k]; | 
 
 
 
 
 | 139 | l++; | 
 
 
 
 
 | 140 |  | 
 
 
 
 
 | 141 | break; | 
 
 
 
 
 | 142 | } | 
 
 
 
 
 | 143 | } | 
 
 
 
 
 | 144 |  | 
 
 
 
 
 | 145 | newPoints[l] = e1.Polygon.PointIndices[e1.Index]; | 
 
 
 
 
 | 146 | newTexCoords[l] = e1.Polygon.TexCoordIndices[e1.Index]; | 
 
 
 
 
 | 147 | l++; | 
 
 
 
 
 | 148 |  | 
 
 
 
 
 | 149 | for (int k = 0; k < 3; k++) | 
 
 
 
 
 | 150 | { | 
 
 
 
 
 | 151 | if (k != e2.Index && k != e2.EndIndex) | 
 
 
 
 
 | 152 | { | 
 
 
 
 
 | 153 | newPoints[l] = e2.Polygon.PointIndices[k]; | 
 
 
 
 
 | 154 | newTexCoords[l] = e2.Polygon.TexCoordIndices[k]; | 
 
 
 
 
 | 155 | l++; | 
 
 
 
 
 | 156 |  | 
 
 
 
 
 | 157 | break; | 
 
 
 
 
 | 158 | } | 
 
 
 
 
 | 159 | } | 
 
 
 
 
 | 160 |  | 
 
 
 
 
 | 161 | return new Polygon(mesh, newPoints, e1.Polygon.Plane) | 
 
 
 
 
 | 162 | { | 
 
 
 
 
 | 163 | TexCoordIndices = newTexCoords, | 
 
 
 
 
 | 164 | Material = e1.Polygon.Material | 
 
 
 
 
 | 165 | }; | 
 
 
 
 
 | 166 | } | 
 
 
 
 
 | 167 | } | 
 
 
 
 
 | 168 |  | 
 
 
 
 
 | 169 | #endregion | 
 
 
 
 
 | 170 |  | 
 
 
 
 
 | 171 | private void GenerateAdjacency() | 
 
 
 
 
 | 172 | { | 
 
 
 
 
 | 173 | var points = mesh.Points; | 
 
 
 
 
 | 174 | var polygons = mesh.Polygons; | 
 
 
 
 
 | 175 |  | 
 
 
 
 
 | 176 | var pointUseCount = new int[points.Count]; | 
 
 
 
 
 | 177 | var pointUsage = new int[points.Count][]; | 
 
 
 
 
 | 178 |  | 
 
 
 
 
 | 179 | foreach (var polygon in polygons) | 
 
 
 
 
 | 180 | { | 
 
 
 
 
 | 181 | foreach (int i in polygon.PointIndices) | 
 
 
 
 
 | 182 | pointUseCount[i]++; | 
 
 
 
 
 | 183 | } | 
 
 
 
 
 | 184 |  | 
 
 
 
 
 | 185 | for (int polygon = 0; polygon < polygons.Count; polygon++) | 
 
 
 
 
 | 186 | { | 
 
 
 
 
 | 187 | foreach (int i in polygons[polygon].PointIndices) | 
 
 
 
 
 | 188 | { | 
 
 
 
 
 | 189 | int useCount = pointUseCount[i]; | 
 
 
 
 
 | 190 | int[] pa = pointUsage[i]; | 
 
 
 
 
 | 191 |  | 
 
 
 
 
 | 192 | if (pa == null) | 
 
 
 
 
 | 193 | { | 
 
 
 
 
 | 194 | pa = new int[useCount]; | 
 
 
 
 
 | 195 | pointUsage[i] = pa; | 
 
 
 
 
 | 196 | } | 
 
 
 
 
 | 197 |  | 
 
 
 
 
 | 198 | pa[pa.Length - useCount] = polygon; | 
 
 
 
 
 | 199 | pointUseCount[i] = useCount - 1; | 
 
 
 
 
 | 200 | } | 
 
 
 
 
 | 201 | } | 
 
 
 
 
 | 202 |  | 
 
 
 
 
 | 203 | var adjacencyBuffer = new List<PolygonEdge>(); | 
 
 
 
 
 | 204 |  | 
 
 
 
 
 | 205 | foreach (var p1 in polygons) | 
 
 
 
 
 | 206 | { | 
 
 
 
 
 | 207 | foreach (var e1 in p1.Edges) | 
 
 
 
 
 | 208 | { | 
 
 
 
 
 | 209 | adjacencyBuffer.Clear(); | 
 
 
 
 
 | 210 |  | 
 
 
 
 
 | 211 | int[] a0 = pointUsage[e1.Point0Index]; | 
 
 
 
 
 | 212 | int[] a1 = pointUsage[e1.Point1Index]; | 
 
 
 
 
 | 213 |  | 
 
 
 
 
 | 214 | if (a0 == null || a1 == null) | 
 
 
 
 
 | 215 | continue; | 
 
 
 
 
 | 216 |  | 
 
 
 
 
 | 217 | foreach (int pi2 in MatchSortedArrays(a0, a1)) | 
 
 
 
 
 | 218 | { | 
 
 
 
 
 | 219 | var p2 = polygons[pi2]; | 
 
 
 
 
 | 220 |  | 
 
 
 
 
 | 221 | if (p2 == p1) | 
 
 
 
 
 | 222 | continue; | 
 
 
 
 
 | 223 |  | 
 
 
 
 
 | 224 | foreach (var e2 in p2.Edges) | 
 
 
 
 
 | 225 | { | 
 
 
 
 
 | 226 | if (e1.Point0Index == e2.Point1Index && e1.Point1Index == e2.Point0Index | 
 
 
 
 
 | 227 | || e1.Point0Index == e2.Point0Index && e1.Point1Index == e2.Point1Index) | 
 
 
 
 
 | 228 | { | 
 
 
 
 
 | 229 | adjacencyBuffer.Add(e2); | 
 
 
 
 
 | 230 | } | 
 
 
 
 
 | 231 | } | 
 
 
 
 
 | 232 | } | 
 
 
 
 
 | 233 |  | 
 
 
 
 
 | 234 | e1.Adjancency = adjacencyBuffer.ToArray(); | 
 
 
 
 
 | 235 | } | 
 
 
 
 
 | 236 | } | 
 
 
 
 
 | 237 | } | 
 
 
 
 
 | 238 |  | 
 
 
 
 
 | 239 | private static IEnumerable<int> MatchSortedArrays(int[] a1, int[] a2) | 
 
 
 
 
 | 240 | { | 
 
 
 
 
 | 241 | int l1 = a1.Length; | 
 
 
 
 
 | 242 | int l2 = a2.Length; | 
 
 
 
 
 | 243 | int i1 = 0; | 
 
 
 
 
 | 244 | int i2 = 0; | 
 
 
 
 
 | 245 |  | 
 
 
 
 
 | 246 | while (i1 < l1 && i2 < l2) | 
 
 
 
 
 | 247 | { | 
 
 
 
 
 | 248 | int v1 = a1[i1]; | 
 
 
 
 
 | 249 | int v2 = a2[i2]; | 
 
 
 
 
 | 250 |  | 
 
 
 
 
 | 251 | if (v1 < v2) | 
 
 
 
 
 | 252 | { | 
 
 
 
 
 | 253 | i1++; | 
 
 
 
 
 | 254 | } | 
 
 
 
 
 | 255 | else if (v1 > v2) | 
 
 
 
 
 | 256 | { | 
 
 
 
 
 | 257 | i2++; | 
 
 
 
 
 | 258 | } | 
 
 
 
 
 | 259 | else | 
 
 
 
 
 | 260 | { | 
 
 
 
 
 | 261 | i1++; | 
 
 
 
 
 | 262 | i2++; | 
 
 
 
 
 | 263 |  | 
 
 
 
 
 | 264 | yield return v1; | 
 
 
 
 
 | 265 | } | 
 
 
 
 
 | 266 | } | 
 
 
 
 
 | 267 | } | 
 
 
 
 
 | 268 | } | 
 
 
 
 
 | 269 | } |