// David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2020 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once #include #include #include #include #include namespace WwiseGTE { class TSManifoldMesh { public: // Triangle data types. class Triangle; typedef std::shared_ptr(*TCreator)(int, int, int); typedef std::map, std::shared_ptr> TMap; // Tetrahedron data types. class Tetrahedron; typedef std::shared_ptr(*SCreator)(int, int, int, int); typedef std::map, std::shared_ptr> SMap; // Triangle object. class Triangle { public: virtual ~Triangle() = default; Triangle(int v0, int v1, int v2) : V{ v0, v1, v2 } { } // Vertices of the face. std::array V; // Tetrahedra sharing the face. std::array, 2> T; }; // Tetrahedron object. class Tetrahedron { public: virtual ~Tetrahedron() = default; Tetrahedron(int v0, int v1, int v2, int v3) : V{ v0, v1, v2, v3 } { } // Vertices, listed in an order so that each face vertices in // counterclockwise order when viewed from outside the // tetrahedron. std::array V; // Adjacent faces. T[i] points to the triangle face // opposite V[i]. // T[0] points to face (V[1],V[2],V[3]) // T[1] points to face (V[0],V[3],V[2]) // T[2] points to face (V[0],V[1],V[3]) // T[3] points to face (V[0],V[2],V[1]) std::array, 4> T; // Adjacent tetrahedra. S[i] points to the adjacent tetrahedron // sharing face T[i]. std::array, 4> S; }; // Construction and destruction. virtual ~TSManifoldMesh() = default; TSManifoldMesh(TCreator tCreator = nullptr, SCreator sCreator = nullptr) : mTCreator(tCreator ? tCreator : CreateTriangle), mSCreator(sCreator ? sCreator : CreateTetrahedron), mThrowOnNonmanifoldInsertion(true) { } // Support for a deep copy of the mesh. The mTMap and mSMap objects // have dynamically allocated memory for triangles and tetrahedra. A // shallow/ copy of the pointers to this memory is problematic. // Allowing sharing, say, via std::shared_ptr, is an option but not // really the intent of copying the mesh graph. TSManifoldMesh(TSManifoldMesh const& mesh) { *this = mesh; } TSManifoldMesh& operator=(TSManifoldMesh const& mesh) { Clear(); mTCreator = mesh.mTCreator; mSCreator = mesh.mSCreator; mThrowOnNonmanifoldInsertion = mesh.mThrowOnNonmanifoldInsertion; for (auto const& element : mesh.mSMap) { Insert(element.first.V[0], element.first.V[1], element.first.V[2], element.first.V[3]); } return *this; } // Member access. inline TMap const& GetTriangles() const { return mTMap; } inline SMap const& GetTetrahedra() const { return mSMap; } // If the insertion of a tetrahedron fails because the mesh would // become nonmanifold, the default behavior is to throw an exception. // You can disable this behavior and continue gracefully without an // exception. void ThrowOnNonmanifoldInsertion(bool doException) { mThrowOnNonmanifoldInsertion = doException; } // If is not in the mesh, a Tetrahedron object is // created and returned; otherwise, is in the mesh and // nullptr is returned. If the insertion leads to a nonmanifold mesh, // the call fails with a nullptr returned. std::shared_ptr Insert(int v0, int v1, int v2, int v3) { TetrahedronKey skey(v0, v1, v2, v3); if (mSMap.find(skey) != mSMap.end()) { // The tetrahedron already exists. Return a null pointer as // a signal to the caller that the insertion failed. return nullptr; } // Add the new tetrahedron. std::shared_ptr tetra = mSCreator(v0, v1, v2, v3); mSMap[skey] = tetra; // Add the faces to the mesh if they do not already exist. for (int i = 0; i < 4; ++i) { auto opposite = TetrahedronKey::GetOppositeFace()[i]; TriangleKey tkey(tetra->V[opposite[0]], tetra->V[opposite[1]], tetra->V[opposite[2]]); std::shared_ptr face; auto titer = mTMap.find(tkey); if (titer == mTMap.end()) { // This is the first time the face is encountered. face = mTCreator(tetra->V[opposite[0]], tetra->V[opposite[1]], tetra->V[opposite[2]]); mTMap[tkey] = face; // Update the face and tetrahedron. face->T[0] = tetra; tetra->T[i] = face; } else { // This is the second time the face is encountered. face = titer->second; LogAssert(face != nullptr, "Unexpected condition."); // Update the face. if (face->T[1].lock()) { if (mThrowOnNonmanifoldInsertion) { LogError("Attempt to create nonmanifold mesh."); } else { return nullptr; } } face->T[1] = tetra; // Update the adjacent tetrahedra. auto adjacent = face->T[0].lock(); LogAssert(adjacent != nullptr, "Unexpected condition."); for (int j = 0; j < 4; ++j) { if (adjacent->T[j].lock() == face) { adjacent->S[j] = tetra; break; } } // Update the tetrahedron. tetra->T[i] = face; tetra->S[i] = adjacent; } } return tetra; } // If is in the mesh, it is removed and 'true' is // returned; otherwise, is not in the mesh and 'false' // is returned. bool Remove(int v0, int v1, int v2, int v3) { TetrahedronKey skey(v0, v1, v2, v3); auto siter = mSMap.find(skey); if (siter == mSMap.end()) { // The tetrahedron does not exist. return false; } // Get the tetrahedron. std::shared_ptr tetra = siter->second; // Remove the faces and update adjacent tetrahedra if necessary. for (int i = 0; i < 4; ++i) { // Inform the faces the tetrahedron is being deleted. auto face = tetra->T[i].lock(); LogAssert(face != nullptr, "Unexpected condition."); if (face->T[0].lock() == tetra) { // One-tetrahedron faces always have pointer at index // zero. face->T[0] = face->T[1]; face->T[1].reset(); } else if (face->T[1].lock() == tetra) { face->T[1].reset(); } else { LogError("Unexpected condition."); } // Remove the face if you have the last reference to it. if (!face->T[0].lock() && !face->T[1].lock()) { TriangleKey tkey(face->V[0], face->V[1], face->V[2]); mTMap.erase(tkey); } // Inform adjacent tetrahedra the tetrahedron is being // deleted. auto adjacent = tetra->S[i].lock(); if (adjacent) { for (int j = 0; j < 4; ++j) { if (adjacent->S[j].lock() == tetra) { adjacent->S[j].reset(); break; } } } } mSMap.erase(skey); return true; } // Destroy the triangles and tetrahedra to obtain an empty mesh. virtual void Clear() { mTMap.clear(); mSMap.clear(); } // A manifold mesh is closed if each face is shared twice. bool IsClosed() const { for (auto const& element : mSMap) { auto tri = element.second; if (!tri->S[0].lock() || !tri->S[1].lock()) { return false; } } return true; } protected: // The triangle data and default triangle creation. static std::shared_ptr CreateTriangle(int v0, int v1, int v2) { return std::make_shared(v0, v1, v2); } TCreator mTCreator; TMap mTMap; // The tetrahedron data and default tetrahedron creation. static std::shared_ptr CreateTetrahedron(int v0, int v1, int v2, int v3) { return std::make_shared(v0, v1, v2, v3); } SCreator mSCreator; SMap mSMap; bool mThrowOnNonmanifoldInsertion; // default: true }; }