Skip to content

Commit 1ed8b18

Browse files
committed
--expand calculation to test for mesh topology - watertight and manifold.
1 parent 6611c5c commit 1ed8b18

File tree

2 files changed

+94
-27
lines changed

2 files changed

+94
-27
lines changed

src/esp/assets/ResourceManager.cpp

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,37 +1058,86 @@ void ResourceManager::computeGeneralMeshAreaAndVolume(
10581058
meshData->positions3DAsArray(0);
10591059
Mn::MeshTools::transformPointsInPlace(absTransforms[iEntry], posArray);
10601060

1061+
// Getting the view properly relies on having the appropriate type of the
1062+
// loaded data
1063+
// const auto idxView = meshData->indices<std::uint32_t>();
1064+
const auto idxAra = meshData->indicesAsArray();
1065+
// # of indices
1066+
uint32_t numIdxs = meshData->indexCount();
1067+
// Assuming no duplicate vertices with different idxs
1068+
// Determine that all edges have exactly 2 sides ->
1069+
// idxAra describes exactly 2 pairs of the same idxs, a->b and b->a
1070+
std::unordered_map<uint64_t, int> edgeCount;
1071+
std::unordered_map<uint64_t, int> altEdgeCount;
1072+
scene::DrawableMeshTopology meshTopology =
1073+
scene::DrawableMeshTopology::ClosedManifold;
1074+
for (uint32_t idx = 0; idx < numIdxs; idx += 3) {
1075+
uint64_t vals[] = {uint64_t(idxAra[idx]), uint64_t(idxAra[idx + 1]),
1076+
uint64_t(idxAra[idx + 2])};
1077+
uint64_t shift_vals[] = {vals[0] << 32, vals[1] << 32, vals[2] << 32};
1078+
// for each edge in poly
1079+
for (uint32_t i = 0; i < 3; ++i) {
1080+
uint32_t next_i = (i + 1) % 3;
1081+
auto res =
1082+
edgeCount.emplace(std::make_pair(shift_vals[i] + vals[next_i], 0));
1083+
if (!res.second) {
1084+
res.first->second += 1;
1085+
// Duplicate edge with same orientation
1086+
meshTopology = scene::DrawableMeshTopology::NonManifold;
1087+
}
1088+
// Alt edge placement - verify the alternate edge is present
1089+
altEdgeCount.emplace(std::make_pair(shift_vals[next_i] + vals[i], 0));
1090+
}
1091+
}
1092+
// If still closed manifold then check that every edge has an alt edge
1093+
// present
1094+
if (meshTopology == scene::DrawableMeshTopology::ClosedManifold) {
1095+
for (const auto entry : edgeCount) {
1096+
altEdgeCount.erase(entry.first);
1097+
}
1098+
if (altEdgeCount.size() > 0) {
1099+
meshTopology = scene::DrawableMeshTopology::OpenManifold;
1100+
}
1101+
}
1102+
1103+
// locate the scene node which contains the current drawable
1104+
scene::SceneNode& node = staticDrawableInfo[iEntry].node;
10611105
// Surface area of the mesh : .5 * ba.cross(bc)
10621106
double ttlSurfaceArea = 0.0;
1063-
10641107
// Volume of the mesh : 1/6 * (OA.dot(ba.cross(bc)))
10651108
// Where O is a distant vertex
1109+
// Only applicable on closed manifold meshes (i.e. all edges have exactly
1110+
// 2 faces)
10661111
double ttlVolume = 0.0f;
1067-
1068-
// locate the scene node which contains the current drawable
1069-
scene::SceneNode& node = staticDrawableInfo[iEntry].node;
1070-
// # of indices
1071-
int numIdxs = meshData->indexCount();
1072-
1073-
// const auto idxView = meshData->indices<std::uint32_t>();
1074-
const auto idxView = meshData->indicesAsArray();
1075-
Mn::Vector3 distPt{1000, 1000, 1000};
1076-
for (uint32_t rawIdx = 0; rawIdx < numIdxs; rawIdx += 3) {
1077-
const auto aVert = posArray[idxView[rawIdx + 1]];
1078-
Mn::Vector3 a = posArray[idxView[rawIdx]] - aVert;
1079-
Mn::Vector3 b = posArray[idxView[rawIdx + 2]] - aVert;
1080-
Mn::Vector3 areaNorm = 0.5 * Mn::Math::cross(a, b);
1081-
double surfArea = areaNorm.length();
1082-
ttlSurfaceArea += surfArea;
1083-
Mn::Vector3 c = distPt - aVert;
1084-
double signedVol = (Mn::Math::dot(c, areaNorm)) / 3.0;
1085-
ttlVolume += signedVol;
1112+
if (meshTopology == scene::DrawableMeshTopology::ClosedManifold) {
1113+
Mn::Vector3 origin{};
1114+
for (uint32_t idx = 0; idx < numIdxs; idx += 3) {
1115+
const auto aVert = posArray[idxAra[idx + 1]];
1116+
Mn::Vector3 aVec = posArray[idxAra[idx]] - aVert;
1117+
Mn::Vector3 bVec = posArray[idxAra[idx + 2]] - aVert;
1118+
Mn::Vector3 areaNormVec = 0.5 * Mn::Math::cross(aVec, bVec);
1119+
double surfArea = areaNormVec.length();
1120+
ttlSurfaceArea += surfArea;
1121+
Mn::Vector3 c = origin - aVert;
1122+
double signedVol = (Mn::Math::dot(c, areaNormVec)) / 3.0;
1123+
ttlVolume += signedVol;
1124+
}
1125+
} else {
1126+
// Open or non-manifold meshes won't have an accurate volume calc
1127+
for (uint32_t idx = 0; idx < numIdxs; idx += 3) {
1128+
const auto aVert = posArray[idxAra[idx + 1]];
1129+
Mn::Vector3 aVec = posArray[idxAra[idx]] - aVert;
1130+
Mn::Vector3 bVec = posArray[idxAra[idx + 2]] - aVert;
1131+
Mn::Vector3 areaNormVec = 0.5 * Mn::Math::cross(aVec, bVec);
1132+
double surfArea = areaNormVec.length();
1133+
ttlSurfaceArea += surfArea;
1134+
}
10861135
}
1087-
10881136
// set the node's volume and surface area
10891137
node.setMeshVolume(ttlVolume);
10901138
node.setMeshSurfaceArea(ttlSurfaceArea);
1091-
1139+
// Set whether the mesh is manifold and/or closed/watertight
1140+
node.setMeshTopology(meshTopology);
10921141
} // iEntry
10931142

10941143
} // ResourceManager::computeGeneralMeshVolume
@@ -3196,9 +3245,9 @@ void ResourceManager::addComponent(
31963245
drawableConfig); // instance skinning data
31973246

31983247
// compute the bounding box for the mesh we are adding
3199-
if (computeAbsoluteAABBs) {
3200-
staticDrawableInfo.emplace_back(StaticDrawableInfo{node, meshID});
3201-
}
3248+
// if (computeAbsoluteAABBs) {
3249+
staticDrawableInfo.emplace_back(StaticDrawableInfo{node, meshID});
3250+
//}
32023251
BaseMesh* meshBB = meshes_.at(meshID).get();
32033252
node.setMeshBB(computeMeshBB(meshBB));
32043253
}

src/esp/scene/SceneNode.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ enum class SceneNodeType {
4040
OBJECT = 4, // objects added via physics api
4141
};
4242

43+
// Topological nature of the drawable mesh held by this scene node, if exists
44+
enum class DrawableMeshTopology {
45+
Unknown = ID_UNDEFINED,
46+
ClosedManifold = 0,
47+
NonManifold,
48+
OpenManifold
49+
50+
};
51+
4352
/**
4453
* @brief This enum holds the idx values for the vector of various types
4554
* of IDs that can be rendered via semantic sensors.
@@ -293,6 +302,11 @@ class SceneNode : public MagnumObject,
293302
//! set frustum plane in last frame that culls this node
294303
void setFrustumPlaneIndex(int index) { frustumPlaneIndex = index; };
295304

305+
DrawableMeshTopology getMeshTopology() const { return isClosedManifold_; }
306+
307+
void setMeshTopology(DrawableMeshTopology _isClosedManifold) {
308+
isClosedManifold_ = _isClosedManifold;
309+
}
296310
//! Set this node's drawable's volume
297311
void setMeshVolume(double _volume) { volume_ = _volume; }
298312
//! Get this node's drawable's volume
@@ -341,11 +355,15 @@ class SceneNode : public MagnumObject,
341355
//! The absolute translation of this node, updated in clean
342356
Magnum::Matrix4 absoluteTransformation_;
343357

358+
//! Whether the drawable mesh is closed and manifold (ever edge is adjacent to
359+
//! exactly 2 faces)
360+
DrawableMeshTopology isClosedManifold_ = DrawableMeshTopology::Unknown;
361+
344362
//! The volume of the drawable mesh held in this node
345-
double volume_ = 0.0f;
363+
double volume_ = 0.0;
346364

347365
//! The surface area of the drawable mesh held in this node
348-
double surfArea_ = 0.0f;
366+
double surfArea_ = 0.0;
349367

350368
//! the global bounding box for *static* meshes stored at this node
351369
// NOTE: this is different from the local bounding box meshBB_ defined above:

0 commit comments

Comments
 (0)