diff --git a/src/esp/assets/ResourceManager.cpp b/src/esp/assets/ResourceManager.cpp index 81435d7ce0..0794af8298 100644 --- a/src/esp/assets/ResourceManager.cpp +++ b/src/esp/assets/ResourceManager.cpp @@ -1058,37 +1058,86 @@ void ResourceManager::computeGeneralMeshAreaAndVolume( meshData->positions3DAsArray(0); Mn::MeshTools::transformPointsInPlace(absTransforms[iEntry], posArray); + // Getting the view properly relies on having the appropriate type of the + // loaded data + // const auto idxView = meshData->indices(); + const auto idxAra = meshData->indicesAsArray(); + // # of indices + uint32_t numIdxs = meshData->indexCount(); + // Assuming no duplicate vertices with different idxs + // Determine that all edges have exactly 2 sides -> + // idxAra describes exactly 2 pairs of the same idxs, a->b and b->a + std::unordered_map edgeCount; + std::unordered_map altEdgeCount; + scene::DrawableMeshTopology meshTopology = + scene::DrawableMeshTopology::ClosedManifold; + for (uint32_t idx = 0; idx < numIdxs; idx += 3) { + uint64_t vals[] = {uint64_t(idxAra[idx]), uint64_t(idxAra[idx + 1]), + uint64_t(idxAra[idx + 2])}; + uint64_t shift_vals[] = {vals[0] << 32, vals[1] << 32, vals[2] << 32}; + // for each edge in poly + for (uint32_t i = 0; i < 3; ++i) { + uint32_t next_i = (i + 1) % 3; + auto res = + edgeCount.emplace(std::make_pair(shift_vals[i] + vals[next_i], 0)); + if (!res.second) { + res.first->second += 1; + // Duplicate edge with same orientation + meshTopology = scene::DrawableMeshTopology::NonManifold; + } + // Alt edge placement - verify the alternate edge is present + altEdgeCount.emplace(std::make_pair(shift_vals[next_i] + vals[i], 0)); + } + } + // If still closed manifold then check that every edge has an alt edge + // present + if (meshTopology == scene::DrawableMeshTopology::ClosedManifold) { + for (const auto entry : edgeCount) { + altEdgeCount.erase(entry.first); + } + if (altEdgeCount.size() > 0) { + meshTopology = scene::DrawableMeshTopology::OpenManifold; + } + } + + // locate the scene node which contains the current drawable + scene::SceneNode& node = staticDrawableInfo[iEntry].node; // Surface area of the mesh : .5 * ba.cross(bc) double ttlSurfaceArea = 0.0; - // Volume of the mesh : 1/6 * (OA.dot(ba.cross(bc))) // Where O is a distant vertex + // Only applicable on closed manifold meshes (i.e. all edges have exactly + // 2 faces) double ttlVolume = 0.0f; - - // locate the scene node which contains the current drawable - scene::SceneNode& node = staticDrawableInfo[iEntry].node; - // # of indices - int numIdxs = meshData->indexCount(); - - // const auto idxView = meshData->indices(); - const auto idxView = meshData->indicesAsArray(); - Mn::Vector3 distPt{1000, 1000, 1000}; - for (uint32_t rawIdx = 0; rawIdx < numIdxs; rawIdx += 3) { - const auto aVert = posArray[idxView[rawIdx + 1]]; - Mn::Vector3 a = posArray[idxView[rawIdx]] - aVert; - Mn::Vector3 b = posArray[idxView[rawIdx + 2]] - aVert; - Mn::Vector3 areaNorm = 0.5 * Mn::Math::cross(a, b); - double surfArea = areaNorm.length(); - ttlSurfaceArea += surfArea; - Mn::Vector3 c = distPt - aVert; - double signedVol = (Mn::Math::dot(c, areaNorm)) / 3.0; - ttlVolume += signedVol; + if (meshTopology == scene::DrawableMeshTopology::ClosedManifold) { + Mn::Vector3 origin{}; + for (uint32_t idx = 0; idx < numIdxs; idx += 3) { + const auto aVert = posArray[idxAra[idx + 1]]; + Mn::Vector3 aVec = posArray[idxAra[idx]] - aVert; + Mn::Vector3 bVec = posArray[idxAra[idx + 2]] - aVert; + Mn::Vector3 areaNormVec = 0.5 * Mn::Math::cross(aVec, bVec); + double surfArea = areaNormVec.length(); + ttlSurfaceArea += surfArea; + Mn::Vector3 c = origin - aVert; + double signedVol = (Mn::Math::dot(c, areaNormVec)) / 3.0; + ttlVolume += signedVol; + } + } else { + // Open or non-manifold meshes won't have an accurate volume calc + for (uint32_t idx = 0; idx < numIdxs; idx += 3) { + const auto aVert = posArray[idxAra[idx + 1]]; + Mn::Vector3 aVec = posArray[idxAra[idx]] - aVert; + Mn::Vector3 bVec = posArray[idxAra[idx + 2]] - aVert; + Mn::Vector3 areaNormVec = 0.5 * Mn::Math::cross(aVec, bVec); + double surfArea = areaNormVec.length(); + ttlSurfaceArea += surfArea; + } } - // set the node's volume and surface area node.setMeshVolume(ttlVolume); node.setMeshSurfaceArea(ttlSurfaceArea); - + // Set whether the mesh is manifold and/or closed/watertight + node.setMeshTopology(meshTopology); } // iEntry } // ResourceManager::computeGeneralMeshVolume @@ -3196,9 +3245,9 @@ void ResourceManager::addComponent( drawableConfig); // instance skinning data // compute the bounding box for the mesh we are adding - if (computeAbsoluteAABBs) { - staticDrawableInfo.emplace_back(StaticDrawableInfo{node, meshID}); - } + // if (computeAbsoluteAABBs) { + staticDrawableInfo.emplace_back(StaticDrawableInfo{node, meshID}); + //} BaseMesh* meshBB = meshes_.at(meshID).get(); node.setMeshBB(computeMeshBB(meshBB)); } diff --git a/src/esp/scene/SceneNode.h b/src/esp/scene/SceneNode.h index 1a93cf872c..ad1f2afea6 100644 --- a/src/esp/scene/SceneNode.h +++ b/src/esp/scene/SceneNode.h @@ -40,6 +40,15 @@ enum class SceneNodeType { OBJECT = 4, // objects added via physics api }; +// Topological nature of the drawable mesh held by this scene node, if exists +enum class DrawableMeshTopology { + Unknown = ID_UNDEFINED, + ClosedManifold = 0, + NonManifold, + OpenManifold + +}; + /** * @brief This enum holds the idx values for the vector of various types * of IDs that can be rendered via semantic sensors. @@ -293,6 +302,11 @@ class SceneNode : public MagnumObject, //! set frustum plane in last frame that culls this node void setFrustumPlaneIndex(int index) { frustumPlaneIndex = index; }; + DrawableMeshTopology getMeshTopology() const { return isClosedManifold_; } + + void setMeshTopology(DrawableMeshTopology _isClosedManifold) { + isClosedManifold_ = _isClosedManifold; + } //! Set this node's drawable's volume void setMeshVolume(double _volume) { volume_ = _volume; } //! Get this node's drawable's volume @@ -341,11 +355,15 @@ class SceneNode : public MagnumObject, //! The absolute translation of this node, updated in clean Magnum::Matrix4 absoluteTransformation_; + //! Whether the drawable mesh is closed and manifold (ever edge is adjacent to + //! exactly 2 faces) + DrawableMeshTopology isClosedManifold_ = DrawableMeshTopology::Unknown; + //! The volume of the drawable mesh held in this node - double volume_ = 0.0f; + double volume_ = 0.0; //! The surface area of the drawable mesh held in this node - double surfArea_ = 0.0f; + double surfArea_ = 0.0; //! the global bounding box for *static* meshes stored at this node // NOTE: this is different from the local bounding box meshBB_ defined above: