| 1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
| 2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
| 3 | #include "Mesh/BsMeshUtility.h" |
| 4 | #include "Math/BsVector4.h" |
| 5 | #include "Math/BsVector3.h" |
| 6 | #include "Math/BsVector2.h" |
| 7 | #include "Math/BsPlane.h" |
| 8 | |
| 9 | namespace bs |
| 10 | { |
| 11 | struct VertexFaces |
| 12 | { |
| 13 | UINT32* faces; |
| 14 | UINT32 numFaces = 0; |
| 15 | }; |
| 16 | |
| 17 | struct VertexConnectivity |
| 18 | { |
| 19 | VertexConnectivity(UINT8* indices, UINT32 numVertices, UINT32 numFaces, UINT32 indexSize) |
| 20 | :vertexFaces(nullptr), mMaxFacesPerVertex(0), mNumVertices(numVertices), mFaces(nullptr) |
| 21 | { |
| 22 | vertexFaces = bs_newN<VertexFaces>(numVertices); |
| 23 | |
| 24 | resizeFaceArray(10); |
| 25 | |
| 26 | for (UINT32 i = 0; i < numFaces; i++) |
| 27 | { |
| 28 | for (UINT32 j = 0; j < 3; j++) |
| 29 | { |
| 30 | UINT32 idx = i * 3 + j; |
| 31 | UINT32 vertexIdx = 0; |
| 32 | memcpy(&vertexIdx, indices + idx * indexSize, indexSize); |
| 33 | |
| 34 | assert(vertexIdx < mNumVertices); |
| 35 | VertexFaces& faces = vertexFaces[vertexIdx]; |
| 36 | if (faces.numFaces >= mMaxFacesPerVertex) |
| 37 | resizeFaceArray(mMaxFacesPerVertex * 2); |
| 38 | |
| 39 | faces.faces[faces.numFaces] = i; |
| 40 | faces.numFaces++; |
| 41 | } |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | ~VertexConnectivity() |
| 46 | { |
| 47 | if (vertexFaces != nullptr) |
| 48 | bs_deleteN(vertexFaces, mNumVertices); |
| 49 | |
| 50 | if (mFaces != nullptr) |
| 51 | bs_free(mFaces); |
| 52 | } |
| 53 | |
| 54 | VertexFaces* vertexFaces; |
| 55 | |
| 56 | private: |
| 57 | void resizeFaceArray(UINT32 numFaces) |
| 58 | { |
| 59 | UINT32* newFaces = (UINT32*)bs_alloc(numFaces * mNumVertices * sizeof(UINT32)); |
| 60 | |
| 61 | if (mFaces != nullptr) |
| 62 | { |
| 63 | for (UINT32 i = 0; i < mNumVertices; i++) |
| 64 | memcpy(newFaces + (i * numFaces), mFaces + (i * mMaxFacesPerVertex), mMaxFacesPerVertex * sizeof(UINT32)); |
| 65 | |
| 66 | bs_free(mFaces); |
| 67 | } |
| 68 | |
| 69 | for (UINT32 i = 0; i < mNumVertices; i++) |
| 70 | vertexFaces[i].faces = newFaces + (i * numFaces); |
| 71 | |
| 72 | mFaces = newFaces; |
| 73 | mMaxFacesPerVertex = numFaces; |
| 74 | } |
| 75 | |
| 76 | UINT32 mMaxFacesPerVertex; |
| 77 | UINT32 mNumVertices; |
| 78 | UINT32* mFaces; |
| 79 | }; |
| 80 | |
| 81 | /** Provides base methods required for clipping of arbitrary triangles. */ |
| 82 | class TriangleClipperBase // Implementation from: http://www.geometrictools.com/Documentation/ClipMesh.pdf |
| 83 | { |
| 84 | protected: |
| 85 | /** Single vertex in the clipped mesh. */ |
| 86 | struct ClipVert |
| 87 | { |
| 88 | ClipVert() { } |
| 89 | |
| 90 | Vector3 point = Vector3::ZERO; |
| 91 | Vector2 uv = Vector2::ZERO; |
| 92 | float distance = 0.0f; |
| 93 | UINT32 occurs = 0; |
| 94 | bool visible = true; |
| 95 | }; |
| 96 | |
| 97 | /** Single edge in the clipped mesh. */ |
| 98 | struct ClipEdge |
| 99 | { |
| 100 | ClipEdge() { } |
| 101 | |
| 102 | UINT32 verts[2]; |
| 103 | Vector<UINT32> faces; |
| 104 | bool visible = true; |
| 105 | }; |
| 106 | |
| 107 | /** Single polygon in the clipped mesh. */ |
| 108 | struct ClipFace |
| 109 | { |
| 110 | ClipFace() { } |
| 111 | |
| 112 | Vector<UINT32> edges; |
| 113 | bool visible = true; |
| 114 | Vector3 normal = Vector3::ZERO; |
| 115 | }; |
| 116 | |
| 117 | /** Contains vertices, edges and faces of the clipped mesh. */ |
| 118 | struct ClipMesh |
| 119 | { |
| 120 | ClipMesh() { } |
| 121 | |
| 122 | Vector<ClipVert> verts; |
| 123 | Vector<ClipEdge> edges; |
| 124 | Vector<ClipFace> faces; |
| 125 | }; |
| 126 | |
| 127 | protected: |
| 128 | /** |
| 129 | * Register all edges and faces, using the mesh vertices as a basis. Assumes vertices are not indexed and that |
| 130 | * every three vertices form a face |
| 131 | */ |
| 132 | void addEdgesAndFaces(); |
| 133 | |
| 134 | /** Clips the current mesh with the provided plane. */ |
| 135 | INT32 clipByPlane(const Plane& plane); |
| 136 | |
| 137 | /** Clips vertices of the current mesh by the provided plane. */ |
| 138 | INT32 processVertices(const Plane& plane); |
| 139 | |
| 140 | /** Clips edges of the current mesh. processVertices() must be called beforehand. */ |
| 141 | void processEdges(); |
| 142 | |
| 143 | /** Clips the faces (polygons) of the current mesh. processEdges() must be called beforehand. */ |
| 144 | void processFaces(); |
| 145 | |
| 146 | /** |
| 147 | * Returns a set of non-culled vertex indices for every visible face in the mesh. This should be called after |
| 148 | * clipping operation is complete to retrieve valid vertices. |
| 149 | */ |
| 150 | void getOrderedFaces(FrameVector<FrameVector<UINT32>>& sortedFaces); |
| 151 | |
| 152 | /** Returns a set of ordered and non-culled vertices for the provided face of the mesh */ |
| 153 | void getOrderedVertices(const ClipFace& face, UINT32* vertices); |
| 154 | |
| 155 | /** Calculates the normal for vertices related to the provided vertex indices. */ |
| 156 | Vector3 getNormal(UINT32* sortedVertices, UINT32 numVertices); |
| 157 | |
| 158 | /** |
| 159 | * Checks is the polygon shape of the provided face open or closed. If open, returns true and outputs endpoints of |
| 160 | * the polyline. |
| 161 | */ |
| 162 | bool getOpenPolyline(ClipFace& face, UINT32& start, UINT32& end); |
| 163 | |
| 164 | ClipMesh mesh; |
| 165 | }; |
| 166 | |
| 167 | void TriangleClipperBase::addEdgesAndFaces() |
| 168 | { |
| 169 | UINT32 numTris = (UINT32)mesh.verts.size() / 3; |
| 170 | |
| 171 | UINT32 numEdges = numTris * 3; |
| 172 | mesh.edges.resize(numEdges); |
| 173 | mesh.faces.resize(numTris); |
| 174 | |
| 175 | for (UINT32 i = 0; i < numTris; i++) |
| 176 | { |
| 177 | UINT32 idx0 = i * 3 + 0; |
| 178 | UINT32 idx1 = i * 3 + 1; |
| 179 | UINT32 idx2 = i * 3 + 2; |
| 180 | |
| 181 | ClipEdge& clipEdge0 = mesh.edges[idx0]; |
| 182 | clipEdge0.verts[0] = idx0; |
| 183 | clipEdge0.verts[1] = idx1; |
| 184 | |
| 185 | ClipEdge& clipEdge1 = mesh.edges[idx1]; |
| 186 | clipEdge1.verts[0] = idx1; |
| 187 | clipEdge1.verts[1] = idx2; |
| 188 | |
| 189 | ClipEdge& clipEdge2 = mesh.edges[idx2]; |
| 190 | clipEdge2.verts[0] = idx2; |
| 191 | clipEdge2.verts[1] = idx0; |
| 192 | |
| 193 | ClipFace& clipFace = mesh.faces[i]; |
| 194 | |
| 195 | clipFace.edges.push_back(idx0); |
| 196 | clipFace.edges.push_back(idx1); |
| 197 | clipFace.edges.push_back(idx2); |
| 198 | |
| 199 | clipEdge0.faces.push_back(i); |
| 200 | clipEdge1.faces.push_back(i); |
| 201 | clipEdge2.faces.push_back(i); |
| 202 | |
| 203 | UINT32 verts[] = { idx0, idx1, idx2, idx0 }; |
| 204 | for (UINT32 j = 0; j < 3; j++) |
| 205 | clipFace.normal += Vector3::cross(mesh.verts[verts[j]].point, mesh.verts[verts[j + 1]].point); |
| 206 | |
| 207 | clipFace.normal.normalize(); |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | INT32 TriangleClipperBase::clipByPlane(const Plane& plane) |
| 212 | { |
| 213 | int state = processVertices(plane); |
| 214 | |
| 215 | if (state == 1) |
| 216 | return +1; // Nothing is clipped |
| 217 | else if (state == -1) |
| 218 | return -1; // Everything is clipped |
| 219 | |
| 220 | processEdges(); |
| 221 | processFaces(); |
| 222 | |
| 223 | return 0; |
| 224 | } |
| 225 | |
| 226 | INT32 TriangleClipperBase::processVertices(const Plane& plane) |
| 227 | { |
| 228 | static const float EPSILON = 0.00001f; |
| 229 | |
| 230 | // Compute signed distances from vertices to plane |
| 231 | int positive = 0, negative = 0; |
| 232 | for (UINT32 i = 0; i < (UINT32)mesh.verts.size(); i++) |
| 233 | { |
| 234 | ClipVert& vertex = mesh.verts[i]; |
| 235 | |
| 236 | if (vertex.visible) |
| 237 | { |
| 238 | vertex.distance = Vector3::dot(plane.normal, vertex.point) - plane.d; |
| 239 | if (vertex.distance >= EPSILON) |
| 240 | { |
| 241 | positive++; |
| 242 | } |
| 243 | else if (vertex.distance <= -EPSILON) |
| 244 | { |
| 245 | negative++; |
| 246 | vertex.visible = false; |
| 247 | } |
| 248 | else |
| 249 | { |
| 250 | // Point on the plane within floating point tolerance |
| 251 | vertex.distance = 0; |
| 252 | } |
| 253 | } |
| 254 | } |
| 255 | if (negative == 0) |
| 256 | { |
| 257 | // All vertices on nonnegative side, no clipping |
| 258 | return +1; |
| 259 | } |
| 260 | if (positive == 0) |
| 261 | { |
| 262 | // All vertices on nonpositive side, everything clipped |
| 263 | return -1; |
| 264 | } |
| 265 | |
| 266 | return 0; |
| 267 | } |
| 268 | |
| 269 | void TriangleClipperBase::processEdges() |
| 270 | { |
| 271 | for (UINT32 i = 0; i < (UINT32)mesh.edges.size(); i++) |
| 272 | { |
| 273 | ClipEdge& edge = mesh.edges[i]; |
| 274 | |
| 275 | if (edge.visible) |
| 276 | { |
| 277 | const ClipVert& v0 = mesh.verts[edge.verts[0]]; |
| 278 | const ClipVert& v1 = mesh.verts[edge.verts[1]]; |
| 279 | |
| 280 | float d0 = v0.distance; |
| 281 | float d1 = v1.distance; |
| 282 | |
| 283 | if (d0 <= 0 && d1 <= 0) |
| 284 | { |
| 285 | // Edge is culled, remove edge from faces sharing it |
| 286 | for (UINT32 j = 0; j < (UINT32)edge.faces.size(); j++) |
| 287 | { |
| 288 | ClipFace& face = mesh.faces[edge.faces[j]]; |
| 289 | |
| 290 | auto iterFind = std::find(face.edges.begin(), face.edges.end(), i); |
| 291 | if (iterFind != face.edges.end()) |
| 292 | { |
| 293 | face.edges.erase(iterFind); |
| 294 | |
| 295 | if (face.edges.empty()) |
| 296 | face.visible = false; |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | edge.visible = false; |
| 301 | continue; |
| 302 | } |
| 303 | |
| 304 | if (d0 >= 0 && d1 >= 0) |
| 305 | { |
| 306 | // Edge is on nonnegative side, faces retain the edge |
| 307 | continue; |
| 308 | } |
| 309 | |
| 310 | // The edge is split by the plane. Compute the point of intersection. |
| 311 | // If the old edge is <V0,V1> and I is the intersection point, the new |
| 312 | // edge is <V0,I> when d0 > 0 or <I,V1> when d1 > 0. |
| 313 | float t = d0 / (d0 - d1); |
| 314 | Vector3 intersectPt = (1 - t)*v0.point + t*v1.point; |
| 315 | Vector2 intersectUv = (1 - t)*v0.uv + t*v1.uv; |
| 316 | |
| 317 | UINT32 newVertIdx = (UINT32)mesh.verts.size(); |
| 318 | mesh.verts.push_back(ClipVert()); |
| 319 | |
| 320 | ClipVert& newVert = mesh.verts.back(); |
| 321 | newVert.point = intersectPt; |
| 322 | newVert.uv = intersectUv; |
| 323 | |
| 324 | if (d0 > 0) |
| 325 | mesh.edges[i].verts[1] = newVertIdx; |
| 326 | else |
| 327 | mesh.edges[i].verts[0] = newVertIdx; |
| 328 | } |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | void TriangleClipperBase::processFaces() |
| 333 | { |
| 334 | for (UINT32 i = 0; i < (UINT32)mesh.faces.size(); i++) |
| 335 | { |
| 336 | ClipFace& face = mesh.faces[i]; |
| 337 | |
| 338 | if (face.visible) |
| 339 | { |
| 340 | // The edge is culled. If the edge is exactly on the clip |
| 341 | // plane, it is possible that a visible triangle shares it. |
| 342 | // The edge will be re-added during the face loop. |
| 343 | |
| 344 | for (UINT32 j = 0; j < (UINT32)face.edges.size(); j++) |
| 345 | { |
| 346 | ClipEdge& edge = mesh.edges[face.edges[j]]; |
| 347 | ClipVert& v0 = mesh.verts[edge.verts[0]]; |
| 348 | ClipVert& v1 = mesh.verts[edge.verts[1]]; |
| 349 | |
| 350 | v0.occurs = 0; |
| 351 | v1.occurs = 0; |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | UINT32 start, end; |
| 356 | if (getOpenPolyline(mesh.faces[i], start, end)) |
| 357 | { |
| 358 | // Polyline is open, close it |
| 359 | UINT32 closeEdgeIdx = (UINT32)mesh.edges.size(); |
| 360 | mesh.edges.push_back(ClipEdge()); |
| 361 | ClipEdge& closeEdge = mesh.edges.back(); |
| 362 | |
| 363 | closeEdge.verts[0] = start; |
| 364 | closeEdge.verts[1] = end; |
| 365 | |
| 366 | closeEdge.faces.push_back(i); |
| 367 | face.edges.push_back(closeEdgeIdx); |
| 368 | } |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | bool TriangleClipperBase::getOpenPolyline(ClipFace& face, UINT32& start, UINT32& end) |
| 373 | { |
| 374 | // Count the number of occurrences of each vertex in the polyline. The |
| 375 | // resulting "occurs" values must be 1 or 2. |
| 376 | for (UINT32 i = 0; i < (UINT32)face.edges.size(); i++) |
| 377 | { |
| 378 | ClipEdge& edge = mesh.edges[face.edges[i]]; |
| 379 | |
| 380 | if (edge.visible) |
| 381 | { |
| 382 | ClipVert& v0 = mesh.verts[edge.verts[0]]; |
| 383 | ClipVert& v1 = mesh.verts[edge.verts[1]]; |
| 384 | |
| 385 | v0.occurs++; |
| 386 | v1.occurs++; |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | // Determine if the polyline is open |
| 391 | bool gotStart = false; |
| 392 | bool gotEnd = false; |
| 393 | for (UINT32 i = 0; i < (UINT32)face.edges.size(); i++) |
| 394 | { |
| 395 | const ClipEdge& edge = mesh.edges[face.edges[i]]; |
| 396 | |
| 397 | const ClipVert& v0 = mesh.verts[edge.verts[0]]; |
| 398 | const ClipVert& v1 = mesh.verts[edge.verts[1]]; |
| 399 | |
| 400 | if (v0.occurs == 1) |
| 401 | { |
| 402 | if (!gotStart) |
| 403 | { |
| 404 | start = edge.verts[0]; |
| 405 | gotStart = true; |
| 406 | } |
| 407 | else if (!gotEnd) |
| 408 | { |
| 409 | end = edge.verts[0]; |
| 410 | gotEnd = true; |
| 411 | } |
| 412 | } |
| 413 | |
| 414 | if (v1.occurs == 1) |
| 415 | { |
| 416 | if (!gotStart) |
| 417 | { |
| 418 | start = edge.verts[1]; |
| 419 | gotStart = true; |
| 420 | } |
| 421 | else if (!gotEnd) |
| 422 | { |
| 423 | end = edge.verts[1]; |
| 424 | gotEnd = true; |
| 425 | } |
| 426 | } |
| 427 | } |
| 428 | |
| 429 | return gotStart; |
| 430 | } |
| 431 | |
| 432 | void TriangleClipperBase::getOrderedFaces(FrameVector<FrameVector<UINT32>>& sortedFaces) |
| 433 | { |
| 434 | for (UINT32 i = 0; i < (UINT32)mesh.faces.size(); i++) |
| 435 | { |
| 436 | const ClipFace& face = mesh.faces[i]; |
| 437 | |
| 438 | if (face.visible) |
| 439 | { |
| 440 | // Get the ordered vertices of the face. The first and last |
| 441 | // element of the array are the same since the polyline is |
| 442 | // closed. |
| 443 | UINT32 numSortedVerts = (UINT32)face.edges.size() + 1; |
| 444 | UINT32* sortedVerts = (UINT32*)bs_stack_alloc(sizeof(UINT32) * numSortedVerts); |
| 445 | |
| 446 | getOrderedVertices(face, sortedVerts); |
| 447 | |
| 448 | FrameVector<UINT32> faceVerts; |
| 449 | |
| 450 | // The convention is that the vertices should be counterclockwise |
| 451 | // ordered when viewed from the negative side of the plane of the |
| 452 | // face. If you need the opposite convention, switch the |
| 453 | // inequality in the if-else statement. |
| 454 | Vector3 normal = getNormal(sortedVerts, numSortedVerts); |
| 455 | if (Vector3::dot(mesh.faces[i].normal, normal) < 0) |
| 456 | { |
| 457 | // Clockwise, need to swap |
| 458 | for (INT32 j = (INT32)numSortedVerts - 2; j >= 0; j--) |
| 459 | faceVerts.push_back(sortedVerts[j]); |
| 460 | } |
| 461 | else |
| 462 | { |
| 463 | // Counterclockwise |
| 464 | for (int j = 0; j <= (INT32)numSortedVerts - 2; j++) |
| 465 | faceVerts.push_back(sortedVerts[j]); |
| 466 | } |
| 467 | |
| 468 | sortedFaces.push_back(faceVerts); |
| 469 | bs_stack_free(sortedVerts); |
| 470 | } |
| 471 | } |
| 472 | } |
| 473 | |
| 474 | void TriangleClipperBase::getOrderedVertices(const ClipFace& face, UINT32* sortedVerts) |
| 475 | { |
| 476 | UINT32 numEdges = (UINT32)face.edges.size(); |
| 477 | UINT32* sortedEdges = (UINT32*)bs_stack_alloc(sizeof(UINT32) * numEdges); |
| 478 | for (UINT32 i = 0; i < numEdges; i++) |
| 479 | sortedEdges[i] = face.edges[i]; |
| 480 | |
| 481 | // Bubble sort to arrange edges in contiguous order |
| 482 | for (UINT32 i0 = 0, i1 = 1, choice = 1; i1 < numEdges - 1; i0 = i1, i1++) |
| 483 | { |
| 484 | const ClipEdge& edge0 = mesh.edges[sortedEdges[i0]]; |
| 485 | |
| 486 | UINT32 current = edge0.verts[choice]; |
| 487 | for (UINT32 j = i1; j < numEdges; j++) |
| 488 | { |
| 489 | const ClipEdge& edge1 = mesh.edges[sortedEdges[j]]; |
| 490 | |
| 491 | if (edge1.verts[0] == current || edge1.verts[1] == current) |
| 492 | { |
| 493 | std::swap(sortedEdges[i1], sortedEdges[j]); |
| 494 | choice = 1; |
| 495 | break; |
| 496 | } |
| 497 | } |
| 498 | } |
| 499 | |
| 500 | // Add the first two vertices |
| 501 | sortedVerts[0] = mesh.edges[sortedEdges[0]].verts[0]; |
| 502 | sortedVerts[1] = mesh.edges[sortedEdges[0]].verts[1]; |
| 503 | |
| 504 | // Add the remaining vertices |
| 505 | for (UINT32 i = 1; i < numEdges; i++) |
| 506 | { |
| 507 | const ClipEdge& edge = mesh.edges[sortedEdges[i]]; |
| 508 | |
| 509 | if (edge.verts[0] == sortedVerts[i]) |
| 510 | sortedVerts[i + 1] = edge.verts[1]; |
| 511 | else |
| 512 | sortedVerts[i + 1] = edge.verts[0]; |
| 513 | } |
| 514 | |
| 515 | bs_stack_free(sortedEdges); |
| 516 | } |
| 517 | |
| 518 | Vector3 TriangleClipperBase::getNormal(UINT32* sortedVertices, UINT32 numVertices) |
| 519 | { |
| 520 | Vector3 normal(BsZero); |
| 521 | for (UINT32 i = 0; i <= numVertices - 2; i++) |
| 522 | normal += Vector3::cross(mesh.verts[sortedVertices[i]].point, mesh.verts[sortedVertices[i + 1]].point); |
| 523 | |
| 524 | normal.normalize(); |
| 525 | return normal; |
| 526 | } |
| 527 | |
| 528 | /** Clips two-dimensional triangles against a set of provided planes. */ |
| 529 | class TriangleClipper2D : public TriangleClipperBase |
| 530 | { |
| 531 | public: |
| 532 | /** @copydoc MeshUtility::clip2D */ |
| 533 | void clip(UINT8* vertices, UINT8* uvs, UINT32 numTris, UINT32 vertexStride, const Vector<Plane>& clipPlanes, |
| 534 | const std::function<void(Vector2*, Vector2*, UINT32)>& writeCallback); |
| 535 | |
| 536 | private: |
| 537 | /** Converts clipped vertices back into triangles and outputs them via the provided callback. */ |
| 538 | void convertToMesh(const std::function<void(Vector2*, Vector2*, UINT32)>& writeCallback); |
| 539 | |
| 540 | static const int BUFFER_SIZE = 64 * 3; // Must be a multiple of three |
| 541 | Vector2 vertexBuffer[BUFFER_SIZE]; |
| 542 | Vector2 uvBuffer[BUFFER_SIZE]; |
| 543 | }; |
| 544 | |
| 545 | void TriangleClipper2D::clip(UINT8* vertices, UINT8* uvs, UINT32 numTris, UINT32 vertexStride, const Vector<Plane>& clipPlanes, |
| 546 | const std::function<void(Vector2*, Vector2*, UINT32)>& writeCallback) |
| 547 | { |
| 548 | // Add vertices |
| 549 | UINT32 numVertices = numTris * 3; |
| 550 | mesh.verts.resize(numVertices); |
| 551 | |
| 552 | if (uvs != nullptr) |
| 553 | { |
| 554 | for (UINT32 i = 0; i < numVertices; i++) |
| 555 | { |
| 556 | ClipVert& clipVert = mesh.verts[i]; |
| 557 | Vector2 vector2D = *(Vector2*)(vertices + vertexStride * i); |
| 558 | |
| 559 | clipVert.point = Vector3(vector2D.x, vector2D.y, 0.0f); |
| 560 | clipVert.uv = *(Vector2*)(uvs + vertexStride * i); |
| 561 | } |
| 562 | } |
| 563 | else |
| 564 | { |
| 565 | for (UINT32 i = 0; i < numVertices; i++) |
| 566 | { |
| 567 | ClipVert& clipVert = mesh.verts[i]; |
| 568 | Vector2 vector2D = *(Vector2*)(vertices + vertexStride * i); |
| 569 | |
| 570 | clipVert.point = Vector3(vector2D.x, vector2D.y, 0.0f); |
| 571 | } |
| 572 | } |
| 573 | |
| 574 | addEdgesAndFaces(); |
| 575 | |
| 576 | for (int i = 0; i < 4; i++) |
| 577 | { |
| 578 | if (clipByPlane(clipPlanes[i]) == -1) |
| 579 | return; |
| 580 | } |
| 581 | |
| 582 | convertToMesh(writeCallback); |
| 583 | } |
| 584 | |
| 585 | void TriangleClipper2D::convertToMesh(const std::function<void(Vector2*, Vector2*, UINT32)>& writeCallback) |
| 586 | { |
| 587 | bs_frame_mark(); |
| 588 | { |
| 589 | FrameVector<FrameVector<UINT32>> allFaces; |
| 590 | getOrderedFaces(allFaces); |
| 591 | |
| 592 | // Note: Consider using Delaunay triangulation to avoid skinny triangles |
| 593 | UINT32 numWritten = 0; |
| 594 | assert(BUFFER_SIZE % 3 == 0); |
| 595 | for (auto& face : allFaces) |
| 596 | { |
| 597 | for (UINT32 i = 0; i < (UINT32)face.size() - 2; i++) |
| 598 | { |
| 599 | const Vector3& v0 = mesh.verts[face[0]].point; |
| 600 | const Vector3& v1 = mesh.verts[face[i + 1]].point; |
| 601 | const Vector3& v2 = mesh.verts[face[i + 2]].point; |
| 602 | |
| 603 | vertexBuffer[numWritten] = Vector2(v0.x, v0.y); |
| 604 | uvBuffer[numWritten] = mesh.verts[face[0]].uv; |
| 605 | numWritten++; |
| 606 | |
| 607 | vertexBuffer[numWritten] = Vector2(v1.x, v1.y); |
| 608 | uvBuffer[numWritten] = mesh.verts[face[i + 1]].uv; |
| 609 | numWritten++; |
| 610 | |
| 611 | vertexBuffer[numWritten] = Vector2(v2.x, v2.y); |
| 612 | uvBuffer[numWritten] = mesh.verts[face[i + 2]].uv; |
| 613 | numWritten++; |
| 614 | |
| 615 | // Only need to check this here since we guarantee the buffer is in multiples of three |
| 616 | if (numWritten >= BUFFER_SIZE) |
| 617 | { |
| 618 | writeCallback(vertexBuffer, uvBuffer, numWritten); |
| 619 | numWritten = 0; |
| 620 | } |
| 621 | } |
| 622 | } |
| 623 | |
| 624 | if (numWritten > 0) |
| 625 | writeCallback(vertexBuffer, uvBuffer, numWritten); |
| 626 | } |
| 627 | bs_frame_clear(); |
| 628 | } |
| 629 | |
| 630 | /** Clips three-dimensional triangles against a set of provided planes. */ |
| 631 | class TriangleClipper3D : public TriangleClipperBase |
| 632 | { |
| 633 | public: |
| 634 | /** @copydoc MeshUtility::clip3D */ |
| 635 | void clip(UINT8* vertices, UINT8* uvs, UINT32 numTris, UINT32 vertexStride, const Vector<Plane>& clipPlanes, |
| 636 | const std::function<void(Vector3*, Vector2*, UINT32)>& writeCallback); |
| 637 | |
| 638 | private: |
| 639 | /** Converts clipped vertices back into triangles and outputs them via the provided callback. */ |
| 640 | void convertToMesh(const std::function<void(Vector3*, Vector2*, UINT32)>& writeCallback); |
| 641 | |
| 642 | static const int BUFFER_SIZE = 64 * 3; // Must be a multiple of three |
| 643 | Vector3 vertexBuffer[BUFFER_SIZE]; |
| 644 | Vector2 uvBuffer[BUFFER_SIZE]; |
| 645 | }; |
| 646 | |
| 647 | void TriangleClipper3D::clip(UINT8* vertices, UINT8* uvs, UINT32 numTris, UINT32 vertexStride, const Vector<Plane>& clipPlanes, |
| 648 | const std::function<void(Vector3*, Vector2*, UINT32)>& writeCallback) |
| 649 | { |
| 650 | // Add vertices |
| 651 | UINT32 numVertices = numTris * 3; |
| 652 | mesh.verts.resize(numVertices); |
| 653 | |
| 654 | if (uvs != nullptr) |
| 655 | { |
| 656 | for (UINT32 i = 0; i < numVertices; i++) |
| 657 | { |
| 658 | ClipVert& clipVert = mesh.verts[i]; |
| 659 | |
| 660 | clipVert.point = *(Vector3*)(vertices + vertexStride * i); |
| 661 | clipVert.uv = *(Vector2*)(uvs + vertexStride * i); |
| 662 | } |
| 663 | } |
| 664 | else |
| 665 | { |
| 666 | for (UINT32 i = 0; i < numVertices; i++) |
| 667 | { |
| 668 | ClipVert& clipVert = mesh.verts[i]; |
| 669 | Vector2 vector2D = *(Vector2*)(vertices + vertexStride * i); |
| 670 | |
| 671 | clipVert.point = Vector3(vector2D.x, vector2D.y, 0.0f); |
| 672 | } |
| 673 | } |
| 674 | |
| 675 | addEdgesAndFaces(); |
| 676 | |
| 677 | for (int i = 0; i < 4; i++) |
| 678 | { |
| 679 | if (clipByPlane(clipPlanes[i]) == -1) |
| 680 | return; |
| 681 | } |
| 682 | |
| 683 | convertToMesh(writeCallback); |
| 684 | } |
| 685 | |
| 686 | void TriangleClipper3D::convertToMesh(const std::function<void(Vector3*, Vector2*, UINT32)>& writeCallback) |
| 687 | { |
| 688 | bs_frame_mark(); |
| 689 | { |
| 690 | FrameVector<FrameVector<UINT32>> allFaces; |
| 691 | getOrderedFaces(allFaces); |
| 692 | |
| 693 | // Note: Consider using Delaunay triangulation to avoid skinny triangles |
| 694 | UINT32 numWritten = 0; |
| 695 | assert(BUFFER_SIZE % 3 == 0); |
| 696 | for (auto& face : allFaces) |
| 697 | { |
| 698 | for (UINT32 i = 0; i < (UINT32)face.size() - 2; i++) |
| 699 | { |
| 700 | vertexBuffer[numWritten] = mesh.verts[face[0]].point; |
| 701 | uvBuffer[numWritten] = mesh.verts[face[0]].uv; |
| 702 | numWritten++; |
| 703 | |
| 704 | vertexBuffer[numWritten] = mesh.verts[face[i + 1]].point; |
| 705 | uvBuffer[numWritten] = mesh.verts[face[i + 1]].uv; |
| 706 | numWritten++; |
| 707 | |
| 708 | vertexBuffer[numWritten] = mesh.verts[face[i + 2]].point; |
| 709 | uvBuffer[numWritten] = mesh.verts[face[i + 2]].uv; |
| 710 | numWritten++; |
| 711 | |
| 712 | // Only need to check this here since we guarantee the buffer is in multiples of three |
| 713 | if (numWritten >= BUFFER_SIZE) |
| 714 | { |
| 715 | writeCallback(vertexBuffer, uvBuffer, numWritten); |
| 716 | numWritten = 0; |
| 717 | } |
| 718 | } |
| 719 | } |
| 720 | |
| 721 | if (numWritten > 0) |
| 722 | writeCallback(vertexBuffer, uvBuffer, numWritten); |
| 723 | } |
| 724 | bs_frame_clear(); |
| 725 | } |
| 726 | |
| 727 | void MeshUtility::calculateNormals(Vector3* vertices, UINT8* indices, UINT32 numVertices, |
| 728 | UINT32 numIndices, Vector3* normals, UINT32 indexSize) |
| 729 | { |
| 730 | UINT32 numFaces = numIndices / 3; |
| 731 | |
| 732 | Vector3* faceNormals = bs_newN<Vector3>(numFaces); |
| 733 | for (UINT32 i = 0; i < numFaces; i++) |
| 734 | { |
| 735 | UINT32 triangle[3]; |
| 736 | memcpy(&triangle[0], indices + (i * 3 + 0) * indexSize, indexSize); |
| 737 | memcpy(&triangle[1], indices + (i * 3 + 1) * indexSize, indexSize); |
| 738 | memcpy(&triangle[2], indices + (i * 3 + 2) * indexSize, indexSize); |
| 739 | |
| 740 | Vector3 edgeA = vertices[triangle[1]] - vertices[triangle[0]]; |
| 741 | Vector3 edgeB = vertices[triangle[2]] - vertices[triangle[0]]; |
| 742 | faceNormals[i] = Vector3::normalize(Vector3::cross(edgeA, edgeB)); |
| 743 | |
| 744 | // Note: Potentially don't normalize here in order to weigh the normals |
| 745 | // by triangle size |
| 746 | } |
| 747 | |
| 748 | VertexConnectivity connectivity(indices, numVertices, numFaces, indexSize); |
| 749 | for (UINT32 i = 0; i < numVertices; i++) |
| 750 | { |
| 751 | VertexFaces& faces = connectivity.vertexFaces[i]; |
| 752 | |
| 753 | normals[i] = Vector3::ZERO; |
| 754 | for (UINT32 j = 0; j < faces.numFaces; j++) |
| 755 | { |
| 756 | UINT32 faceIdx = faces.faces[j]; |
| 757 | normals[i] += faceNormals[faceIdx]; |
| 758 | } |
| 759 | |
| 760 | normals[i].normalize(); |
| 761 | } |
| 762 | |
| 763 | bs_deleteN(faceNormals, numFaces); |
| 764 | } |
| 765 | |
| 766 | void MeshUtility::calculateTangents(Vector3* vertices, Vector3* normals, Vector2* uv, UINT8* indices, UINT32 numVertices, |
| 767 | UINT32 numIndices, Vector3* tangents, Vector3* bitangents, UINT32 indexSize, UINT32 vertexStride) |
| 768 | { |
| 769 | UINT32 numFaces = numIndices / 3; |
| 770 | UINT32 vec2Stride = vertexStride == 0 ? sizeof(Vector2) : vertexStride; |
| 771 | UINT32 vec3Stride = vertexStride == 0 ? sizeof(Vector3) : vertexStride; |
| 772 | |
| 773 | UINT8* positionBytes = (UINT8*)vertices; |
| 774 | UINT8* normalBytes = (UINT8*)normals; |
| 775 | UINT8* uvBytes = (UINT8*)uv; |
| 776 | |
| 777 | Vector3* faceTangents = bs_newN<Vector3>(numFaces); |
| 778 | Vector3* faceBitangents = bs_newN<Vector3>(numFaces); |
| 779 | for (UINT32 i = 0; i < numFaces; i++) |
| 780 | { |
| 781 | UINT32 triangle[3]; |
| 782 | memcpy(&triangle[0], indices + (i * 3 + 0) * indexSize, indexSize); |
| 783 | memcpy(&triangle[1], indices + (i * 3 + 1) * indexSize, indexSize); |
| 784 | memcpy(&triangle[2], indices + (i * 3 + 2) * indexSize, indexSize); |
| 785 | |
| 786 | Vector3 p0 = *(Vector3*)&positionBytes[triangle[0] * vec3Stride]; |
| 787 | Vector3 p1 = *(Vector3*)&positionBytes[triangle[1] * vec3Stride]; |
| 788 | Vector3 p2 = *(Vector3*)&positionBytes[triangle[2] * vec3Stride]; |
| 789 | |
| 790 | Vector2 uv0 = *(Vector2*)&uvBytes[triangle[0] * vec2Stride]; |
| 791 | Vector2 uv1 = *(Vector2*)&uvBytes[triangle[1] * vec2Stride]; |
| 792 | Vector2 uv2 = *(Vector2*)&uvBytes[triangle[2] * vec2Stride]; |
| 793 | |
| 794 | Vector3 q0 = p1 - p0; |
| 795 | Vector3 q1 = p2 - p0; |
| 796 | |
| 797 | Vector2 st1 = uv1 - uv0; |
| 798 | Vector2 st2 = uv2 - uv0; |
| 799 | |
| 800 | float denom = st1.x * st2.y - st2.x * st1.y; |
| 801 | if (fabs(denom) >= 0e-8f) |
| 802 | { |
| 803 | float r = 1.0f / denom; |
| 804 | |
| 805 | faceTangents[i] = (st2.y * q0 - st1.y * q1) * r; |
| 806 | faceBitangents[i] = (st1.x * q1 - st2.x * q0) * r; |
| 807 | |
| 808 | faceTangents[i].normalize(); |
| 809 | faceBitangents[i].normalize(); |
| 810 | } |
| 811 | |
| 812 | // Note: Potentially don't normalize here in order to weight the normals by triangle size |
| 813 | } |
| 814 | |
| 815 | VertexConnectivity connectivity(indices, numVertices, numFaces, indexSize); |
| 816 | for (UINT32 i = 0; i < numVertices; i++) |
| 817 | { |
| 818 | VertexFaces& faces = connectivity.vertexFaces[i]; |
| 819 | |
| 820 | tangents[i] = Vector3::ZERO; |
| 821 | bitangents[i] = Vector3::ZERO; |
| 822 | |
| 823 | for (UINT32 j = 0; j < faces.numFaces; j++) |
| 824 | { |
| 825 | UINT32 faceIdx = faces.faces[j]; |
| 826 | tangents[i] += faceTangents[faceIdx]; |
| 827 | bitangents[i] += faceBitangents[faceIdx]; |
| 828 | } |
| 829 | |
| 830 | tangents[i].normalize(); |
| 831 | bitangents[i].normalize(); |
| 832 | |
| 833 | Vector3 normal = *(Vector3*)&normalBytes[i * vec3Stride]; |
| 834 | |
| 835 | // Orthonormalize |
| 836 | float dot0 = normal.dot(tangents[i]); |
| 837 | tangents[i] -= dot0*normal; |
| 838 | tangents[i].normalize(); |
| 839 | |
| 840 | float dot1 = tangents[i].dot(bitangents[i]); |
| 841 | dot0 = normal.dot(bitangents[i]); |
| 842 | bitangents[i] -= dot0*normal + dot1*tangents[i]; |
| 843 | bitangents[i].normalize(); |
| 844 | } |
| 845 | |
| 846 | bs_deleteN(faceTangents, numFaces); |
| 847 | bs_deleteN(faceBitangents, numFaces); |
| 848 | |
| 849 | // TODO - Consider weighing tangents by triangle size and/or edge angles |
| 850 | } |
| 851 | |
| 852 | void MeshUtility::calculateTangentSpace(Vector3* vertices, Vector2* uv, UINT8* indices, UINT32 numVertices, |
| 853 | UINT32 numIndices, Vector3* normals, Vector3* tangents, Vector3* bitangents, UINT32 indexSize) |
| 854 | { |
| 855 | calculateNormals(vertices, indices, numVertices, numIndices, normals, indexSize); |
| 856 | calculateTangents(vertices, normals, uv, indices, numVertices, numIndices, tangents, bitangents, indexSize); |
| 857 | } |
| 858 | |
| 859 | void MeshUtility::clip2D(UINT8* vertices, UINT8* uvs, UINT32 numTris, UINT32 vertexStride, const Vector<Plane>& clipPlanes, |
| 860 | const std::function<void(Vector2*, Vector2*, UINT32)>& writeCallback) |
| 861 | { |
| 862 | TriangleClipper2D clipper; |
| 863 | clipper.clip(vertices, uvs, numTris, vertexStride, clipPlanes, writeCallback); |
| 864 | } |
| 865 | |
| 866 | void MeshUtility::clip3D(UINT8* vertices, UINT8* uvs, UINT32 numTris, UINT32 vertexStride, const Vector<Plane>& clipPlanes, |
| 867 | const std::function<void(Vector3*, Vector2*, UINT32)>& writeCallback) |
| 868 | { |
| 869 | TriangleClipper3D clipper; |
| 870 | clipper.clip(vertices, uvs, numTris, vertexStride, clipPlanes, writeCallback); |
| 871 | } |
| 872 | |
| 873 | void MeshUtility::packNormals(Vector3* source, UINT8* destination, UINT32 count, UINT32 inStride, UINT32 outStride) |
| 874 | { |
| 875 | UINT8* srcPtr = (UINT8*)source; |
| 876 | UINT8* dstPtr = destination; |
| 877 | for (UINT32 i = 0; i < count; i++) |
| 878 | { |
| 879 | Vector3 src = *(Vector3*)srcPtr; |
| 880 | |
| 881 | PackedNormal& packed = *(PackedNormal*)dstPtr; |
| 882 | packed.x = Math::clamp((int)(src.x * 127.5f + 127.5f), 0, 255); |
| 883 | packed.y = Math::clamp((int)(src.y * 127.5f + 127.5f), 0, 255); |
| 884 | packed.z = Math::clamp((int)(src.z * 127.5f + 127.5f), 0, 255); |
| 885 | packed.w = 128; |
| 886 | |
| 887 | srcPtr += inStride; |
| 888 | dstPtr += outStride; |
| 889 | } |
| 890 | } |
| 891 | |
| 892 | void MeshUtility::packNormals(Vector4* source, UINT8* destination, UINT32 count, UINT32 inStride, UINT32 outStride) |
| 893 | { |
| 894 | UINT8* srcPtr = (UINT8*)source; |
| 895 | UINT8* dstPtr = destination; |
| 896 | for (UINT32 i = 0; i < count; i++) |
| 897 | { |
| 898 | Vector4 src = *(Vector4*)srcPtr; |
| 899 | PackedNormal& packed = *(PackedNormal*)dstPtr; |
| 900 | |
| 901 | packed.x = Math::clamp((int)(src.x * 127.5f + 127.5f), 0, 255); |
| 902 | packed.y = Math::clamp((int)(src.y * 127.5f + 127.5f), 0, 255); |
| 903 | packed.z = Math::clamp((int)(src.z * 127.5f + 127.5f), 0, 255); |
| 904 | packed.w = Math::clamp((int)(src.w * 127.5f + 127.5f), 0, 255); |
| 905 | |
| 906 | srcPtr += inStride; |
| 907 | dstPtr += outStride; |
| 908 | } |
| 909 | } |
| 910 | |
| 911 | void MeshUtility::unpackNormals(UINT8* source, Vector3* destination, UINT32 count, UINT32 stride) |
| 912 | { |
| 913 | UINT8* ptr = source; |
| 914 | for (UINT32 i = 0; i < count; i++) |
| 915 | { |
| 916 | destination[i] = unpackNormal(ptr); |
| 917 | |
| 918 | ptr += stride; |
| 919 | } |
| 920 | } |
| 921 | |
| 922 | void MeshUtility::unpackNormals(UINT8* source, Vector4* destination, UINT32 count, UINT32 stride) |
| 923 | { |
| 924 | UINT8* ptr = source; |
| 925 | for (UINT32 i = 0; i < count; i++) |
| 926 | { |
| 927 | PackedNormal& packed = *(PackedNormal*)ptr; |
| 928 | |
| 929 | const float inv = (1.0f / 255.0f) * 2.0f; |
| 930 | destination[i].x = (packed.x * inv - 1.0f); |
| 931 | destination[i].y = (packed.y * inv - 1.0f); |
| 932 | destination[i].z = (packed.z * inv - 1.0f); |
| 933 | destination[i].w = (packed.w * inv - 1.0f); |
| 934 | |
| 935 | ptr += stride; |
| 936 | } |
| 937 | } |
| 938 | } |