Files
IfcOpenShell/src/ifcviewer/InstanceCompose.cpp
T
Dion Moult 1fe4570860 ifcviewer: extract ChunkPlanner + InstanceCompose; add Tier-1 test trio
The chunk planner (Morton sort + greedy pack) and instance composition
(federation × placement matrix chain + world-AABB derive) were inline
helpers in ViewportWindow.cpp. Pulled both out as free-function modules
so the math + lookup logic can be exercised without a Qt window or a
wgpu device. ViewportWindow now delegates; InstanceLookup is a using-
alias to InstanceCompose::InstanceLookup.

Also added an addSubBufferForTesting / clearSubPoolsForTesting seam to
BufferPool so the sub-allocator invariants can be pinned with fake
WGPUBuffer handles. The fakes are never dereferenced; the guard drops
the sub-pools before destructor would call wgpuBufferRelease.

Three new test binaries under src/ifcviewer/tests/, 33 cases / 173
assertions: BufferPool first-fit + alignment + coalescing + multi-
sub-pool isolation; ChunkPlanner Morton split / interleave / stable
sort / greedy-pack monotonicity and single-mesh-oversize; InstanceCompose
identity / translation / order-of-multiplication / large-placement
cancellation against federation false origin / column-major writeback /
findInstance lookup paths.
2026-06-04 17:19:24 +10:00

99 lines
4.5 KiB
C++

/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#include "InstanceCompose.h"
#include <cstring>
#include <limits>
namespace InstanceCompose {
void worldAabbFromLocal(const float local_min[3], const float local_max[3],
const float M[16],
float world_min_out[3], float world_max_out[3]) {
world_min_out[0] = world_min_out[1] = world_min_out[2] =
std::numeric_limits<float>::max();
world_max_out[0] = world_max_out[1] = world_max_out[2] =
-std::numeric_limits<float>::max();
for (int c = 0; c < 8; ++c) {
const float x = (c & 1) ? local_max[0] : local_min[0];
const float y = (c & 2) ? local_max[1] : local_min[1];
const float z = (c & 4) ? local_max[2] : local_min[2];
const float wx = M[0]*x + M[4]*y + M[8] *z + M[12];
const float wy = M[1]*x + M[5]*y + M[9] *z + M[13];
const float wz = M[2]*x + M[6]*y + M[10]*z + M[14];
if (wx < world_min_out[0]) world_min_out[0] = wx;
if (wx > world_max_out[0]) world_max_out[0] = wx;
if (wy < world_min_out[1]) world_min_out[1] = wy;
if (wy > world_max_out[1]) world_max_out[1] = wy;
if (wz < world_min_out[2]) world_min_out[2] = wz;
if (wz > world_max_out[2]) world_max_out[2] = wz;
}
}
void composeInstance(
const double placement_col_major[16],
const Eigen::Matrix4d& federated_false_origin,
const Eigen::Matrix4d& model_transformation,
const Eigen::Matrix4d& coordinate_operation,
const float local_aabb_min[3], const float local_aabb_max[3],
float transform_col_major_out[16],
float world_aabb_min_out[3], float world_aabb_max_out[3]) {
using Mat4dCol = Eigen::Matrix<double, 4, 4, Eigen::ColMajor>;
using Mat4fCol = Eigen::Matrix<float, 4, 4, Eigen::ColMajor>;
const Eigen::Matrix4d P =
Eigen::Map<const Mat4dCol>(placement_col_major);
const Eigen::Matrix4d composed =
federated_false_origin *
model_transformation *
coordinate_operation *
P;
Eigen::Map<Mat4fCol> T_f(transform_col_major_out);
T_f = composed.cast<float>();
worldAabbFromLocal(local_aabb_min, local_aabb_max,
transform_col_major_out,
world_aabb_min_out, world_aabb_max_out);
}
bool findInstanceInModels(
uint32_t object_id,
const std::unordered_map<uint32_t, ModelGpuData>& models,
InstanceLookup& out) {
if (object_id == 0) return false;
for (const auto& [mid, m] : models) {
auto it = m.object_id_to_instance.find(object_id);
if (it == m.object_id_to_instance.end()) continue;
const uint32_t inst_idx = it->second;
if (inst_idx >= m.instances.size()) continue;
const InstanceCpu& inst = m.instances[inst_idx];
out.model_id = mid;
out.mesh_id = inst.mesh_id;
std::memcpy(out.placement_transformation,
inst.placement_transformation,
sizeof(out.placement_transformation));
return true;
}
return false;
}
} // namespace InstanceCompose