| 1 |
using System; |
| 2 |
using System.Collections.Generic; |
| 3 |
|
| 4 |
namespace Oni.Motoko |
| 5 |
{ |
| 6 |
internal class Quadify |
| 7 |
{ |
| 8 |
private readonly Geometry mesh; |
| 9 |
private readonly List<Face> faces; |
| 10 |
|
| 11 |
public Quadify(Geometry mesh) |
| 12 |
{ |
| 13 |
this.mesh = mesh; |
| 14 |
|
| 15 |
faces = new List<Face>(); |
| 16 |
|
| 17 |
for (int i = 0; i < mesh.Triangles.Length; i += 3) |
| 18 |
{ |
| 19 |
var plane = new Plane( |
| 20 |
mesh.Points[mesh.Triangles[i + 0]], |
| 21 |
mesh.Points[mesh.Triangles[i + 1]], |
| 22 |
mesh.Points[mesh.Triangles[i + 2]]); |
| 23 |
|
| 24 |
faces.Add(new Face( |
| 25 |
mesh, |
| 26 |
new[] { mesh.Triangles[i + 0], mesh.Triangles[i + 1], mesh.Triangles[i + 2] }, |
| 27 |
plane.Normal)); |
| 28 |
} |
| 29 |
} |
| 30 |
|
| 31 |
public static List<int[]> Do(Geometry mesh) |
| 32 |
{ |
| 33 |
var quadrangulate = new Quadify(mesh); |
| 34 |
return quadrangulate.Execute(); |
| 35 |
} |
| 36 |
|
| 37 |
public List<int[]> Execute() |
| 38 |
{ |
| 39 |
GenerateAdjacency(); |
| 40 |
|
| 41 |
var candidates = new List<QuadCandidate>(); |
| 42 |
var newFaces = new int[faces.Count][]; |
| 43 |
var quadified = new bool[faces.Count]; |
| 44 |
int quadCount = 0; |
| 45 |
|
| 46 |
for (int i = 0; i < faces.Count; i++) |
| 47 |
{ |
| 48 |
Face f1 = faces[i]; |
| 49 |
|
| 50 |
if (quadified[i]) |
| 51 |
continue; |
| 52 |
|
| 53 |
candidates.Clear(); |
| 54 |
|
| 55 |
foreach (Edge e1 in f1.edges) |
| 56 |
{ |
| 57 |
foreach (Edge e2 in e1.adjacency) |
| 58 |
{ |
| 59 |
if (quadified[faces.IndexOf(e2.face)]) |
| 60 |
continue; |
| 61 |
|
| 62 |
candidates.Add(new QuadCandidate(e1, e2)); |
| 63 |
} |
| 64 |
} |
| 65 |
|
| 66 |
if (candidates.Count > 0) |
| 67 |
{ |
| 68 |
candidates.Sort(new QuadCandidateComparer()); |
| 69 |
newFaces[i] = candidates[0].CreateQuad(); |
| 70 |
|
| 71 |
int k = faces.IndexOf(candidates[0].e2.face); |
| 72 |
|
| 73 |
quadified[i] = true; |
| 74 |
quadified[k] = true; |
| 75 |
|
| 76 |
quadCount++; |
| 77 |
} |
| 78 |
} |
| 79 |
|
| 80 |
var quadList = new List<int[]>(faces.Count - quadCount); |
| 81 |
|
| 82 |
for (int i = 0; i < faces.Count; i++) |
| 83 |
{ |
| 84 |
if (newFaces[i] != null) |
| 85 |
quadList.Add(newFaces[i]); |
| 86 |
else if (!quadified[i]) |
| 87 |
quadList.Add(faces[i].indices); |
| 88 |
} |
| 89 |
|
| 90 |
return quadList; |
| 91 |
} |
| 92 |
|
| 93 |
private void GenerateAdjacency() |
| 94 |
{ |
| 95 |
var points = mesh.Points; |
| 96 |
var pointUseCount = new int[points.Length]; |
| 97 |
var pointUsage = new int[points.Length][]; |
| 98 |
|
| 99 |
foreach (Face face in faces) |
| 100 |
{ |
| 101 |
foreach (int i in face.indices) |
| 102 |
pointUseCount[i]++; |
| 103 |
} |
| 104 |
|
| 105 |
for (int faceIndex = 0; faceIndex < faces.Count; faceIndex++) |
| 106 |
{ |
| 107 |
foreach (int pointIndex in faces[faceIndex].indices) |
| 108 |
{ |
| 109 |
int useCount = pointUseCount[pointIndex]; |
| 110 |
int[] usage = pointUsage[pointIndex]; |
| 111 |
|
| 112 |
if (usage == null) |
| 113 |
{ |
| 114 |
usage = new int[useCount]; |
| 115 |
pointUsage[pointIndex] = usage; |
| 116 |
} |
| 117 |
|
| 118 |
usage[usage.Length - useCount] = faceIndex; |
| 119 |
pointUseCount[pointIndex] = useCount - 1; |
| 120 |
} |
| 121 |
} |
| 122 |
|
| 123 |
var adjacencyBuffer = new List<Edge>(); |
| 124 |
|
| 125 |
foreach (Face f1 in faces) |
| 126 |
{ |
| 127 |
foreach (Edge e1 in f1.edges) |
| 128 |
{ |
| 129 |
int[] usage0 = pointUsage[e1.Point0Index]; |
| 130 |
int[] usage1 = pointUsage[e1.Point1Index]; |
| 131 |
|
| 132 |
if (usage0 == null || usage1 == null) |
| 133 |
continue; |
| 134 |
|
| 135 |
adjacencyBuffer.Clear(); |
| 136 |
|
| 137 |
foreach (int adjFaceIndex in MatchSortedArrays(usage0, usage1)) |
| 138 |
{ |
| 139 |
Face f2 = faces[adjFaceIndex]; |
| 140 |
|
| 141 |
if (f2 == f1 || (f2.normal - f1.normal).Length() > 0.01f) |
| 142 |
continue; |
| 143 |
|
| 144 |
foreach (Edge e2 in f2.edges) |
| 145 |
{ |
| 146 |
if (e1.IsShared(e2)) |
| 147 |
adjacencyBuffer.Add(e2); |
| 148 |
} |
| 149 |
} |
| 150 |
|
| 151 |
e1.adjacency = adjacencyBuffer.ToArray(); |
| 152 |
} |
| 153 |
} |
| 154 |
} |
| 155 |
|
| 156 |
private static IEnumerable<int> MatchSortedArrays(int[] a1, int[] a2) |
| 157 |
{ |
| 158 |
int l1 = a1.Length; |
| 159 |
int l2 = a2.Length; |
| 160 |
int i1 = 0; |
| 161 |
int i2 = 0; |
| 162 |
|
| 163 |
while (i1 < l1 && i2 < l2) |
| 164 |
{ |
| 165 |
int v1 = a1[i1]; |
| 166 |
int v2 = a2[i2]; |
| 167 |
|
| 168 |
if (v1 < v2) |
| 169 |
{ |
| 170 |
i1++; |
| 171 |
} |
| 172 |
else if (v1 > v2) |
| 173 |
{ |
| 174 |
i2++; |
| 175 |
} |
| 176 |
else |
| 177 |
{ |
| 178 |
i1++; |
| 179 |
i2++; |
| 180 |
|
| 181 |
yield return v1; |
| 182 |
} |
| 183 |
} |
| 184 |
} |
| 185 |
|
| 186 |
private class Face |
| 187 |
{ |
| 188 |
public readonly Geometry mesh; |
| 189 |
public readonly int[] indices; |
| 190 |
public readonly Edge[] edges; |
| 191 |
public readonly Vector3 normal; |
| 192 |
|
| 193 |
public Face(Geometry mesh, int[] pointIndices, Vector3 normal) |
| 194 |
{ |
| 195 |
this.mesh = mesh; |
| 196 |
this.indices = pointIndices; |
| 197 |
this.normal = normal; |
| 198 |
|
| 199 |
edges = new Edge[indices.Length]; |
| 200 |
|
| 201 |
for (int i = 0; i < edges.Length; i++) |
| 202 |
edges[i] = new Edge(this, i); |
| 203 |
} |
| 204 |
} |
| 205 |
|
| 206 |
private class Edge |
| 207 |
{ |
| 208 |
private static readonly Edge[] emptyEdges = new Edge[0]; |
| 209 |
|
| 210 |
public readonly Face face; |
| 211 |
public readonly int i0; |
| 212 |
public readonly int i1; |
| 213 |
public Edge[] adjacency; |
| 214 |
|
| 215 |
public Edge(Face polygon, int index) |
| 216 |
{ |
| 217 |
this.face = polygon; |
| 218 |
this.i0 = index; |
| 219 |
this.i1 = (index + 1) % face.edges.Length; |
| 220 |
this.adjacency = emptyEdges; |
| 221 |
} |
| 222 |
|
| 223 |
public int Point0Index |
| 224 |
{ |
| 225 |
get { return face.indices[i0]; } |
| 226 |
} |
| 227 |
|
| 228 |
public int Point1Index |
| 229 |
{ |
| 230 |
get { return face.indices[i1]; } |
| 231 |
} |
| 232 |
|
| 233 |
public bool IsShared(Edge e) |
| 234 |
{ |
| 235 |
return (Point0Index == e.Point1Index && Point1Index == e.Point0Index); |
| 236 |
} |
| 237 |
} |
| 238 |
|
| 239 |
private class QuadCandidateComparer : IComparer<QuadCandidate> |
| 240 |
{ |
| 241 |
public int Compare(QuadCandidate x, QuadCandidate y) |
| 242 |
{ |
| 243 |
return x.length.CompareTo(y.length); |
| 244 |
} |
| 245 |
} |
| 246 |
|
| 247 |
private class QuadCandidate |
| 248 |
{ |
| 249 |
public readonly Edge e1; |
| 250 |
public readonly Edge e2; |
| 251 |
public readonly float length; |
| 252 |
|
| 253 |
public QuadCandidate(Edge e1, Edge e2) |
| 254 |
{ |
| 255 |
this.e1 = e1; |
| 256 |
this.e2 = e2; |
| 257 |
|
| 258 |
Vector3[] points = e1.face.mesh.Points; |
| 259 |
this.length = (points[e1.Point0Index] - points[e1.Point1Index]).LengthSquared(); |
| 260 |
} |
| 261 |
|
| 262 |
public int[] CreateQuad() |
| 263 |
{ |
| 264 |
int[] newPoints = new int[4]; |
| 265 |
int l = 0; |
| 266 |
|
| 267 |
newPoints[l] = e1.face.indices[e1.i1]; |
| 268 |
l++; |
| 269 |
|
| 270 |
for (int k = 0; k < 3; k++) |
| 271 |
{ |
| 272 |
if (k != e1.i0 && k != e1.i1) |
| 273 |
{ |
| 274 |
newPoints[l] = e1.face.indices[k]; |
| 275 |
l++; |
| 276 |
|
| 277 |
break; |
| 278 |
} |
| 279 |
} |
| 280 |
|
| 281 |
newPoints[l] = e1.face.indices[e1.i0]; |
| 282 |
l++; |
| 283 |
|
| 284 |
for (int k = 0; k < 3; k++) |
| 285 |
{ |
| 286 |
if (k != e2.i0 && k != e2.i1) |
| 287 |
{ |
| 288 |
newPoints[l] = e2.face.indices[k]; |
| 289 |
l++; |
| 290 |
|
| 291 |
break; |
| 292 |
} |
| 293 |
} |
| 294 |
|
| 295 |
return newPoints; |
| 296 |
} |
| 297 |
} |
| 298 |
} |
| 299 |
} |