|
1 | 1 | using System; |
2 | 2 | using System.Collections.Generic; |
3 | | -using System.Linq; |
4 | 3 |
|
5 | 4 | namespace RadiantMapToWavefrontObj |
6 | 5 | { |
@@ -142,50 +141,142 @@ private static Vertex[] FindIntersections(ClippingPlane[] planes) |
142 | 141 | private static Face[] CreateFaces(Vertex[] vertices, ClippingPlane[] planes) |
143 | 142 | { |
144 | 143 | List<Face> faces = new List<Face>(); |
145 | | - double centerOffset = 1e-4; |
146 | 144 |
|
147 | 145 | for (int i = 0; i < planes.Length; ++i) |
148 | 146 | { |
149 | | - // Create a center point, we need a tiny offset to make sure we don't mess up for perfectly symetrical shapes. |
150 | | - double centerX = centerOffset; |
151 | | - double centerY = centerOffset; |
152 | | - double centerZ = centerOffset; |
153 | | - |
154 | | - List<Vertex> verts = planes[i].FindVerticesInPlane(vertices).ToList(); |
| 147 | + Vertex[] verts = planes[i].FindVerticesInPlane(vertices); |
155 | 148 |
|
156 | 149 | // Abort when there are no vertices anyway. Something went wrong... |
157 | | - if (verts.Count <= 0) |
| 150 | + if (verts.Length < 3) |
158 | 151 | return null; |
159 | 152 |
|
160 | | - foreach (Vertex v in verts) |
| 153 | + foreach (Face face in BowyerWatson(verts)) |
| 154 | + { |
| 155 | + FixNormal(face, planes[i].Normal); |
| 156 | + faces.Add(face); |
| 157 | + } |
| 158 | + } |
| 159 | + |
| 160 | + return faces.ToArray(); |
| 161 | + } |
| 162 | + |
| 163 | + // Apply Bowyer-Watson algorithm to triangulate all the points in a plane. |
| 164 | + // Pseudo code taken from related wikipedia page and provided on the side in comments. |
| 165 | + public static Face[] BowyerWatson(Vertex[] vertices) |
| 166 | + { |
| 167 | + List<Face> triangles = new List<Face>(); |
| 168 | + |
| 169 | + // Add super triangle to list. |
| 170 | + Face superTriangle = FindSuperTriangle(vertices); |
| 171 | + Vertex[] superVertices = superTriangle.GetVertices(); |
| 172 | + triangles.Add(superTriangle); |
| 173 | + |
| 174 | + // Add points. |
| 175 | + foreach (Vertex v in vertices) // for each point in pointList do |
| 176 | + { |
| 177 | + List<Face> badTriangles = new List<Face>(); // badTriangles := empty set |
| 178 | + foreach (Face triangle in triangles) // for each triangle in triangulation do |
161 | 179 | { |
162 | | - centerX += v.X; |
163 | | - centerY += v.Y; |
164 | | - centerZ += v.Z; |
| 180 | + if (InCircumsphere(v, triangle)) // if point is inside circumcircle of triangle |
| 181 | + badTriangles.Add(triangle); // add triangle to badTriangles |
165 | 182 | } |
166 | 183 |
|
167 | | - Vertex center = new Vertex(centerX / verts.Count, centerY / verts.Count, centerZ / verts.Count); |
168 | | - center.SetNormal(planes[i].Normal); |
| 184 | + List<Edge> polygon = new List<Edge>(); // polygon := empty set |
169 | 185 |
|
170 | | - // Calculate faces based on some hackish algorithm that seems to work so far. Might need replacement later. |
171 | | - // Algorithm: 1. Find vertex closest to the center point. |
172 | | - // 2. Sort the list of vertices based on the distance to the vertex found in 1. |
173 | | - // 3. Add faces in the following manner: {0,1,2}, {1,2,3}, {2,3,4}, etc... |
174 | | - // 4. Check if the face's normal is in the right direction, otherwise: invert it. |
175 | | - if (verts.Count >= 3) |
| 186 | + foreach (Face triangle in badTriangles) // for each triangle in badTriangles do |
176 | 187 | { |
177 | | - verts.Sort((el1, el2) => center.Distance(el1).CompareTo(center.Distance(el2))); |
178 | | - verts.Sort((el1, el2) => verts[0].Distance(el1).CompareTo(verts[0].Distance(el2))); |
179 | | - for (int j = 0; j < verts.Count - 2; ++j) |
| 188 | + foreach (Edge edge in triangle.GetEdges()) // for each edge in triangle do |
180 | 189 | { |
181 | | - Face face = new Face(verts.GetRange(j, 3).ToArray()); |
182 | | - FixNormal(face, planes[i].Normal); |
183 | | - faces.Add(face); |
| 190 | + bool shared = false; |
| 191 | + foreach (Face otherTriangle in badTriangles) // if edge is not shared by any other triangles in badTriangles |
| 192 | + { |
| 193 | + if (triangle == otherTriangle) |
| 194 | + continue; |
| 195 | + if (otherTriangle.GetEdges().Contains(edge) || otherTriangle.GetEdges().Contains(edge.GetInverse())) |
| 196 | + { |
| 197 | + shared = true; |
| 198 | + break; |
| 199 | + } |
| 200 | + } |
| 201 | + if (!shared) |
| 202 | + polygon.Add(edge); // add edge to polygon |
184 | 203 | } |
185 | 204 | } |
| 205 | + |
| 206 | + foreach (Face triangle in badTriangles) // for each triangle in badTriangles do |
| 207 | + triangles.Remove(triangle); // remove triangle from triangulation |
| 208 | + |
| 209 | + foreach (Edge e in polygon) // for each edge in polygon do |
| 210 | + triangles.Add(new Face(e.A, e.B, v)); // newTri := form a triangle from edge to point + add newTri to triangulation |
186 | 211 | } |
187 | 212 |
|
188 | | - return faces.ToArray(); |
| 213 | + List<Face> result = new List<Face>(); |
| 214 | + |
| 215 | + foreach (Face t in triangles) // for each triangle in triangulation |
| 216 | + { |
| 217 | + Vertex[] curVertices = t.GetVertices(); |
| 218 | + if (!curVertices.Contains(superVertices[0]) // if triangle contains a vertex from original super-triangle |
| 219 | + && !curVertices.Contains(superVertices[1]) |
| 220 | + && !curVertices.Contains(superVertices[2])) |
| 221 | + result.Add(t); // remove triangle from triangulation |
| 222 | + } |
| 223 | + |
| 224 | + return result.ToArray(); // return triangulation |
| 225 | + } |
| 226 | + |
| 227 | + // Finds the Bowyer-Watson super triangle of a set of vertices. |
| 228 | + private static Face FindSuperTriangle(Vertex[] vertices) |
| 229 | + { |
| 230 | + // Setup super triangle. |
| 231 | + double minX, maxX, minY, maxY, minZ, maxZ; |
| 232 | + |
| 233 | + minX = minY = minZ = Double.MaxValue; |
| 234 | + maxX = maxY = maxZ = Double.MinValue; |
| 235 | + |
| 236 | + foreach (Vertex v in vertices) |
| 237 | + { |
| 238 | + if (v.X < minX) |
| 239 | + minX = v.X; |
| 240 | + if (v.X > maxX) |
| 241 | + maxX = v.X; |
| 242 | + |
| 243 | + if (v.Y < minY) |
| 244 | + minY = v.Y; |
| 245 | + if (v.Y > maxY) |
| 246 | + maxY = v.Y; |
| 247 | + |
| 248 | + if (v.Z < minZ) |
| 249 | + minZ = v.Z; |
| 250 | + if (v.Z > maxZ) |
| 251 | + maxZ = v.Z; |
| 252 | + } |
| 253 | + |
| 254 | + ClippingPlane plane = new ClippingPlane(vertices[0], vertices[1], vertices[2]); |
| 255 | + |
| 256 | + Vertex a = new Vertex(minX, minY, minZ); |
| 257 | + Vertex b = new Vertex(maxX, maxY, maxZ); |
| 258 | + |
| 259 | + Vector ab = (Vector)(b - a); |
| 260 | + a -= 10*ab; |
| 261 | + b += 10*ab; |
| 262 | + |
| 263 | + Vector triBase = Vector.CrossProduct(ab, plane.Normal).Unit(); |
| 264 | + |
| 265 | + double length = ((Vector)(b - a)).Length(); |
| 266 | + |
| 267 | + Vertex c = a + triBase * length; |
| 268 | + Vertex d = a - triBase * length; |
| 269 | + |
| 270 | + return new Face(b, c, d); |
| 271 | + } |
| 272 | + |
| 273 | + // Checks if a point lies in the circumsphere of a face. |
| 274 | + private static bool InCircumsphere(Vertex point, Face face) |
| 275 | + { |
| 276 | + Tuple<Vertex, double> cs = face.GetCircumsphere(); |
| 277 | + if (point.Distance(cs.Item1) < cs.Item2) |
| 278 | + return true; |
| 279 | + return false; |
189 | 280 | } |
190 | 281 |
|
191 | 282 | // Fix normals of faces pointing in the wrong direction. |
|
0 commit comments