mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-10 02:53:51 +00:00
feat(editor): --info-wob-stats + fix weld hash collision bug
Add --info-wob-stats reporting per-group + aggregate triangle counts, surface area, edge analysis, and watertight check for WOB buildings. Same flag surface as --info-mesh-stats including --weld <eps> for true topological closure check on per-face-vertex meshes. Also fixes a correctness bug in the weld implementation of --info-mesh-stats: the previous code used a 64-bit hash of the quantized position as the equality key, which gave false-positive collisions that incorrectly merged distinct vertices. A unit cube's 8 corners collapsed to 2 positions under the buggy hash. Replace with std::map keyed on the actual quantized (qx, qy, qz) tuple so equality is exact. Re-verified: cube 8→8 watertight YES; firepit 240→80 watertight YES (was wrongly reporting 56 unique with 48 non-manifold edges); tent_solid 18→6 watertight YES; tent_fixed 21→9 with 5 boundary edges at the door perimeter (correct — door is intentionally open).
This commit is contained in:
parent
a7989cc7ab
commit
4112b6d257
4 changed files with 208 additions and 12 deletions
|
|
@ -11,8 +11,10 @@
|
|||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -383,20 +385,21 @@ int handleInfoMeshStats(int& i, int argc, char** argv) {
|
|||
std::vector<uint32_t> canon(wom.vertices.size());
|
||||
std::size_t uniquePositions = 0;
|
||||
if (useWeld) {
|
||||
// Use the quantized (qx, qy, qz) tuple as the equality key —
|
||||
// a hash key would risk false-positive collisions that
|
||||
// incorrectly merge distinct corners (e.g. a cube's 8 corners
|
||||
// collapsing to 2). std::map gives exact-match equality at
|
||||
// O(log n) per op which is fast enough for any real mesh.
|
||||
const float invEps = 1.0f / std::max(weldEps, 1e-9f);
|
||||
std::unordered_map<uint64_t, uint32_t> bucket;
|
||||
bucket.reserve(wom.vertices.size());
|
||||
auto posKey = [&](const glm::vec3& p) -> uint64_t {
|
||||
int64_t qx = static_cast<int64_t>(std::lround(p.x * invEps));
|
||||
int64_t qy = static_cast<int64_t>(std::lround(p.y * invEps));
|
||||
int64_t qz = static_cast<int64_t>(std::lround(p.z * invEps));
|
||||
uint64_t h = static_cast<uint64_t>(qx) * 0x9E3779B185EBCA87ULL;
|
||||
h ^= static_cast<uint64_t>(qy) * 0xC2B2AE3D27D4EB4FULL;
|
||||
h ^= static_cast<uint64_t>(qz) * 0x165667B19E3779F9ULL;
|
||||
return h;
|
||||
using QKey = std::tuple<int64_t, int64_t, int64_t>;
|
||||
std::map<QKey, uint32_t> bucket;
|
||||
auto qkey = [&](const glm::vec3& p) -> QKey {
|
||||
return {static_cast<int64_t>(std::lround(p.x * invEps)),
|
||||
static_cast<int64_t>(std::lround(p.y * invEps)),
|
||||
static_cast<int64_t>(std::lround(p.z * invEps))};
|
||||
};
|
||||
for (std::size_t v = 0; v < wom.vertices.size(); ++v) {
|
||||
uint64_t k = posKey(wom.vertices[v].position);
|
||||
QKey k = qkey(wom.vertices[v].position);
|
||||
auto it = bucket.find(k);
|
||||
if (it == bucket.end()) {
|
||||
bucket.emplace(k, static_cast<uint32_t>(v));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue