From ad6822ac856fb497a771ba0580eb6c188fca260f Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 5 Jun 2026 13:32:19 +1000 Subject: [PATCH] ifcviewer: move composeInstanceFromPlacement into ViewportCore (#84-d) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First method-body migration. composeInstanceFromPlacement composes the federated-false-origin × model-transformation × coordinate-operation × placement chain and re-derives the world AABB; it's a small, self-contained method that only reads scene state and one matrix. Moved: Eigen::Matrix4d federated_false_origin_meters_ (storage → core_) void composeInstanceFromPlacement(InstanceCpu&, ...) (body → core_) ViewportWindow keeps: - alias reference to federated_false_origin_meters_ (existing setFederatedFalseOrigin call site still writes through it) - no method declaration — internal callers route through core_ Internal caller (recomposeAndUploadModel) now invokes core_.composeInstanceFromPlacement; once recomposeAndUploadModel itself moves into ViewportCore the call shortens back. Pattern for the rest of #84: state moves, then method body moves, then internal callers update. Each commit leaves desktop / bonsai / web green and tests 100/100. This is one of many such steps. --- src/ifcviewer/ViewportCore.cpp | 34 +++++++++++++++++++++++++++++ src/ifcviewer/ViewportCore.h | 20 +++++++++++++++++ src/ifcviewer/ViewportWindow.cpp | 37 ++++---------------------------- src/ifcviewer/ViewportWindow.h | 8 ++++--- 4 files changed, 63 insertions(+), 36 deletions(-) diff --git a/src/ifcviewer/ViewportCore.cpp b/src/ifcviewer/ViewportCore.cpp index 19f06f6a33..bc9d168839 100644 --- a/src/ifcviewer/ViewportCore.cpp +++ b/src/ifcviewer/ViewportCore.cpp @@ -19,5 +19,39 @@ #include "ViewportCore.h" +#include "InstanceCompose.h" + ViewportCore::ViewportCore(ViewportHost* host) : host_(host) {} ViewportCore::~ViewportCore() = default; + +void ViewportCore::composeInstanceFromPlacement(InstanceCpu& inst, + const ModelGpuData& m) const { + if (inst.mesh_id < m.meshes.size()) { + const MeshInfo& mi = m.meshes[inst.mesh_id]; + InstanceCompose::composeInstance( + inst.placement_transformation, + federated_false_origin_meters_, + m.model_transformation_meters, + m.coordinate_operation_meters, + mi.local_aabb_min, mi.local_aabb_max, + inst.transform, + inst.world_aabb_min, inst.world_aabb_max); + } else { + // Unknown mesh id: still compose the transform (downstream may + // use it for picking / readback even without geometry), but + // emit a degenerate world AABB so cull doesn't pick this up. + const float zero[3] = {0.0f, 0.0f, 0.0f}; + InstanceCompose::composeInstance( + inst.placement_transformation, + federated_false_origin_meters_, + m.model_transformation_meters, + m.coordinate_operation_meters, + zero, zero, + inst.transform, + inst.world_aabb_min, inst.world_aabb_max); + for (int a = 0; a < 3; ++a) { + inst.world_aabb_min[a] = 0.0f; + inst.world_aabb_max[a] = 0.0f; + } + } +} diff --git a/src/ifcviewer/ViewportCore.h b/src/ifcviewer/ViewportCore.h index 30f128fbb2..52ce2ef7a8 100644 --- a/src/ifcviewer/ViewportCore.h +++ b/src/ifcviewer/ViewportCore.h @@ -35,10 +35,13 @@ #include +#include + #include #include #include "BufferPool.h" +#include "InstancedGeometry.h" #include "ModelGpuData.h" #include "StreamingThread.h" #include "ViewportHost.h" @@ -53,6 +56,18 @@ public: ViewportHost* host() const { return host_; } + // ---- Scene-mutation methods -------------------------------------------- + // + // composeInstanceFromPlacement composes the per-instance + // transform = federated_false_origin × model_transformation + // × coordinate_operation × placement + // (all in metres, double precision) and rebakes the world AABB + // from the mesh-local one. Used by the per-model recompose path + // after any of the four federation matrices change. Pure scene + // math — no GPU touch. + void composeInstanceFromPlacement(InstanceCpu& inst, + const ModelGpuData& m) const; + // Friend access for ViewportWindow's reference proxies. As each // render method moves into ViewportCore it stops needing these // (it touches the fields directly); once everything has migrated @@ -129,6 +144,11 @@ private: // the sidecar's local object_ids by base_object_id_so_far so picks // are unambiguous across models. uint32_t next_object_id_ = 1; + + // Federation false origin (metres, double precision). Applied to + // every instance composition so geometry rebased through a large + // model offset doesn't lose float32 precision near the GPU origin. + Eigen::Matrix4d federated_false_origin_meters_ = Eigen::Matrix4d::Identity(); }; #endif // VIEWPORTCORE_H diff --git a/src/ifcviewer/ViewportWindow.cpp b/src/ifcviewer/ViewportWindow.cpp index cc09eb9d1b..71f3e5f7ce 100644 --- a/src/ifcviewer/ViewportWindow.cpp +++ b/src/ifcviewer/ViewportWindow.cpp @@ -638,7 +638,8 @@ ViewportWindow::ViewportWindow(QWindow* parent) streaming_thread_(core_.streaming_thread_), models_gpu_ (core_.models_gpu_), next_model_id_ (core_.next_model_id_), - next_object_id_ (core_.next_object_id_) { + next_object_id_ (core_.next_object_id_), + federated_false_origin_meters_(core_.federated_false_origin_meters_) { // wgpu doesn't need a GL context; we just need a real native window // whose backing layer matches the GPU API wgpu will drive. // @@ -1377,37 +1378,7 @@ void ViewportWindow::setModelTransformation(uint32_t model_id, recomposeAndUploadModel(model_id); } -void ViewportWindow::composeInstanceFromPlacement(InstanceCpu& inst, - const ModelGpuData& m) const { - if (inst.mesh_id < m.meshes.size()) { - const MeshInfo& mi = m.meshes[inst.mesh_id]; - InstanceCompose::composeInstance( - inst.placement_transformation, - federated_false_origin_meters_, - m.model_transformation_meters, - m.coordinate_operation_meters, - mi.local_aabb_min, mi.local_aabb_max, - inst.transform, - inst.world_aabb_min, inst.world_aabb_max); - } else { - // Unknown mesh id: still compose the transform (downstream may - // use it for picking / readback even without geometry), but - // emit a degenerate world AABB so cull doesn't pick this up. - const float zero[3] = {0.0f, 0.0f, 0.0f}; - InstanceCompose::composeInstance( - inst.placement_transformation, - federated_false_origin_meters_, - m.model_transformation_meters, - m.coordinate_operation_meters, - zero, zero, - inst.transform, - inst.world_aabb_min, inst.world_aabb_max); - for (int a = 0; a < 3; ++a) { - inst.world_aabb_min[a] = 0.0f; - inst.world_aabb_max[a] = 0.0f; - } - } -} +// composeInstanceFromPlacement moved to ViewportCore (#84-d). void ViewportWindow::recomposeAndUploadModel(uint32_t model_id) { if (!wgpu_initialized_) return; @@ -1419,7 +1390,7 @@ void ViewportWindow::recomposeAndUploadModel(uint32_t model_id) { std::vector gpu(m.instances.size()); for (size_t i = 0; i < m.instances.size(); ++i) { InstanceCpu& inst = m.instances[i]; - composeInstanceFromPlacement(inst, m); + core_.composeInstanceFromPlacement(inst, m); InstanceGpu& dst = gpu[i]; std::memcpy(dst.transform, inst.transform, sizeof(dst.transform)); diff --git a/src/ifcviewer/ViewportWindow.h b/src/ifcviewer/ViewportWindow.h index e2ed9351ba..237555db61 100644 --- a/src/ifcviewer/ViewportWindow.h +++ b/src/ifcviewer/ViewportWindow.h @@ -624,8 +624,9 @@ private: // in double; the cast to float happens last so large IFC placements // get cancelled by the federation false origin before precision is // narrowed. Mirrors GL ViewportWindow::composeInstanceFromPlacement. - void composeInstanceFromPlacement(InstanceCpu& inst, - const ModelGpuData& m) const; + // Implementation lives in ViewportCore now (#84-d); this declaration + // stayed during the move and forwards to core_ — once every internal + // caller routes through ViewportCore directly the forwarder goes away. // Walk every instance of `model_id`, recompose its transform from the // current federation matrices, refresh per-chunk world AABBs, and @@ -1120,7 +1121,8 @@ private: // FederatedFalseOrigin matrix, in metres. Default identity. Stored // but not yet applied to per-instance composed transforms — the // recompose pass arrives with the federation-load OOM work. - Eigen::Matrix4d federated_false_origin_meters_ = Eigen::Matrix4d::Identity(); + // Federation false-origin alias (storage in core_). + Eigen::Matrix4d& federated_false_origin_meters_; // Per-frame LOD selection counts, mutated from cullModelCpuCompute // and reset after the [frame] heartbeat prints them. Keeps an eye