From cee2dbcc8511adc016c6a59e020313778b4bd23c Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 14 May 2026 10:06:19 +1000 Subject: [PATCH] Add interface sidecar writeback Port the streamed-model sidecar writeback path into the interface, including packed element metadata, georef persistence, LOD generation, and viewport LOD application. Generated with the assistance of an AI coding tool. --- src/interface/ElementRegistry.cpp | 11 ++++ src/interface/ElementRegistry.h | 1 + src/interface/modules/models/Controller.cpp | 67 +++++++++++++++++++++ src/interface/modules/models/Controller.h | 1 + 4 files changed, 80 insertions(+) diff --git a/src/interface/ElementRegistry.cpp b/src/interface/ElementRegistry.cpp index 724c5ad987..79502d0004 100644 --- a/src/interface/ElementRegistry.cpp +++ b/src/interface/ElementRegistry.cpp @@ -53,6 +53,17 @@ void ElementRegistry::removeModel(uint32_t model_id) { } } +std::vector ElementRegistry::basicElementInfoForModel(uint32_t model_id) const { + std::vector result; + result.reserve(elements_.size()); + for (const auto& [object_id, info] : elements_) { + (void)object_id; + if (info.model_id != model_id) continue; + result.push_back(info); + } + return result; +} + std::optional ElementRegistry::findBasicElementInfo(uint32_t object_id) const { auto it = elements_.find(object_id); if (it == elements_.end()) return std::nullopt; diff --git a/src/interface/ElementRegistry.h b/src/interface/ElementRegistry.h index abad8451da..8d41654cce 100644 --- a/src/interface/ElementRegistry.h +++ b/src/interface/ElementRegistry.h @@ -53,6 +53,7 @@ public: void bindLoader(SceneLoader* loader); void clear(); void removeModel(uint32_t model_id); + std::vector basicElementInfoForModel(uint32_t model_id) const; std::optional findBasicElementInfo(uint32_t object_id) const; std::optional findEntity(uint32_t object_id) const; diff --git a/src/interface/modules/models/Controller.cpp b/src/interface/modules/models/Controller.cpp index f39c3c8a79..f05b514124 100644 --- a/src/interface/modules/models/Controller.cpp +++ b/src/interface/modules/models/Controller.cpp @@ -27,14 +27,20 @@ #include "../../SessionState.h" #include "AddModelDialog.h" #include "../../../ifcviewer/Federation.h" +#include "../../../ifcviewer/LodBuilder.h" #include "../../../ifcviewer/SceneLoader.h" +#include "../../../ifcviewer/SidecarCache.h" #include "../../../ifcviewer/ViewportWindow.h" +#include #include +#include #include #include #include +#include + namespace ifcinterface::modules::models { ModelsPanelController::ModelsPanelController(QWidget* host, @@ -102,6 +108,7 @@ void ModelsPanelController::bindLoader(SceneLoader* loader) { }); connect(loader, &SceneLoader::loadedFromStream, this, [this, loader](uint32_t mid, qint64 elapsed_ms) { + writeSidecarForModel(loader, mid); session_state_->setStatusMessage( "Loaded", QString("%1 streamed in %2") @@ -226,4 +233,64 @@ QString ModelsPanelController::formatElapsed(qint64 ms) const { : QString::number(ms) + " ms"; } +void ModelsPanelController::writeSidecarForModel(SceneLoader* loader, uint32_t mid) const { + if (!loader || !viewport_ || !session_state_) return; + + SidecarData sidecar_data; + if (!viewport_->snapshotModel(mid, sidecar_data)) return; + + if (const ModelGeoref* georef = loader->modelGeoref(mid)) { + sidecar_data.has_coordinate_operation = georef->has_coordinate_operation ? 1 : 0; + Eigen::Map>( + sidecar_data.coordinate_operation_meters) = georef->coordinate_operation_meters; + sidecar_data.project_length_to_meters = georef->units.project_length_to_meters; + sidecar_data.map_unit_to_meters = georef->units.map_unit_to_meters; + } + + auto* element_registry = session_state_->elementRegistry(); + if (element_registry) { + for (const auto& info : element_registry->basicElementInfoForModel(mid)) { + PackedElementInfo packed; + packed.object_id = info.object_id; + packed.model_id = info.model_id; + packed.ifc_id = info.ifc_id; + packed.parent_id = info.parent_id; + + const std::string guid = info.guid.toStdString(); + packed.guid_offset = static_cast(sidecar_data.string_table.size()); + packed.guid_length = static_cast(guid.size()); + sidecar_data.string_table += guid; + + const std::string name = info.name.toStdString(); + packed.name_offset = static_cast(sidecar_data.string_table.size()); + packed.name_length = static_cast(name.size()); + sidecar_data.string_table += name; + + const std::string type = info.type.toStdString(); + packed.type_offset = static_cast(sidecar_data.string_table.size()); + packed.type_length = static_cast(type.size()); + sidecar_data.string_table += type; + + sidecar_data.elements.push_back(packed); + } + } + + QElapsedTimer lod_timer; + lod_timer.start(); + buildLods(sidecar_data); + const LodStats lod_stats = summariseLods(sidecar_data); + qDebug(" LOD build: %lld ms — %u/%u meshes got LOD1 " + "(%u tris -> %u tris for those meshes)", + lod_timer.elapsed(), + lod_stats.meshes_with_lod1, lod_stats.meshes_total, + lod_stats.tris_lod0_for_lod1, lod_stats.tris_lod1); + + viewport_->applyLodExtension(mid, sidecar_data); + + QElapsedTimer sidecar_timer; + sidecar_timer.start(); + const bool ok = writeSidecar(loader->filePath(mid).toStdString(), sidecar_data); + qDebug(" Sidecar write: %lld ms (%s)", sidecar_timer.elapsed(), ok ? "ok" : "FAILED"); +} + } // namespace ifcinterface::modules::models diff --git a/src/interface/modules/models/Controller.h b/src/interface/modules/models/Controller.h index d1848d4e81..f5b4d02bc2 100644 --- a/src/interface/modules/models/Controller.h +++ b/src/interface/modules/models/Controller.h @@ -54,6 +54,7 @@ public: private: QString formatElapsed(qint64 ms) const; + void writeSidecarForModel(SceneLoader* loader, uint32_t mid) const; QWidget* host_ = nullptr; ModelsPanel* widget_ = nullptr;