diff --git a/src/bonsaiviewer/ElementRegistry.cpp b/src/bonsaiviewer/ElementRegistry.cpp index d633e70f17..33ec5c8cdd 100644 --- a/src/bonsaiviewer/ElementRegistry.cpp +++ b/src/bonsaiviewer/ElementRegistry.cpp @@ -88,37 +88,37 @@ std::optional ElementRegistry::findEntity(uint32_t object_id) con } } -void ElementRegistry::onSidecarElementsReady(uint32_t /*mid*/, +void ElementRegistry::onSidecarElementsReady(uint32_t /*model_id*/, std::vector elements, std::string string_table) { - auto str = [&](uint32_t offset, uint32_t length) -> QString { + auto string_from_table = [&](uint32_t offset, uint32_t length) -> QString { if (length == 0 || offset + length > string_table.size()) return {}; return QString::fromStdString(string_table.substr(offset, length)); }; - for (const auto& pe : elements) { + for (const auto& packed_element : elements) { BasicElementInfo info; - info.object_id = pe.object_id; - info.model_id = pe.model_id; - info.ifc_id = pe.ifc_id; - info.parent_id = pe.parent_id; - info.guid = str(pe.guid_offset, pe.guid_length); - info.name = str(pe.name_offset, pe.name_length); - info.type = str(pe.type_offset, pe.type_length); + info.object_id = packed_element.object_id; + info.model_id = packed_element.model_id; + info.ifc_id = packed_element.ifc_id; + info.parent_id = packed_element.parent_id; + info.guid = string_from_table(packed_element.guid_offset, packed_element.guid_length); + info.name = string_from_table(packed_element.name_offset, packed_element.name_length); + info.type = string_from_table(packed_element.type_offset, packed_element.type_length); elements_[info.object_id] = info; } } -void ElementRegistry::onStreamedElementsReady(uint32_t /*mid*/, std::vector elements) { - for (const auto& e : elements) { +void ElementRegistry::onStreamedElementsReady(uint32_t /*model_id*/, std::vector elements) { + for (const auto& element : elements) { BasicElementInfo info; - info.object_id = e.object_id; - info.model_id = e.model_id; - info.ifc_id = e.ifc_id; - info.parent_id = e.parent_id; - info.guid = QString::fromStdString(e.guid); - info.name = QString::fromStdString(e.name); - info.type = QString::fromStdString(e.type); + info.object_id = element.object_id; + info.model_id = element.model_id; + info.ifc_id = element.ifc_id; + info.parent_id = element.parent_id; + info.guid = QString::fromStdString(element.guid); + info.name = QString::fromStdString(element.name); + info.type = QString::fromStdString(element.type); elements_[info.object_id] = info; } } diff --git a/src/bonsaiviewer/ElementRegistry.h b/src/bonsaiviewer/ElementRegistry.h index b804919f08..469e4b5996 100644 --- a/src/bonsaiviewer/ElementRegistry.h +++ b/src/bonsaiviewer/ElementRegistry.h @@ -58,10 +58,10 @@ public: std::optional findEntity(uint32_t object_id) const; private: - void onSidecarElementsReady(uint32_t mid, + void onSidecarElementsReady(uint32_t model_id, std::vector elements, std::string string_table); - void onStreamedElementsReady(uint32_t mid, std::vector elements); + void onStreamedElementsReady(uint32_t model_id, std::vector elements); SceneLoader* loader_ = nullptr; std::unordered_map elements_; diff --git a/src/bonsaiviewer/SessionState.cpp b/src/bonsaiviewer/SessionState.cpp index 91189e9680..4a5563e05d 100644 --- a/src/bonsaiviewer/SessionState.cpp +++ b/src/bonsaiviewer/SessionState.cpp @@ -64,25 +64,25 @@ void SessionState::createLoader(ViewportWindow* viewport) { }); connect(loader_, &SceneLoader::progressChanged, this, &SessionState::setProgress); connect(loader_, &SceneLoader::loadedFromSidecar, this, - [this, format_elapsed](uint32_t mid, qint64 elapsed_ms) { + [this, format_elapsed](uint32_t model_id, qint64 elapsed_ms) { setStatusMessage("Loaded", QString("%1 from cache in %2") - .arg(loader_->displayName(mid)) + .arg(loader_->displayName(model_id)) .arg(format_elapsed(elapsed_ms))); endProgress(); - emit modelGeometryReady(mid); + emit modelGeometryReady(model_id); }); connect(loader_, &SceneLoader::loadedFromStream, this, - [this, format_elapsed](uint32_t mid, qint64 elapsed_ms) { + [this, format_elapsed](uint32_t model_id, qint64 elapsed_ms) { setStatusMessage("Loaded", QString("%1 streamed in %2") - .arg(loader_->displayName(mid)) + .arg(loader_->displayName(model_id)) .arg(format_elapsed(elapsed_ms))); endProgress(); - emit modelGeometryReady(mid); + emit modelGeometryReady(model_id); }); - connect(loader_, &SceneLoader::loadCancelled, this, [this](uint32_t mid) { - setStatusMessage("Cancelled", loader_->displayName(mid)); + connect(loader_, &SceneLoader::loadCancelled, this, [this](uint32_t model_id) { + setStatusMessage("Cancelled", loader_->displayName(model_id)); endProgress(); }); connect(loader_, &SceneLoader::loadError, this, diff --git a/src/bonsaiviewer/modules/connectors/PickerDialog.cpp b/src/bonsaiviewer/modules/connectors/PickerDialog.cpp index 5da7dc0ec2..4cc11c4eef 100644 --- a/src/bonsaiviewer/modules/connectors/PickerDialog.cpp +++ b/src/bonsaiviewer/modules/connectors/PickerDialog.cpp @@ -60,9 +60,9 @@ ConnectorPickerDialog::ConnectorPickerDialog(const std::vectorsetSpacing(components::style::metrics::padding); QList buttons; - for (const auto& m : manifests) { - auto* button = components::buttons::makeButton(m.name, ":/icons/cloud-square.svg", choices); - const QString id = m.id; + for (const auto& manifest : manifests) { + auto* button = components::buttons::makeButton(manifest.name, ":/icons/cloud-square.svg", choices); + const QString id = manifest.id; connect(button, &QToolButton::clicked, this, [this, id]() { selected_id_ = id; accept(); diff --git a/src/bonsaiviewer/modules/connectors/Process.cpp b/src/bonsaiviewer/modules/connectors/Process.cpp index 1f27222391..b85e7d54f5 100644 --- a/src/bonsaiviewer/modules/connectors/Process.cpp +++ b/src/bonsaiviewer/modules/connectors/Process.cpp @@ -189,8 +189,8 @@ void ConnectorProcess::dispatchLine(const QByteArray& line) { void ConnectorProcess::failPendingAndClear(int code, const QString& message) { QHash snapshot; snapshot.swap(pending_); - for (const auto& p : snapshot) { - if (p.on_error) p.on_error(code, message); + for (const auto& pending_request : snapshot) { + if (pending_request.on_error) pending_request.on_error(code, message); } } diff --git a/src/bonsaiviewer/modules/connectors/Registry.cpp b/src/bonsaiviewer/modules/connectors/Registry.cpp index 1ec2c6ad86..1a244d8c1b 100644 --- a/src/bonsaiviewer/modules/connectors/Registry.cpp +++ b/src/bonsaiviewer/modules/connectors/Registry.cpp @@ -48,14 +48,14 @@ void ConnectorRegistry::refresh() { // exec changed under us. Surviving entries keep their running process. for (auto it = processes_.begin(); it != processes_.end();) { const QString id = it.key(); - ConnectorProcess* p = it.value(); - const ConnectorManifest* now = manifestFor(id); - const bool stale = !now || !p || - p->manifest().exec_path != now->exec_path; + ConnectorProcess* process = it.value(); + const ConnectorManifest* current_manifest = manifestFor(id); + const bool stale = !current_manifest || !process || + process->manifest().exec_path != current_manifest->exec_path; if (stale) { - if (p) { - p->shutdown(); - p->deleteLater(); + if (process) { + process->shutdown(); + process->deleteLater(); } it = processes_.erase(it); } else { @@ -65,8 +65,8 @@ void ConnectorRegistry::refresh() { } const ConnectorManifest* ConnectorRegistry::manifestFor(const QString& id) const { - for (const auto& m : manifests_) { - if (m.id == id) return &m; + for (const auto& manifest : manifests_) { + if (manifest.id == id) return &manifest; } return nullptr; } @@ -90,7 +90,7 @@ ConnectorProcess* ConnectorRegistry::get(const QString& id) { processes_.insert(id, proc); connect(proc, &ConnectorProcess::crashed, this, [this, id](const QString& message) { qWarning() << "ifcviewer connectors:" << message; - if (auto* p = processes_.take(id)) p->deleteLater(); + if (auto* process = processes_.take(id)) process->deleteLater(); }); return proc; } @@ -99,9 +99,9 @@ void ConnectorRegistry::shutdownAll() { const auto procs = processes_; processes_.clear(); for (auto it = procs.begin(); it != procs.end(); ++it) { - if (auto* p = it.value()) { - p->shutdown(); - p->deleteLater(); + if (auto* process = it.value()) { + process->shutdown(); + process->deleteLater(); } } } diff --git a/src/bonsaiviewer/modules/models/Commands.cpp b/src/bonsaiviewer/modules/models/Commands.cpp index 3736d68293..4aadb3ca5a 100644 --- a/src/bonsaiviewer/modules/models/Commands.cpp +++ b/src/bonsaiviewer/modules/models/Commands.cpp @@ -91,24 +91,24 @@ QString formatElapsed(qint64 ms) { } // namespace -void toggleVisibility(SessionState& s, ItemKind kind, const QString& id) { - Federation* fed = s.federation(); +void toggleVisibility(SessionState& session, ItemKind kind, const QString& id) { + Federation* federation = session.federation(); if (kind == ItemKind::Group) { - const Federation::Group* group = fed->findGroupById(id); + const Federation::Group* group = federation->findGroupById(id); if (!group) return; - fed->setGroupVisible(id, !group->visible); - s.notifyVisibilityChanged(); - s.setStatusMessage("Models", group->visible ? "Group hidden" : "Group shown"); + federation->setGroupVisible(id, !group->visible); + session.notifyVisibilityChanged(); + session.setStatusMessage("Models", group->visible ? "Group hidden" : "Group shown"); } else { - const Federation::Model* model = fed->findById(id); + const Federation::Model* model = federation->findById(id); if (!model) return; - fed->setModelVisible(id, !model->visible); - s.notifyVisibilityChanged(); - s.setStatusMessage("Models", model->visible ? "Model hidden" : "Model shown"); + federation->setModelVisible(id, !model->visible); + session.notifyVisibilityChanged(); + session.setStatusMessage("Models", model->visible ? "Model hidden" : "Model shown"); } } -void addGroup(SessionState& s, QWidget& host, const QString& parent_group_id) { +void addGroup(SessionState& session, QWidget& host, const QString& parent_group_id) { bool ok = false; const QString name = QInputDialog::getText( &host, "New Group", "Group name:", QLineEdit::Normal, "Group", &ok); @@ -116,13 +116,13 @@ void addGroup(SessionState& s, QWidget& host, const QString& parent_group_id) { const QString trimmed = name.trimmed(); if (trimmed.isEmpty()) return; - s.federation()->addGroup(trimmed, parent_group_id); - s.notifyFederationChanged(); - s.setStatusMessage("Models", "Group added"); + session.federation()->addGroup(trimmed, parent_group_id); + session.notifyFederationChanged(); + session.setStatusMessage("Models", "Group added"); } -void renameGroup(SessionState& s, QWidget& host, const QString& group_id) { - const Federation::Group* group = s.federation()->findGroupById(group_id); +void renameGroup(SessionState& session, QWidget& host, const QString& group_id) { + const Federation::Group* group = session.federation()->findGroupById(group_id); if (!group) return; bool ok = false; @@ -132,27 +132,27 @@ void renameGroup(SessionState& s, QWidget& host, const QString& group_id) { const QString trimmed = name.trimmed(); if (trimmed.isEmpty()) return; - s.federation()->setGroupName(group_id, trimmed); - s.notifyFederationChanged(); - s.setStatusMessage("Models", "Group renamed"); + session.federation()->setGroupName(group_id, trimmed); + session.notifyFederationChanged(); + session.setStatusMessage("Models", "Group renamed"); } -void moveGroup(SessionState& s, const QString& id, const QString& parent_group_id) { - s.federation()->setGroupParent(id, parent_group_id); - s.notifyFederationChanged(); - s.setStatusMessage("Models", parent_group_id.isEmpty() ? "Group moved to root" : "Group moved"); +void moveGroup(SessionState& session, const QString& id, const QString& parent_group_id) { + session.federation()->setGroupParent(id, parent_group_id); + session.notifyFederationChanged(); + session.setStatusMessage("Models", parent_group_id.isEmpty() ? "Group moved to root" : "Group moved"); } -void moveModels(SessionState& s, const QStringList& ids, const QString& parent_group_id) { +void moveModels(SessionState& session, const QStringList& ids, const QString& parent_group_id) { for (const auto& id : ids) { - s.federation()->setModelGroup(id, parent_group_id); + session.federation()->setModelGroup(id, parent_group_id); } - s.notifyFederationChanged(); - s.setStatusMessage("Models", parent_group_id.isEmpty() ? "Model(s) moved to root" : "Model(s) moved"); + session.notifyFederationChanged(); + session.setStatusMessage("Models", parent_group_id.isEmpty() ? "Model(s) moved to root" : "Model(s) moved"); } -void removeGroup(SessionState& s, QWidget& host, const QString& group_id) { - const Federation::Group* group = s.federation()->findGroupById(group_id); +void removeGroup(SessionState& session, QWidget& host, const QString& group_id) { + const Federation::Group* group = session.federation()->findGroupById(group_id); if (!group) return; const auto choice = QMessageBox::question( @@ -161,13 +161,13 @@ void removeGroup(SessionState& s, QWidget& host, const QString& group_id) { QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (choice != QMessageBox::Yes) return; - s.federation()->removeGroup(group_id); - s.notifyFederationChanged(); - s.setStatusMessage("Models", "Group removed"); + session.federation()->removeGroup(group_id); + session.notifyFederationChanged(); + session.setStatusMessage("Models", "Group removed"); } -void removeModel(SessionState& s, ViewportWindow& vp, QWidget& host, const QString& fed_id) { - const Federation::Model* model = s.federation()->findById(fed_id); +void removeModel(SessionState& session, ViewportWindow& viewport, QWidget& host, const QString& fed_id) { + const Federation::Model* model = session.federation()->findById(fed_id); const QString label = model ? model->display_name : fed_id; const auto choice = QMessageBox::question( &host, "Remove Model", @@ -175,41 +175,41 @@ void removeModel(SessionState& s, ViewportWindow& vp, QWidget& host, const QStri QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (choice != QMessageBox::Yes) return; - const uint32_t mid = s.modelIdForFedId(fed_id); - if (mid == 0) { - s.federation()->removeModel(fed_id); - s.notifyFederationChanged(); - s.setStatusMessage("Models", "Model removed"); + const uint32_t model_id = session.modelIdForFedId(fed_id); + if (model_id == 0) { + session.federation()->removeModel(fed_id); + session.notifyFederationChanged(); + session.setStatusMessage("Models", "Model removed"); return; } - if (s.loader()->isLoadingModel(mid)) return; + if (session.loader()->isLoadingModel(model_id)) return; - vp.setSelectedObjectId(0); - s.setSelectedObjectId(0); - s.federation()->removeModel(fed_id); - vp.removeModel(mid); - s.loader()->removeModel(mid); - s.elementRegistry()->removeModel(mid); - s.removeModelMappingByFedId(fed_id); - s.notifySelectionChanged(); - s.notifyModelsChanged(); - s.setStatusMessage("Models", "Model removed"); + viewport.setSelectedObjectId(0); + session.setSelectedObjectId(0); + session.federation()->removeModel(fed_id); + viewport.removeModel(model_id); + session.loader()->removeModel(model_id); + session.elementRegistry()->removeModel(model_id); + session.removeModelMappingByFedId(fed_id); + session.notifySelectionChanged(); + session.notifyModelsChanged(); + session.setStatusMessage("Models", "Model removed"); } namespace detail { -void loadModels(SessionState& s, const QStringList& paths, const QStringList& fed_ids) { +void loadModels(SessionState& session, const QStringList& paths, const QStringList& fed_ids) { if (paths.isEmpty()) return; - const auto ids = s.loader()->addFiles(paths); - for (int i = 0; i < paths.size() && i < static_cast(ids.size()) && i < fed_ids.size(); ++i) { - s.setModelMapping(fed_ids[i], ids[i]); + const auto model_ids = session.loader()->addFiles(paths); + for (int i = 0; i < paths.size() && i < static_cast(model_ids.size()) && i < fed_ids.size(); ++i) { + session.setModelMapping(fed_ids[i], model_ids[i]); } } } // namespace detail -void addModel(SessionState& s, QWidget& host) { +void addModel(SessionState& session, QWidget& host) { AddModelDialog dialog(&host); if (dialog.exec() != QDialog::Accepted) return; @@ -253,13 +253,13 @@ void addModel(SessionState& s, QWidget& host) { break; } case SourceMode::CloudModel: - addModelFromCloud(s, host); + addModelFromCloud(session, host); return; case SourceMode::ConvertToDatabase: - convertIfcToDatabase(s, host); + convertIfcToDatabase(session, host); return; case SourceMode::ExportGeometryDatabase: - exportGeometryDatabase(s, host); + exportGeometryDatabase(session, host); return; case SourceMode::None: return; @@ -270,24 +270,24 @@ void addModel(SessionState& s, QWidget& host) { // origin via ViewportView. Checked here (before federation->addModel) // because federation->addModel doesn't yet populate SessionState's // model mapping; modelIds() reflects pre-add state at this point. - if (s.modelIds().isEmpty()) { + if (session.modelIds().isEmpty()) { armFederatedFalseOriginGuess(); } QStringList accepted_paths; QStringList accepted_fed_ids; for (const auto& path : paths) { - const QString fed_id = s.federation()->addModel(path); + const QString fed_id = session.federation()->addModel(path); if (fed_id.isEmpty()) continue; accepted_paths << path; accepted_fed_ids << fed_id; } - detail::loadModels(s, accepted_paths, accepted_fed_ids); - s.notifyModelsChanged(); + detail::loadModels(session, accepted_paths, accepted_fed_ids); + session.notifyModelsChanged(); } -void addModelFromCloud(SessionState& s, QWidget& host) { - auto* registry = s.connectorRegistry(); +void addModelFromCloud(SessionState& session, QWidget& host) { + auto* registry = session.connectorRegistry(); const auto& manifests = registry->available(); if (manifests.empty()) { QMessageBox::information(&host, "Add From Cloud", @@ -310,9 +310,9 @@ void addModelFromCloud(SessionState& s, QWidget& host) { return; } - s.setStatusMessage("Cloud", QString("Browsing %1...").arg(connector_id)); + session.setStatusMessage("Cloud", QString("Browsing %1...").arg(connector_id)); - QPointer sguard(&s); + QPointer sguard(&session); proc->call("pull_models_interactive", QJsonValue(), [sguard, connector_id](const QJsonValue& result) { @@ -327,9 +327,9 @@ void addModelFromCloud(SessionState& s, QWidget& host) { QStringList paths; QStringList fed_ids; int added = 0; - for (const QJsonValue& v : arr) { - if (v.isNull() || !v.isObject()) continue; - const QJsonObject entry = v.toObject(); + for (const QJsonValue& value : arr) { + if (value.isNull() || !value.isObject()) continue; + const QJsonObject entry = value.toObject(); const QString display_name = entry.value("display_name").toString(); const QString path = entry.value("path").toString(); if (path.isEmpty()) continue; @@ -368,35 +368,35 @@ void addModelFromCloud(SessionState& s, QWidget& host) { namespace { // Shared "local path on disk" lookup for the right-click cloud commands: -// the loader keeps the path keyed by mid (set when a file or pull_models +// the loader keeps the path keyed by model_id (set when a file or pull_models // path was queued). Both local-sourced and resolved cloud-sourced models // have one; only un-resolved cloud models (where pull_models hasn't // returned yet) won't. -QString localPathForModel(SessionState& s, const QString& fed_id) { - const uint32_t mid = s.modelIdForFedId(fed_id); - if (mid == 0 || !s.loader()) return {}; - return s.loader()->filePath(mid); +QString localPathForModel(SessionState& session, const QString& fed_id) { + const uint32_t model_id = session.modelIdForFedId(fed_id); + if (model_id == 0 || !session.loader()) return {}; + return session.loader()->filePath(model_id); } } // namespace -void saveModelToCloud(SessionState& s, QWidget& host, const QString& fed_id) { - auto* fed = s.federation(); - const Federation::Model* model = fed->findById(fed_id); +void saveModelToCloud(SessionState& session, QWidget& host, const QString& fed_id) { + auto* federation = session.federation(); + const Federation::Model* model = federation->findById(fed_id); if (!model) return; if (model->source_connector == "local") { QMessageBox::information(&host, "Save Model To Cloud", "This model has no cloud target. Use \"Save As To Cloud\" first."); return; } - const QString local_path = localPathForModel(s, fed_id); + const QString local_path = localPathForModel(session, fed_id); if (local_path.isEmpty()) { QMessageBox::warning(&host, "Save Model To Cloud", "Cannot find a local copy of this model to push."); return; } const QString connector_id = model->source_connector; - auto* registry = s.connectorRegistry(); + auto* registry = session.connectorRegistry(); auto* proc = registry->get(connector_id); if (!proc) { QMessageBox::warning(&host, "Save Model To Cloud", @@ -411,10 +411,10 @@ void saveModelToCloud(SessionState& s, QWidget& host, const QString& fed_id) { params["path"] = local_path; params["source"] = source; - s.setStatusMessage("Cloud", + session.setStatusMessage("Cloud", QString("Saving %1 to %2...").arg(model->display_name, connector_id)); - QPointer sguard(&s); + QPointer sguard(&session); proc->call("push_model", params, [sguard, fed_id, connector_id](const QJsonValue& result) { if (!sguard) return; @@ -438,17 +438,17 @@ void saveModelToCloud(SessionState& s, QWidget& host, const QString& fed_id) { }); } -void saveModelAsToCloud(SessionState& s, QWidget& host, const QString& fed_id) { - const Federation::Model* model = s.federation()->findById(fed_id); +void saveModelAsToCloud(SessionState& session, QWidget& host, const QString& fed_id) { + const Federation::Model* model = session.federation()->findById(fed_id); if (!model) return; - const QString local_path = localPathForModel(s, fed_id); + const QString local_path = localPathForModel(session, fed_id); if (local_path.isEmpty()) { QMessageBox::warning(&host, "Save Model As To Cloud", "Cannot find a local copy of this model to push."); return; } - auto* registry = s.connectorRegistry(); + auto* registry = session.connectorRegistry(); const auto& manifests = registry->available(); if (manifests.empty()) { QMessageBox::information(&host, "Save Model As To Cloud", @@ -475,10 +475,10 @@ void saveModelAsToCloud(SessionState& s, QWidget& host, const QString& fed_id) { QJsonObject params; params["path"] = local_path; - s.setStatusMessage("Cloud", + session.setStatusMessage("Cloud", QString("Pushing %1 to %2...").arg(model->display_name, connector_id)); - QPointer sguard(&s); + QPointer sguard(&session); proc->call("push_model_interactive", params, [sguard, fed_id, connector_id](const QJsonValue& result) { if (!sguard) return; @@ -507,7 +507,7 @@ void saveModelAsToCloud(SessionState& s, QWidget& host, const QString& fed_id) { }); } -void convertIfcToDatabase(SessionState& s, QWidget& host) { +void convertIfcToDatabase(SessionState& session, QWidget& host) { QFileDialog input_dialog(&host, "Select IFC File to Convert"); input_dialog.setFileMode(QFileDialog::ExistingFile); input_dialog.setNameFilter("IFC Files (*.ifc);;All Files (*)"); @@ -545,10 +545,10 @@ void convertIfcToDatabase(SessionState& s, QWidget& host) { } } - s.beginProgress(QString("Converting %1 to %2…") + session.beginProgress(QString("Converting %1 to %2…") .arg(QFileInfo(input_path).fileName(), QFileInfo(output_path).fileName())); - s.setStatusMessage("Converting", + session.setStatusMessage("Converting", QString("%1 → %2").arg(QFileInfo(input_path).fileName(), QFileInfo(output_path).fileName())); auto timer = std::make_shared(); @@ -583,20 +583,20 @@ void convertIfcToDatabase(SessionState& s, QWidget& host) { }); QObject::connect(thread, &QThread::finished, &host, - [&s, host_ptr = &host, thread, timer, error_message, input_path, output_path]() { + [&session, host_ptr = &host, thread, timer, error_message, input_path, output_path]() { const qint64 elapsed = timer->elapsed(); - s.endProgress(); + session.endProgress(); thread->deleteLater(); if (!error_message->isEmpty()) { - s.setStatusMessage("Error", *error_message); + session.setStatusMessage("Error", *error_message); QMessageBox::warning(host_ptr, "Convert IFC to Database", QString("Conversion failed:\n%1").arg(*error_message)); return; } - s.setStatusMessage( + session.setStatusMessage( "Converted", QString("%1 → %2 in %3") .arg(QFileInfo(input_path).fileName(), @@ -609,7 +609,7 @@ void convertIfcToDatabase(SessionState& s, QWidget& host) { thread->start(); } -void exportGeometryDatabase(SessionState& s, QWidget& host) { +void exportGeometryDatabase(SessionState& session, QWidget& host) { QFileDialog input_dialog(&host, "Select IFC File to Export"); input_dialog.setFileMode(QFileDialog::ExistingFile); input_dialog.setNameFilter("IFC Files (*.ifc);;All Files (*)"); @@ -637,10 +637,10 @@ void exportGeometryDatabase(SessionState& s, QWidget& host) { output_path += ".rdbview"; } - s.beginProgress(QString("Exporting %1 to %2…") + session.beginProgress(QString("Exporting %1 to %2…") .arg(QFileInfo(input_path).fileName(), QFileInfo(output_path).fileName())); - s.setStatusMessage("Exporting", + session.setStatusMessage("Exporting", QString("%1 → %2").arg(QFileInfo(input_path).fileName(), QFileInfo(output_path).fileName())); auto timer = std::make_shared(); @@ -695,13 +695,13 @@ void exportGeometryDatabase(SessionState& s, QWidget& host) { // Write to a sibling `.tmp` then rename so a partial file never // appears at the destination (matters for cloud-sync folders). - const QString tmp_zip = output_path + ".tmp"; - QFile::remove(tmp_zip); + const QString temporary_zip = output_path + ".tmp"; + QFile::remove(temporary_zip); { - QZipWriter writer(tmp_zip); + QZipWriter writer(temporary_zip); if (writer.status() != QZipWriter::NoError) { throw ifcopenshell::exception( - ("Failed to open " + tmp_zip + " for writing").toStdString()); + ("Failed to open " + temporary_zip + " for writing").toStdString()); } writer.setCompressionPolicy(QZipWriter::AutoCompress); @@ -731,15 +731,15 @@ void exportGeometryDatabase(SessionState& s, QWidget& host) { writer.close(); if (writer.status() != QZipWriter::NoError) { throw ifcopenshell::exception( - ("Failed to finalize " + tmp_zip).toStdString()); + ("Failed to finalize " + temporary_zip).toStdString()); } } QFile::remove(output_path); - if (!QFile::rename(tmp_zip, output_path)) { - QFile::remove(tmp_zip); + if (!QFile::rename(temporary_zip, output_path)) { + QFile::remove(temporary_zip); throw ifcopenshell::exception( - ("Failed to move " + tmp_zip + " to " + output_path).toStdString()); + ("Failed to move " + temporary_zip + " to " + output_path).toStdString()); } } catch (const std::exception& e) { *error_message = QString::fromUtf8(e.what()); @@ -751,20 +751,20 @@ void exportGeometryDatabase(SessionState& s, QWidget& host) { }); QObject::connect(thread, &QThread::finished, &host, - [&s, host_ptr = &host, thread, timer, error_message, input_path, output_path]() { + [&session, host_ptr = &host, thread, timer, error_message, input_path, output_path]() { const qint64 elapsed = timer->elapsed(); - s.endProgress(); + session.endProgress(); thread->deleteLater(); if (!error_message->isEmpty()) { - s.setStatusMessage("Error", *error_message); + session.setStatusMessage("Error", *error_message); QMessageBox::warning(host_ptr, "Export Geometry Database", QString("Export failed:\n%1").arg(*error_message)); return; } - s.setStatusMessage( + session.setStatusMessage( "Exported", QString("%1 → %2 in %3") .arg(QFileInfo(input_path).fileName(), @@ -777,8 +777,8 @@ void exportGeometryDatabase(SessionState& s, QWidget& host) { thread->start(); } -void openSettings(SessionState& s, QWidget& host) { - SettingsDialog dialog(&s, &host); +void openSettings(SessionState& session, QWidget& host) { + SettingsDialog dialog(&session, &host); dialog.exec(); } diff --git a/src/bonsaiviewer/modules/models/Commands.h b/src/bonsaiviewer/modules/models/Commands.h index 702e70eb82..9130508b04 100644 --- a/src/bonsaiviewer/modules/models/Commands.h +++ b/src/bonsaiviewer/modules/models/Commands.h @@ -52,35 +52,35 @@ namespace bonsaiviewer::modules::models::commands { // User-facing commands. Each one is responsible for emitting any notify() // signals exactly once, at the end of its execution. -void toggleVisibility(SessionState& s, ItemKind kind, const QString& id); -void addGroup(SessionState& s, QWidget& host, const QString& parent_group_id); -void renameGroup(SessionState& s, QWidget& host, const QString& group_id); -void moveGroup(SessionState& s, const QString& id, const QString& parent_group_id); -void moveModels(SessionState& s, const QStringList& ids, const QString& parent_group_id); -void removeGroup(SessionState& s, QWidget& host, const QString& group_id); -void removeModel(SessionState& s, ViewportWindow& vp, QWidget& host, const QString& fed_id); -void addModel(SessionState& s, QWidget& host); +void toggleVisibility(SessionState& session, ItemKind kind, const QString& id); +void addGroup(SessionState& session, QWidget& host, const QString& parent_group_id); +void renameGroup(SessionState& session, QWidget& host, const QString& group_id); +void moveGroup(SessionState& session, const QString& id, const QString& parent_group_id); +void moveModels(SessionState& session, const QStringList& ids, const QString& parent_group_id); +void removeGroup(SessionState& session, QWidget& host, const QString& group_id); +void removeModel(SessionState& session, ViewportWindow& viewport, QWidget& host, const QString& fed_id); +void addModel(SessionState& session, QWidget& host); // Connector picker → pull_models_interactive → addCloudModel + load. // Reachable from AddModelDialog's CloudModel button; the underlying call // is async, so addModelFromCloud returns immediately after kicking it off. -void addModelFromCloud(SessionState& s, QWidget& host); +void addModelFromCloud(SessionState& session, QWidget& host); // push_model: push a cloud-sourced model back to its existing target. // Only valid when model.source_connector != "local". Async. -void saveModelToCloud(SessionState& s, QWidget& host, const QString& fed_id); +void saveModelToCloud(SessionState& session, QWidget& host, const QString& fed_id); // push_model_interactive: pick a connector and push to a fresh cloud // target. Valid for any model (local or already cloud-sourced). Async. -void saveModelAsToCloud(SessionState& s, QWidget& host, const QString& fed_id); -void convertIfcToDatabase(SessionState& s, QWidget& host); -void exportGeometryDatabase(SessionState& s, QWidget& host); -void openSettings(SessionState& s, QWidget& host); +void saveModelAsToCloud(SessionState& session, QWidget& host, const QString& fed_id); +void convertIfcToDatabase(SessionState& session, QWidget& host); +void exportGeometryDatabase(SessionState& session, QWidget& host); +void openSettings(SessionState& session, QWidget& host); // Internal building blocks shared by commands here and by ProjectController. // These NEVER call notify*() — the caller is responsible for emitting once // at the end of its execution. namespace detail { -// Queues already-federated models on the loader and maps their fed-ids to mids. -void loadModels(SessionState& s, const QStringList& paths, const QStringList& fed_ids); +// Queues already-federated models on the loader and maps their federation-ids to mids. +void loadModels(SessionState& session, const QStringList& paths, const QStringList& fed_ids); } // namespace detail diff --git a/src/bonsaiviewer/modules/models/FederationItemModel.cpp b/src/bonsaiviewer/modules/models/FederationItemModel.cpp index fa7efca01f..8c206e24a3 100644 --- a/src/bonsaiviewer/modules/models/FederationItemModel.cpp +++ b/src/bonsaiviewer/modules/models/FederationItemModel.cpp @@ -164,8 +164,8 @@ void FederationItemModel::refreshSubtreeVisibility(QStandardItem* root) { const auto kind = static_cast(item->data(KindRole).toInt()); bool visible = true; if (kind == ItemKind::Group) { - const Federation::Group* g = federation_->findGroupById(id); - visible = g && g->visible; + const Federation::Group* group = federation_->findGroupById(id); + visible = group && group->visible; } else { visible = federation_->isModelEffectivelyVisible(id); } diff --git a/src/bonsaiviewer/modules/models/Panel.cpp b/src/bonsaiviewer/modules/models/Panel.cpp index ebda8d2a93..8567e6cca3 100644 --- a/src/bonsaiviewer/modules/models/Panel.cpp +++ b/src/bonsaiviewer/modules/models/Panel.cpp @@ -197,8 +197,9 @@ private: if (!target_index.isValid()) return true; if (kindOf(target_index) != ItemKind::Group) return false; if (group_id == target_group_id) return false; - for (QModelIndex cur = target_index; cur.isValid(); cur = cur.parent()) { - if (idOf(cur) == group_id) return false; + for (QModelIndex ancestor_index = target_index; ancestor_index.isValid(); + ancestor_index = ancestor_index.parent()) { + if (idOf(ancestor_index) == group_id) return false; } return true; } diff --git a/src/bonsaiviewer/modules/models/SettingsDialog.cpp b/src/bonsaiviewer/modules/models/SettingsDialog.cpp index 9f544ac9a3..a864aa674d 100644 --- a/src/bonsaiviewer/modules/models/SettingsDialog.cpp +++ b/src/bonsaiviewer/modules/models/SettingsDialog.cpp @@ -166,10 +166,10 @@ void SettingsDialog::setupUi() { federation_unit_form->setHorizontalSpacing(16); federation_unit_form->setVerticalSpacing(10); unit_combo_ = new QComboBox(federation_unit_body); - for (const auto& uc : kUnitChoices) { + for (const auto& unit_choice : kUnitChoices) { QStringList data; - data << QString::fromUtf8(uc.prefix) << QString::fromUtf8(uc.name); - unit_combo_->addItem(uc.label, data); + data << QString::fromUtf8(unit_choice.prefix) << QString::fromUtf8(unit_choice.name); + unit_combo_->addItem(unit_choice.label, data); } federation_unit_form->addRow("Unit", unit_combo_); federation_unit_section->addBodyWidget(unit_hint); @@ -341,13 +341,13 @@ void SettingsDialog::setupUi() { void SettingsDialog::syncFromFederation() { if (!federation_) return; - const auto& cfg = federation_->config(); + const auto& config = federation_->config(); int idx = -1; for (int i = 0; i < unit_combo_->count(); ++i) { const QStringList data = unit_combo_->itemData(i).toStringList(); if (data.size() == 2 && - data[0].toStdString() == cfg.unit_prefix && - data[1].toStdString() == cfg.unit_name) { + data[0].toStdString() == config.unit_prefix && + data[1].toStdString() == config.unit_name) { idx = i; break; } @@ -370,7 +370,7 @@ void SettingsDialog::populateModelTable() { int row = 0; for (const auto& model : federation_->models()) { - const auto& xf = model.model_transformation; + const auto& transformation = model.model_transformation; model_table_->insertRow(row); auto* model_item = new QTableWidgetItem(model.display_name.isEmpty() ? model.id : model.display_name); @@ -383,13 +383,13 @@ void SettingsDialog::populateModelTable() { widgets.frame = new QComboBox(model_table_); widgets.frame->addItem("Local", static_cast(AFrame::ModelLocal)); widgets.frame->addItem("Global", static_cast(AFrame::ModelGlobal)); - widgets.frame->setCurrentIndex(xf.a_frame == AFrame::ModelGlobal ? 1 : 0); + widgets.frame->setCurrentIndex(transformation.a_frame == AFrame::ModelGlobal ? 1 : 0); model_table_->setCellWidget(row, 1, widgets.frame); - widgets.from_point = new QTableWidgetItem(formatVector3(xf.a)); - widgets.to_point = new QTableWidgetItem(formatVector3(xf.b)); - widgets.rotate = new QTableWidgetItem(formatVector3(xf.rxyz_deg)); - widgets.pivot = new QTableWidgetItem(formatVector3(xf.pivot)); + widgets.from_point = new QTableWidgetItem(formatVector3(transformation.a)); + widgets.to_point = new QTableWidgetItem(formatVector3(transformation.b)); + widgets.rotate = new QTableWidgetItem(formatVector3(transformation.rxyz_deg)); + widgets.pivot = new QTableWidgetItem(formatVector3(transformation.pivot)); model_table_->setItem(row, 2, widgets.from_point); model_table_->setItem(row, 3, widgets.to_point); model_table_->setItem(row, 4, widgets.rotate); @@ -454,12 +454,12 @@ void SettingsDialog::updateSelectedModelGeoref() { void SettingsDialog::onAccepted() { if (federation_) { const QStringList data = unit_combo_->currentData().toStringList(); - FederationConfig cfg; + FederationConfig config; if (data.size() == 2) { - cfg.unit_prefix = data[0].toStdString(); - cfg.unit_name = data[1].toStdString(); + config.unit_prefix = data[0].toStdString(); + config.unit_name = data[1].toStdString(); } - federation_->setConfig(cfg); + federation_->setConfig(config); FederatedFalseOrigin origin; origin.xyz = Eigen::Vector3d(parseNumber(xyz_x_), parseNumber(xyz_y_), parseNumber(xyz_z_)); @@ -467,13 +467,13 @@ void SettingsDialog::onAccepted() { federation_->setFederatedFalseOrigin(origin); for (const auto& row : model_rows_) { - ModelTransformation xf; - xf.a_frame = static_cast(row.frame->currentData().toInt()); - xf.a = parseVector3(row.from_point->text()); - xf.b = parseVector3(row.to_point->text()); - xf.rxyz_deg = parseVector3(row.rotate->text()); - xf.pivot = parseVector3(row.pivot->text()); - federation_->setModelTransformation(row.fed_id, xf); + ModelTransformation transformation; + transformation.a_frame = static_cast(row.frame->currentData().toInt()); + transformation.a = parseVector3(row.from_point->text()); + transformation.b = parseVector3(row.to_point->text()); + transformation.rxyz_deg = parseVector3(row.rotate->text()); + transformation.pivot = parseVector3(row.pivot->text()); + federation_->setModelTransformation(row.fed_id, transformation); } if (session_state_) { session_state_->notifyFederationChanged(); diff --git a/src/bonsaiviewer/modules/models/SettingsView.cpp b/src/bonsaiviewer/modules/models/SettingsView.cpp index 31ef07f973..bfe9f2196a 100644 --- a/src/bonsaiviewer/modules/models/SettingsView.cpp +++ b/src/bonsaiviewer/modules/models/SettingsView.cpp @@ -39,12 +39,16 @@ QString formatNumber(double value) { QString formatAngleDms(double degrees) { const double absolute = std::fabs(degrees); - const int d = static_cast(absolute); - const double minutes_total = (absolute - static_cast(d)) * 60.0; - const int m = static_cast(minutes_total); - const double s = (minutes_total - static_cast(m)) * 60.0; + const int degree_part = static_cast(absolute); + const double minutes_total = (absolute - static_cast(degree_part)) * 60.0; + const int minute_part = static_cast(minutes_total); + const double second_part = (minutes_total - static_cast(minute_part)) * 60.0; const QString sign = degrees < 0.0 ? "-" : ""; - return QString("%1%2° %3' %4\"").arg(sign).arg(d).arg(m, 2, 10, QChar('0')).arg(formatNumber(s)); + return QString("%1%2° %3' %4\"") + .arg(sign) + .arg(degree_part) + .arg(minute_part, 2, 10, QChar('0')) + .arg(formatNumber(second_part)); } SelectedModelGeorefState unknownState(const QString& georef, const QString& type) { @@ -74,8 +78,8 @@ QString formatCachedUnitScale(double meters_per_unit) { std::string enumString(const attribute_value& av) { if (av.isNull()) return {}; if (av.type() != ifcopenshell::Argument_ENUMERATION) return {}; - enumeration_reference er = av; - return std::string(er.value() ? er.value() : ""); + enumeration_reference enumeration = av; + return std::string(enumeration.value() ? enumeration.value() : ""); } QString formatNamedUnit(const express::Base& unit) { @@ -224,18 +228,18 @@ void SettingsView::refresh(const QString& fed_id) const { return; } - const uint32_t mid = session_state_->modelIdForFedId(fed_id); - if (mid == 0) { + const uint32_t model_id = session_state_->modelIdForFedId(fed_id); + if (model_id == 0) { widget_->renderSelectedModelGeoref(unknownState("Not loaded", "No live model")); return; } - if (auto* ifc_file = loader->ifcFile(mid)) { + if (auto* ifc_file = loader->ifcFile(model_id)) { widget_->renderSelectedModelGeoref(stateFromLiveFile(ifc_file)); return; } - const ModelGeoref* georef = loader->modelGeoref(mid); + const ModelGeoref* georef = loader->modelGeoref(model_id); if (!georef) { widget_->renderSelectedModelGeoref(unknownState("Not available yet", "No data source")); return; diff --git a/src/bonsaiviewer/modules/project/Commands.cpp b/src/bonsaiviewer/modules/project/Commands.cpp index f0e626c7f2..7761fdb474 100644 --- a/src/bonsaiviewer/modules/project/Commands.cpp +++ b/src/bonsaiviewer/modules/project/Commands.cpp @@ -53,28 +53,28 @@ namespace { // Pure helper — clears the loaded scene without emitting any signals. The // caller (newProject / openProject) emits projectReset / projectOpened once // the whole flow finishes. -void clearScene(SessionState& s, ViewportWindow& vp) { - vp.setSelectedObjectId(0); - s.setSelectedObjectId(0); - for (uint32_t mid : s.modelIds()) { - vp.removeModel(mid); - s.loader()->removeModel(mid); +void clearScene(SessionState& session, ViewportWindow& viewport) { + viewport.setSelectedObjectId(0); + session.setSelectedObjectId(0); + for (uint32_t model_id : session.modelIds()) { + viewport.removeModel(model_id); + session.loader()->removeModel(model_id); } - s.clearModelMappings(); - s.elementRegistry()->clear(); + session.clearModelMappings(); + session.elementRegistry()->clear(); } // Returns false if the user cancelled (i.e. don't proceed with the destructive // op). Handles the Save → Discard → Cancel branch including a follow-on save. -bool confirmDiscardIfDirty(SessionState& s, QWidget& host) { - if (!s.federation()->isDirty()) return true; +bool confirmDiscardIfDirty(SessionState& session, QWidget& host) { + if (!session.federation()->isDirty()) return true; const auto result = QMessageBox::question( &host, "Unsaved Project", "The current project has unsaved changes. Save before continuing?", QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Save); if (result == QMessageBox::Cancel) return false; - if (result == QMessageBox::Save) return saveProject(s, host); + if (result == QMessageBox::Save) return saveProject(session, host); return true; } @@ -88,18 +88,18 @@ bool confirmDiscardIfDirty(SessionState& s, QWidget& host) { // - if no scene entry exists yet (initial open), queue a load. // Per spec, connector errors are not surfaced to the user; the connector // has already shown its own UI. -void resolveCloudModels(SessionState& s, ViewportWindow& vp) { - auto* fed = s.federation(); +void resolveCloudModels(SessionState& session, ViewportWindow& viewport) { + auto* federation = session.federation(); QHash connector_to_fed_ids; - for (const auto& m : fed->models()) { - if (m.source_connector == "local") continue; - connector_to_fed_ids[m.source_connector].push_back(m.id); + for (const auto& model : federation->models()) { + if (model.source_connector == "local") continue; + connector_to_fed_ids[model.source_connector].push_back(model.id); } if (connector_to_fed_ids.isEmpty()) return; - auto* registry = s.connectorRegistry(); - QPointer sguard(&s); - QPointer vguard(&vp); + auto* registry = session.connectorRegistry(); + QPointer sguard(&session); + QPointer vguard(&viewport); for (auto it = connector_to_fed_ids.constBegin(); it != connector_to_fed_ids.constEnd(); ++it) { @@ -115,13 +115,13 @@ void resolveCloudModels(SessionState& s, ViewportWindow& vp) { QJsonArray params; for (const QString& fed_id : fed_ids) { - const Federation::Model* m = fed->findById(fed_id); - if (!m) continue; - QJsonObject source = m->source_data; - source["connector"] = m->source_connector; + const Federation::Model* model = federation->findById(fed_id); + if (!model) continue; + QJsonObject source = model->source_data; + source["connector"] = model->source_connector; QJsonObject entry; - entry["display_name"] = m->display_name; - entry["id"] = m->id; + entry["display_name"] = model->display_name; + entry["id"] = model->id; entry["source"] = source; params.append(entry); } @@ -187,7 +187,7 @@ void resolveCloudModels(SessionState& s, ViewportWindow& vp) { it != connector_to_fed_ids.constEnd(); ++it) { total += it.value().size(); } - s.setStatusMessage("Cloud", QString("Resolving %1 model(s)...").arg(total)); + session.setStatusMessage("Cloud", QString("Resolving %1 model(s)...").arg(total)); } // Byte-equality check for "is this .ifcfed the same as what we have loaded?" @@ -203,29 +203,29 @@ bool isIfcfedUnchanged(const QString& current_path, const QString& candidate_pat return a.readAll() == b.readAll(); } -bool openProjectAt(SessionState& s, QWidget& host, ViewportWindow& vp, const QString& path) { - SceneLoader* loader = s.loader(); +bool openProjectAt(SessionState& session, QWidget& host, ViewportWindow& viewport, const QString& path) { + SceneLoader* loader = session.loader(); if (loader && loader->isLoading()) { QMessageBox::information( &host, "Open Project", "Wait until the current model load finishes before opening another project."); return false; } - if (!confirmDiscardIfDirty(s, host)) return false; + if (!confirmDiscardIfDirty(session, host)) return false; QStringList warnings; QString err; - if (!s.federation()->load(path, &warnings, &err)) { + if (!session.federation()->load(path, &warnings, &err)) { QMessageBox::warning(&host, "Open Project", QString("Could not open project:\n%1").arg(err)); return false; } - clearScene(s, vp); + clearScene(session, viewport); QStringList paths; QStringList fed_ids; - for (const auto& model : s.federation()->models()) { + for (const auto& model : session.federation()->models()) { if (model.source_connector != "local") continue; if (!QFileInfo::exists(model.source_path)) { warnings << QString("Source not found, kept in project: %1").arg(model.source_path); @@ -234,58 +234,58 @@ bool openProjectAt(SessionState& s, QWidget& host, ViewportWindow& vp, const QSt paths << model.source_path; fed_ids << model.id; } - modules::models::commands::detail::loadModels(s, paths, fed_ids); + modules::models::commands::detail::loadModels(session, paths, fed_ids); if (!warnings.isEmpty()) { QMessageBox::warning(&host, "Open Project", "Project opened with warnings:\n\n" + warnings.join("\n")); } - s.federation()->markClean(); - if (s.federation()->hasHomeView()) { - const auto& hv = s.federation()->homeView(); - vp.setCamera(hv.target.x(), hv.target.y(), hv.target.z(), - hv.distance, hv.yaw, hv.pitch); + session.federation()->markClean(); + if (session.federation()->hasHomeView()) { + const auto& home_view = session.federation()->homeView(); + viewport.setCamera(home_view.target.x(), home_view.target.y(), home_view.target.z(), + home_view.distance, home_view.yaw, home_view.pitch); } - s.setStatusMessage("Project", QFileInfo(path).fileName()); - s.notifyProjectOpened(path); + session.setStatusMessage("Project", QFileInfo(path).fileName()); + session.notifyProjectOpened(path); - resolveCloudModels(s, vp); + resolveCloudModels(session, viewport); return true; } -bool saveProjectTo(SessionState& s, QWidget& host, const QString& path) { +bool saveProjectTo(SessionState& session, QWidget& host, const QString& path) { QString err; - if (!s.federation()->save(path, &err)) { + if (!session.federation()->save(path, &err)) { QMessageBox::warning(&host, "Save Project", QString("Could not save project:\n%1").arg(err)); return false; } - s.setStatusMessage("Project", QFileInfo(path).fileName()); - s.notifyProjectSaved(path); + session.setStatusMessage("Project", QFileInfo(path).fileName()); + session.notifyProjectSaved(path); return true; } } // namespace -bool newProject(SessionState& s, QWidget& host, ViewportWindow& vp) { - SceneLoader* loader = s.loader(); +bool newProject(SessionState& session, QWidget& host, ViewportWindow& viewport) { + SceneLoader* loader = session.loader(); if (loader && loader->isLoading()) { QMessageBox::information( &host, "New Project", "Wait until the current model load finishes before creating a new project."); return false; } - if (!confirmDiscardIfDirty(s, host)) return false; + if (!confirmDiscardIfDirty(session, host)) return false; - clearScene(s, vp); - s.federation()->clear(); - s.setStatusMessage("Project", "Untitled"); - s.notifyProjectReset(); + clearScene(session, viewport); + session.federation()->clear(); + session.setStatusMessage("Project", "Untitled"); + session.notifyProjectReset(); return true; } -bool openProject(SessionState& s, QWidget& host, ViewportWindow& vp) { +bool openProject(SessionState& session, QWidget& host, ViewportWindow& viewport) { QFileDialog file_dialog(&host, "Open Project"); file_dialog.setFileMode(QFileDialog::ExistingFile); file_dialog.setNameFilter("IFC Federation (*.ifcfed);;All Files (*)"); @@ -294,25 +294,25 @@ bool openProject(SessionState& s, QWidget& host, ViewportWindow& vp) { const QString path = file_dialog.selectedFiles().value(0); if (path.isEmpty()) return false; - return openProjectAt(s, host, vp, path); + return openProjectAt(session, host, viewport, path); } -bool openProjectPath(SessionState& s, QWidget& host, ViewportWindow& vp, const QString& path) { +bool openProjectPath(SessionState& session, QWidget& host, ViewportWindow& viewport, const QString& path) { if (path.isEmpty()) return false; - return openProjectAt(s, host, vp, path); + return openProjectAt(session, host, viewport, path); } -bool openCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) { - SceneLoader* loader = s.loader(); +bool openCloudProject(SessionState& session, QWidget& host, ViewportWindow& viewport) { + SceneLoader* loader = session.loader(); if (loader && loader->isLoading()) { QMessageBox::information( &host, "Open from Cloud", "Wait until the current model load finishes before opening another project."); return false; } - if (!confirmDiscardIfDirty(s, host)) return false; + if (!confirmDiscardIfDirty(session, host)) return false; - auto* registry = s.connectorRegistry(); + auto* registry = session.connectorRegistry(); const auto& manifests = registry->available(); if (manifests.empty()) { QMessageBox::information(&host, "Open from Cloud", @@ -336,12 +336,12 @@ bool openCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) { return false; } - s.beginProgress(QString("Opening project from %1...").arg(connector_id)); - s.setStatusMessage("Cloud", QString("Browsing %1...").arg(connector_id)); + session.beginProgress(QString("Opening project from %1...").arg(connector_id)); + session.setStatusMessage("Cloud", QString("Browsing %1...").arg(connector_id)); - QPointer sguard(&s); + QPointer sguard(&session); QPointer hguard(&host); - QPointer vguard(&vp); + QPointer vguard(&viewport); proc->call("pull_ifcfed_interactive", QJsonValue(), [sguard, hguard, vguard, connector_id](const QJsonValue& result) { @@ -371,16 +371,16 @@ bool openCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) { return true; } -bool syncCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) { - auto* fed = s.federation(); +bool syncCloudProject(SessionState& session, QWidget& host, ViewportWindow& viewport) { + auto* federation = session.federation(); // Per spec, sync has two independent phases — refreshing the .ifcfed // (requires manifest) and refreshing cloud models (requires any // non-local source). Either is sufficient. - const bool has_manifest = fed->hasManifest(); + const bool has_manifest = federation->hasManifest(); bool has_cloud_models = false; - for (const auto& m : fed->models()) { - if (m.source_connector != "local") { has_cloud_models = true; break; } + for (const auto& model : federation->models()) { + if (model.source_connector != "local") { has_cloud_models = true; break; } } if (!has_manifest && !has_cloud_models) { QMessageBox::information(&host, "Sync From Cloud", @@ -388,7 +388,7 @@ bool syncCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) { return false; } - SceneLoader* loader = s.loader(); + SceneLoader* loader = session.loader(); if (loader && loader->isLoading()) { QMessageBox::information(&host, "Sync From Cloud", "Wait until the current model load finishes before syncing."); @@ -399,23 +399,23 @@ bool syncCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) { // skipped). Just refresh cloud-sourced models against the .ifcfed // already on disk. Federation state is preserved, so no dirty prompt. if (!has_manifest) { - s.setStatusMessage("Cloud", "Refreshing cloud models..."); - resolveCloudModels(s, vp); + session.setStatusMessage("Cloud", "Refreshing cloud models..."); + resolveCloudModels(session, viewport); return true; } // Manifest path: the .ifcfed itself may be replaced. Confirm dirty — // even though we'll attempt to preserve the session if the returned // .ifcfed is byte-equal, that's not known until after the round-trip. - if (!confirmDiscardIfDirty(s, host)) return false; + if (!confirmDiscardIfDirty(session, host)) return false; - const QString connector_id = fed->manifestConnectorId(); + const QString connector_id = federation->manifestConnectorId(); if (connector_id.isEmpty()) { QMessageBox::warning(&host, "Sync From Cloud", "The project's manifest does not name a connector."); return false; } - auto* registry = s.connectorRegistry(); + auto* registry = session.connectorRegistry(); auto* proc = registry->get(connector_id); if (!proc) { QMessageBox::warning(&host, "Sync From Cloud", @@ -424,15 +424,15 @@ bool syncCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) { return false; } - s.beginProgress(QString("Syncing from %1...").arg(connector_id)); - s.setStatusMessage("Cloud", QString("Syncing from %1...").arg(connector_id)); + session.beginProgress(QString("Syncing from %1...").arg(connector_id)); + session.setStatusMessage("Cloud", QString("Syncing from %1...").arg(connector_id)); - QPointer sguard(&s); + QPointer sguard(&session); QPointer hguard(&host); - QPointer vguard(&vp); - const QString current_path = fed->filePath(); + QPointer vguard(&viewport); + const QString current_path = federation->filePath(); - proc->call("pull_ifcfed", fed->manifest(), + proc->call("pull_ifcfed", federation->manifest(), [sguard, hguard, vguard, connector_id, current_path](const QJsonValue& result) { if (!sguard) return; sguard->endProgress(); @@ -475,13 +475,13 @@ bool syncCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp) { return true; } -bool saveProject(SessionState& s, QWidget& host) { - if (s.federation()->filePath().isEmpty()) return saveProjectAs(s, host); - return saveProjectTo(s, host, s.federation()->filePath()); +bool saveProject(SessionState& session, QWidget& host) { + if (session.federation()->filePath().isEmpty()) return saveProjectAs(session, host); + return saveProjectTo(session, host, session.federation()->filePath()); } -bool saveProjectAs(SessionState& s, QWidget& host) { - QString suggested = s.federation()->filePath(); +bool saveProjectAs(SessionState& session, QWidget& host) { + QString suggested = session.federation()->filePath(); if (suggested.isEmpty()) suggested = "project.ifcfed"; QFileDialog file_dialog(&host, "Save Project As", suggested); @@ -494,7 +494,7 @@ bool saveProjectAs(SessionState& s, QWidget& host) { QString path = file_dialog.selectedFiles().value(0); if (path.isEmpty()) return false; if (!path.endsWith(".ifcfed", Qt::CaseInsensitive)) path += ".ifcfed"; - return saveProjectTo(s, host, path); + return saveProjectTo(session, host, path); } namespace { @@ -507,7 +507,7 @@ struct TempProjectFile { QString path; }; -TempProjectFile writeProjectToTemp(SessionState& s, QWidget& host, const QString& op_title) { +TempProjectFile writeProjectToTemp(SessionState& session, QWidget& host, const QString& op_title) { TempProjectFile out; out.dir = std::make_shared(); if (!out.dir->isValid()) { @@ -517,12 +517,12 @@ TempProjectFile writeProjectToTemp(SessionState& s, QWidget& host, const QString out.dir.reset(); return out; } - const QString name = s.federation()->filePath().isEmpty() + const QString name = session.federation()->filePath().isEmpty() ? "project.ifcfed" - : QFileInfo(s.federation()->filePath()).fileName(); + : QFileInfo(session.federation()->filePath()).fileName(); const QString tmp_path = QDir(out.dir->path()).filePath(name); QString err; - if (!s.federation()->writeCopyTo(tmp_path, &err)) { + if (!session.federation()->writeCopyTo(tmp_path, &err)) { QMessageBox::warning(&host, op_title, QString("Failed to write temporary project:\n%1").arg(err)); out.dir.reset(); @@ -534,12 +534,12 @@ TempProjectFile writeProjectToTemp(SessionState& s, QWidget& host, const QString // Shared continuation for push_ifcfed[_interactive]: on success, repoint // Federation to the returned path and notify; on error, log + status. -void onPushIfcfedResult(SessionState& s, +void onPushIfcfedResult(SessionState& session, QWidget& host, const QString& op_title, const QString& connector_id, const QJsonValue& result) { - s.endProgress(); + session.endProgress(); const QString new_path = result.toObject().value("path").toString(); if (new_path.isEmpty()) { QMessageBox::warning(&host, op_title, @@ -547,24 +547,24 @@ void onPushIfcfedResult(SessionState& s, return; } QStringList warnings; - s.federation()->repointTo(new_path, &warnings); - s.setStatusMessage("Cloud", + session.federation()->repointTo(new_path, &warnings); + session.setStatusMessage("Cloud", QString("Saved to %1 via %2") .arg(QFileInfo(new_path).fileName(), connector_id)); - s.notifyProjectSaved(new_path); + session.notifyProjectSaved(new_path); } } // namespace -bool saveCloudProject(SessionState& s, QWidget& host) { - auto* fed = s.federation(); - if (!fed->hasManifest()) { +bool saveCloudProject(SessionState& session, QWidget& host) { + auto* federation = session.federation(); + if (!federation->hasManifest()) { QMessageBox::information(&host, "Save To Cloud", "This project has no cloud target. Use \"Save As To Cloud\" first."); return false; } - const QString connector_id = fed->manifestConnectorId(); - auto* registry = s.connectorRegistry(); + const QString connector_id = federation->manifestConnectorId(); + auto* registry = session.connectorRegistry(); auto* proc = registry->get(connector_id); if (!proc) { QMessageBox::warning(&host, "Save To Cloud", @@ -573,26 +573,26 @@ bool saveCloudProject(SessionState& s, QWidget& host) { return false; } - auto tmp = writeProjectToTemp(s, host, "Save To Cloud"); - if (!tmp.dir) return false; + auto temporary_project = writeProjectToTemp(session, host, "Save To Cloud"); + if (!temporary_project.dir) return false; QJsonObject params; - params["path"] = tmp.path; - params["manifest"] = fed->manifest(); + params["path"] = temporary_project.path; + params["manifest"] = federation->manifest(); - s.beginProgress(QString("Saving to %1...").arg(connector_id)); - s.setStatusMessage("Cloud", QString("Saving to %1...").arg(connector_id)); + session.beginProgress(QString("Saving to %1...").arg(connector_id)); + session.setStatusMessage("Cloud", QString("Saving to %1...").arg(connector_id)); - QPointer sguard(&s); + QPointer sguard(&session); QPointer hguard(&host); proc->call("push_ifcfed", params, - [sguard, hguard, connector_id, tmp_keepalive = tmp.dir](const QJsonValue& result) { + [sguard, hguard, connector_id, tmp_keepalive = temporary_project.dir](const QJsonValue& result) { (void)tmp_keepalive; if (!sguard || !hguard) return; onPushIfcfedResult(*sguard, *hguard, "Save To Cloud", connector_id, result); }, - [sguard, connector_id, tmp_keepalive = tmp.dir](int code, const QString& message) { + [sguard, connector_id, tmp_keepalive = temporary_project.dir](int code, const QString& message) { (void)tmp_keepalive; qWarning() << "push_ifcfed to" << connector_id << "failed:" << code << message; @@ -605,8 +605,8 @@ bool saveCloudProject(SessionState& s, QWidget& host) { return true; } -bool saveAsCloudProject(SessionState& s, QWidget& host) { - auto* registry = s.connectorRegistry(); +bool saveAsCloudProject(SessionState& session, QWidget& host) { + auto* registry = session.connectorRegistry(); const auto& manifests = registry->available(); if (manifests.empty()) { QMessageBox::information(&host, "Save As To Cloud", @@ -629,25 +629,25 @@ bool saveAsCloudProject(SessionState& s, QWidget& host) { return false; } - auto tmp = writeProjectToTemp(s, host, "Save As To Cloud"); - if (!tmp.dir) return false; + auto temporary_project = writeProjectToTemp(session, host, "Save As To Cloud"); + if (!temporary_project.dir) return false; QJsonObject params; - params["path"] = tmp.path; + params["path"] = temporary_project.path; - s.beginProgress(QString("Pushing to %1...").arg(connector_id)); - s.setStatusMessage("Cloud", QString("Pushing to %1...").arg(connector_id)); + session.beginProgress(QString("Pushing to %1...").arg(connector_id)); + session.setStatusMessage("Cloud", QString("Pushing to %1...").arg(connector_id)); - QPointer sguard(&s); + QPointer sguard(&session); QPointer hguard(&host); proc->call("push_ifcfed_interactive", params, - [sguard, hguard, connector_id, tmp_keepalive = tmp.dir](const QJsonValue& result) { + [sguard, hguard, connector_id, tmp_keepalive = temporary_project.dir](const QJsonValue& result) { (void)tmp_keepalive; if (!sguard || !hguard) return; onPushIfcfedResult(*sguard, *hguard, "Save As To Cloud", connector_id, result); }, - [sguard, connector_id, tmp_keepalive = tmp.dir](int code, const QString& message) { + [sguard, connector_id, tmp_keepalive = temporary_project.dir](int code, const QString& message) { (void)tmp_keepalive; qWarning() << "push_ifcfed_interactive to" << connector_id << "failed:" << code << message; @@ -660,14 +660,14 @@ bool saveAsCloudProject(SessionState& s, QWidget& host) { return true; } -bool saveProjectDialog(SessionState& s, QWidget& host) { - SaveProjectDialog dialog(s.federation()->hasManifest(), &host); +bool saveProjectDialog(SessionState& session, QWidget& host) { + SaveProjectDialog dialog(session.federation()->hasManifest(), &host); if (dialog.exec() != QDialog::Accepted) return false; switch (dialog.selectedTarget()) { - case SaveTarget::Local: return saveProject(s, host); - case SaveTarget::LocalAs: return saveProjectAs(s, host); - case SaveTarget::Cloud: return saveCloudProject(s, host); - case SaveTarget::CloudAs: return saveAsCloudProject(s, host); + case SaveTarget::Local: return saveProject(session, host); + case SaveTarget::LocalAs: return saveProjectAs(session, host); + case SaveTarget::Cloud: return saveCloudProject(session, host); + case SaveTarget::CloudAs: return saveAsCloudProject(session, host); case SaveTarget::None: return false; } return false; diff --git a/src/bonsaiviewer/modules/project/Commands.h b/src/bonsaiviewer/modules/project/Commands.h index 12dccdc057..386076c116 100644 --- a/src/bonsaiviewer/modules/project/Commands.h +++ b/src/bonsaiviewer/modules/project/Commands.h @@ -32,35 +32,35 @@ namespace bonsaiviewer::modules::project::commands { // User-facing commands. Each owns its own dialogs and confirmations; each // emits exactly one notify() at the end (projectReset / projectOpened / // projectSaved) so views refresh once per command. -bool newProject(SessionState& s, QWidget& host, ViewportWindow& vp); -bool openProject(SessionState& s, QWidget& host, ViewportWindow& vp); +bool newProject(SessionState& session, QWidget& host, ViewportWindow& viewport); +bool openProject(SessionState& session, QWidget& host, ViewportWindow& viewport); // Open a specific .ifcfed by path, bypassing the file dialog. Used by the // "Open Recent" menu. Same dirty-check / load / cloud-resolve flow as // openProject; returns false if the load failed or was cancelled. -bool openProjectPath(SessionState& s, QWidget& host, ViewportWindow& vp, const QString& path); +bool openProjectPath(SessionState& session, QWidget& host, ViewportWindow& viewport, const QString& path); // Pick a connector, then call pull_ifcfed_interactive and open the resulting // .ifcfed as a fresh project. Non-local models in the loaded federation are // resolved asynchronously via pull_models. -bool openCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp); +bool openCloudProject(SessionState& session, QWidget& host, ViewportWindow& viewport); // pull_ifcfed using the current project's .ifcfed.manifest. Re-downloads // the .ifcfed from the same cloud target it came from (typically without // user interaction), then opens it like a fresh project — discarding any // local edits after the usual dirty-check prompt. -bool syncCloudProject(SessionState& s, QWidget& host, ViewportWindow& vp); -bool saveProject(SessionState& s, QWidget& host); -bool saveProjectAs(SessionState& s, QWidget& host); +bool syncCloudProject(SessionState& session, QWidget& host, ViewportWindow& viewport); +bool saveProject(SessionState& session, QWidget& host); +bool saveProjectAs(SessionState& session, QWidget& host); // Push the current federation to the cloud target named in its manifest // (push_ifcfed). No user prompt for destination. Caller is responsible for // gating this on Federation::hasManifest. -bool saveCloudProject(SessionState& s, QWidget& host); +bool saveCloudProject(SessionState& session, QWidget& host); // Pick a connector and push the current federation to a fresh cloud target // (push_ifcfed_interactive). The connector returns a new path + manifest; // Federation repoints to that location. -bool saveAsCloudProject(SessionState& s, QWidget& host); +bool saveAsCloudProject(SessionState& session, QWidget& host); // Show the four-way Save dialog (Local / Save As Local / To Cloud / Save // As To Cloud) and dispatch to one of the above. This is what the "Save // Project" ribbon button is wired to. -bool saveProjectDialog(SessionState& s, QWidget& host); +bool saveProjectDialog(SessionState& session, QWidget& host); } // namespace bonsaiviewer::modules::project::commands diff --git a/src/bonsaiviewer/modules/viewport/Commands.cpp b/src/bonsaiviewer/modules/viewport/Commands.cpp index fd8fa30dd5..a6166d07b0 100644 --- a/src/bonsaiviewer/modules/viewport/Commands.cpp +++ b/src/bonsaiviewer/modules/viewport/Commands.cpp @@ -26,8 +26,8 @@ namespace bonsaiviewer::modules::viewport::commands { -void setHome(SessionState& session, ViewportWindow& vp) { - auto camera = vp.cameraState(); +void setHome(SessionState& session, ViewportWindow& viewport) { + auto camera = viewport.cameraState(); Federation::HomeView home_view; home_view.target = camera.target; home_view.distance = camera.distance; @@ -37,66 +37,66 @@ void setHome(SessionState& session, ViewportWindow& vp) { session.setStatusMessage("Camera", "Home view updated"); } -void goHome(SessionState& session, ViewportWindow& vp) { +void goHome(SessionState& session, ViewportWindow& viewport) { Federation* federation = session.federation(); if (!federation->hasHomeView()) { session.setStatusMessage("Camera", "No home view set for this project"); return; } const auto& home_view = federation->homeView(); - vp.setCamera( + viewport.setCamera( home_view.target.x(), home_view.target.y(), home_view.target.z(), home_view.distance, home_view.yaw, home_view.pitch); session.setStatusMessage("Camera", "Home view restored"); } -void viewSelected(ViewportWindow& vp) { - vp.focusOnSelectedObject(); +void viewSelected(ViewportWindow& viewport) { + viewport.focusOnSelectedObject(); } -void fly(SessionState& session, ViewportWindow& vp) { - vp.requestActivate(); - vp.enterFpsMode(); +void fly(SessionState& session, ViewportWindow& viewport) { + viewport.requestActivate(); + viewport.enterFpsMode(); session.setStatusMessage("Mode", "Fly mode active"); } -void toggleSection(SessionState& session, ViewportWindow& vp) { - vp.toggleSectionTool(); +void toggleSection(SessionState& session, ViewportWindow& viewport) { + viewport.toggleSectionTool(); session.setStatusMessage("Section", - vp.sectionToolActive() ? "Section tool active" : "Section tool off"); + viewport.sectionToolActive() ? "Section tool active" : "Section tool off"); } -void clearSection(SessionState& session, ViewportWindow& vp) { - vp.clearSectionPlanes(); +void clearSection(SessionState& session, ViewportWindow& viewport) { + viewport.clearSectionPlanes(); session.setStatusMessage("Section", "Section planes cleared"); } -void toggleDistance(ViewportWindow& vp) { - vp.toggleLengthTool(); +void toggleDistance(ViewportWindow& viewport) { + viewport.toggleLengthTool(); } -void toggleArea(ViewportWindow& vp) { - vp.toggleAreaTool(); +void toggleArea(ViewportWindow& viewport) { + viewport.toggleAreaTool(); } -void toggleVolume(ViewportWindow& vp) { - vp.toggleVolumeTool(); +void toggleVolume(ViewportWindow& viewport) { + viewport.toggleVolumeTool(); } -void hideSelected(ViewportWindow& vp) { - vp.hideSelectedElements(); +void hideSelected(ViewportWindow& viewport) { + viewport.hideSelectedElements(); } -void isolateSelected(ViewportWindow& vp) { - vp.isolateSelectedElements(); +void isolateSelected(ViewportWindow& viewport) { + viewport.isolateSelectedElements(); } -void showAll(ViewportWindow& vp) { - vp.showAllElements(); +void showAll(ViewportWindow& viewport) { + viewport.showAllElements(); } -void invertVisibility(ViewportWindow& vp) { - vp.invertElementVisibility(); +void invertVisibility(ViewportWindow& viewport) { + viewport.invertElementVisibility(); } } // namespace bonsaiviewer::modules::viewport::commands diff --git a/src/bonsaiviewer/modules/viewport/Commands.h b/src/bonsaiviewer/modules/viewport/Commands.h index 0233873faa..dea82a32ee 100644 --- a/src/bonsaiviewer/modules/viewport/Commands.h +++ b/src/bonsaiviewer/modules/viewport/Commands.h @@ -26,22 +26,22 @@ namespace bonsaiviewer { class SessionState; } namespace bonsaiviewer::modules::viewport::commands { -void setHome(SessionState& session, ViewportWindow& vp); -void goHome(SessionState& session, ViewportWindow& vp); -void viewSelected(ViewportWindow& vp); +void setHome(SessionState& session, ViewportWindow& viewport); +void goHome(SessionState& session, ViewportWindow& viewport); +void viewSelected(ViewportWindow& viewport); -void fly(SessionState& session, ViewportWindow& vp); -void toggleSection(SessionState& session, ViewportWindow& vp); -void clearSection(SessionState& session, ViewportWindow& vp); +void fly(SessionState& session, ViewportWindow& viewport); +void toggleSection(SessionState& session, ViewportWindow& viewport); +void clearSection(SessionState& session, ViewportWindow& viewport); -void toggleDistance(ViewportWindow& vp); -void toggleArea(ViewportWindow& vp); -void toggleVolume(ViewportWindow& vp); +void toggleDistance(ViewportWindow& viewport); +void toggleArea(ViewportWindow& viewport); +void toggleVolume(ViewportWindow& viewport); -void hideSelected(ViewportWindow& vp); -void isolateSelected(ViewportWindow& vp); -void showAll(ViewportWindow& vp); -void invertVisibility(ViewportWindow& vp); +void hideSelected(ViewportWindow& viewport); +void isolateSelected(ViewportWindow& viewport); +void showAll(ViewportWindow& viewport); +void invertVisibility(ViewportWindow& viewport); } // namespace bonsaiviewer::modules::viewport::commands diff --git a/src/bonsaiviewer/modules/viewport/View.cpp b/src/bonsaiviewer/modules/viewport/View.cpp index cf9140bf31..1f49941f71 100644 --- a/src/bonsaiviewer/modules/viewport/View.cpp +++ b/src/bonsaiviewer/modules/viewport/View.cpp @@ -67,9 +67,9 @@ ViewportView::ViewportView(bonsaiviewer::SessionState* session_state, // first geometry-ready consumes the arm). refresh() stays terminal — // any federation mutation from the guess propagates through // SessionState's federatedFalseOriginChanged relay. - connect(session_state_, &SessionState::modelGeometryReady, this, [this](uint32_t mid) { + connect(session_state_, &SessionState::modelGeometryReady, this, [this](uint32_t model_id) { if (modules::models::consumeFederatedFalseOriginGuess()) { - guessFederatedFalseOriginFromFirstModel(mid); + guessFederatedFalseOriginFromFirstModel(model_id); } refresh(); }); @@ -136,34 +136,34 @@ void ViewportView::refresh() { viewport_->setFederatedFalseOrigin( composeFederatedFalseOrigin(federation->federatedFalseOrigin(), federation->config())); - for (uint32_t mid : session_state_->modelIds()) { - applyCoordinateOperation(mid); - applyModelVisibility(mid); + for (uint32_t model_id : session_state_->modelIds()) { + applyCoordinateOperation(model_id); + applyModelVisibility(model_id); } } -void ViewportView::applyCoordinateOperation(uint32_t mid) { +void ViewportView::applyCoordinateOperation(uint32_t model_id) { SceneLoader* loader = session_state_->loader(); Eigen::Matrix4d matrix = Eigen::Matrix4d::Identity(); - if (const ModelGeoref* georef = loader->modelGeoref(mid)) { + if (const ModelGeoref* georef = loader->modelGeoref(model_id)) { if (georef->has_coordinate_operation) { matrix = georef->coordinate_operation_meters; } } - viewport_->setModelCoordinateOperation(mid, matrix); - applyModelTransformation(mid); + viewport_->setModelCoordinateOperation(model_id, matrix); + applyModelTransformation(model_id); } -void ViewportView::applyModelTransformation(uint32_t mid) { +void ViewportView::applyModelTransformation(uint32_t model_id) { Federation* federation = session_state_->federation(); SceneLoader* loader = session_state_->loader(); Eigen::Matrix4d matrix = Eigen::Matrix4d::Identity(); - const QString fed_id = session_state_->fedIdForModelId(mid); + const QString fed_id = session_state_->fedIdForModelId(model_id); if (!fed_id.isEmpty()) { if (const Federation::Model* model = federation->findById(fed_id)) { ModelUnits units; Eigen::Matrix4d coordinate_operation = Eigen::Matrix4d::Identity(); - if (const ModelGeoref* georef = loader->modelGeoref(mid)) { + if (const ModelGeoref* georef = loader->modelGeoref(model_id)) { units = georef->units; if (georef->has_coordinate_operation) { coordinate_operation = georef->coordinate_operation_meters; @@ -173,18 +173,18 @@ void ViewportView::applyModelTransformation(uint32_t mid) { model->model_transformation, federation->config(), units, coordinate_operation); } } - viewport_->setModelTransformation(mid, matrix); + viewport_->setModelTransformation(model_id, matrix); } -void ViewportView::applyModelVisibility(uint32_t mid) { +void ViewportView::applyModelVisibility(uint32_t model_id) { Federation* federation = session_state_->federation(); - const QString fed_id = session_state_->fedIdForModelId(mid); + const QString fed_id = session_state_->fedIdForModelId(model_id); if (fed_id.isEmpty()) return; if (federation->isModelEffectivelyVisible(fed_id)) { - viewport_->showModel(mid); + viewport_->showModel(model_id); } else { - viewport_->hideModel(mid); + viewport_->hideModel(model_id); } } @@ -209,7 +209,7 @@ void ViewportView::applyModelVisibility(uint32_t mid) { // mutation here propagates through SessionState's federation relay // (federatedFalseOriginChanged → notifyFederationChanged) without // re-entering this function. -void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t mid) { +void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t model_id) { Federation* federation = session_state_->federation(); if (!federation->filePath().isEmpty()) return; @@ -218,10 +218,10 @@ void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t mid) { if (current.xyz != defaults.xyz || current.rz_deg != defaults.rz_deg) return; Eigen::Vector3d first_geometry_point_m; - if (!viewport_->firstGeometryPointWorldM(mid, first_geometry_point_m)) return; + if (!viewport_->firstGeometryPointWorldM(model_id, first_geometry_point_m)) return; SceneLoader* loader = session_state_->loader(); - const ModelGeoref* georef = loader->modelGeoref(mid); + const ModelGeoref* georef = loader->modelGeoref(model_id); if (georef == nullptr) return; federation->setFederatedFalseOrigin(::guessFederatedFalseOrigin( @@ -236,42 +236,42 @@ void ViewportView::guessFederatedFalseOriginFromFirstModel(uint32_t mid) { // (0,0,0) — the federated false origin in render space — capped at // 100 m so a model with crazy-coord geometry can't pull the camera // back into nothing. - viewport_->frameOnFederatedOrigin(mid, 100.0f); + viewport_->frameOnFederatedOrigin(model_id, 100.0f); } void ViewportView::updateVolumeReadout() { if (viewport_->toolMode() != ViewportWindow::ToolMode::Volume) return; - const auto& sel = viewport_->selection().selectionIds(); - if (sel.empty()) { + const auto& selection_ids = viewport_->selection().selectionIds(); + if (selection_ids.empty()) { viewport_->setHudText(std::string()); viewport_->setOverlayLabels({}); return; } - std::vector ids(sel.begin(), sel.end()); - const auto per_obj = volumesPerObject(*viewport_, ids); + std::vector object_ids(selection_ids.begin(), selection_ids.end()); + const auto volumes_by_object = volumesPerObject(*viewport_, object_ids); double total = 0.0; std::vector labels; - labels.reserve(per_obj.size()); - for (const auto& [oid, v] : per_obj) { - total += v; + labels.reserve(volumes_by_object.size()); + for (const auto& [object_id, volume] : volumes_by_object) { + total += volume; Eigen::Vector3f mn, mx; - if (!viewport_->computeObjectAabb(oid, mn, mx)) continue; + if (!viewport_->computeObjectAabb(object_id, mn, mx)) continue; OverlayRenderer::Label lbl; - const Eigen::Vector3f c = (mn + mx) * 0.5f; - lbl.world_pos[0] = c.x(); - lbl.world_pos[1] = c.y(); - lbl.world_pos[2] = c.z(); - lbl.text = QString::number(v, 'f', 4) + " m³"; + const Eigen::Vector3f center = (mn + mx) * 0.5f; + lbl.world_pos[0] = center.x(); + lbl.world_pos[1] = center.y(); + lbl.world_pos[2] = center.z(); + lbl.text = QString::number(volume, 'f', 4) + " m³"; labels.push_back(std::move(lbl)); } viewport_->setHudText(QString("Volume: %1 m³ (%2 object%3)") .arg(total, 0, 'f', 4) - .arg(per_obj.size()) - .arg(per_obj.size() == 1 ? "" : "s").toStdString()); + .arg(volumes_by_object.size()) + .arg(volumes_by_object.size() == 1 ? "" : "s").toStdString()); viewport_->setOverlayLabels(labels); } diff --git a/src/bonsaiviewer/modules/viewport/View.h b/src/bonsaiviewer/modules/viewport/View.h index 56e356afc4..196b4b4cff 100644 --- a/src/bonsaiviewer/modules/viewport/View.h +++ b/src/bonsaiviewer/modules/viewport/View.h @@ -50,10 +50,10 @@ public: private: void refresh(); - void applyCoordinateOperation(uint32_t mid); - void applyModelTransformation(uint32_t mid); - void applyModelVisibility(uint32_t mid); - void guessFederatedFalseOriginFromFirstModel(uint32_t mid); + void applyCoordinateOperation(uint32_t model_id); + void applyModelTransformation(uint32_t model_id); + void applyModelVisibility(uint32_t model_id); + void guessFederatedFalseOriginFromFirstModel(uint32_t model_id); void updateVolumeReadout(); bonsaiviewer::SessionState* session_state_ = nullptr; diff --git a/src/ifcviewer-minimal/main.cpp b/src/ifcviewer-minimal/main.cpp index bb059b8798..42551be469 100644 --- a/src/ifcviewer-minimal/main.cpp +++ b/src/ifcviewer-minimal/main.cpp @@ -82,10 +82,11 @@ int main(int argc, char* argv[]) { const QStringList parts = parser.value("camera").split(','); if (parts.size() == 6) { bool ok = true; - float v[6]; - for (int i = 0; i < 6 && ok; ++i) v[i] = parts[i].toFloat(&ok); + float camera_values[6]; + for (int i = 0; i < 6 && ok; ++i) camera_values[i] = parts[i].toFloat(&ok); if (ok) { - viewport->setCamera(v[0], v[1], v[2], v[3], v[4], v[5]); + viewport->setCamera(camera_values[0], camera_values[1], camera_values[2], + camera_values[3], camera_values[4], camera_values[5]); } else { Log::warn() << "--camera: failed to parse " << parser.value("camera"); diff --git a/src/ifcviewer/BufferPool.cpp b/src/ifcviewer/BufferPool.cpp index 2dfde93b3e..bdf533587b 100644 --- a/src/ifcviewer/BufferPool.cpp +++ b/src/ifcviewer/BufferPool.cpp @@ -41,8 +41,8 @@ void BufferPool::configure(WGPUInstance instance, WGPUDevice device, } void BufferPool::destroy() { - for (auto& sp : sub_pools_) { - if (sp.buffer) wgpuBufferRelease(sp.buffer); + for (auto& sub_pool : sub_pools_) { + if (sub_pool.buffer) wgpuBufferRelease(sub_pool.buffer); } sub_pools_.clear(); device_ = nullptr; @@ -140,7 +140,7 @@ bool BufferPool::addSubBuffer() { WGPUBuffer buf = wgpuDeviceCreateBuffer(device_, &desc); struct PopResult { bool done = false; bool error = false; }; - auto pop = [&](PopResult& pr) { + auto pop = [&](PopResult& pop_result) { WGPUPopErrorScopeCallbackInfo pcb = {}; pcb.mode = WGPUCallbackMode_AllowProcessEvents; pcb.callback = [](WGPUPopErrorScopeStatus, WGPUErrorType type, @@ -149,9 +149,9 @@ bool BufferPool::addSubBuffer() { p->done = true; p->error = (type != WGPUErrorType_NoError); }; - pcb.userdata1 = ≺ + pcb.userdata1 = &pop_result; wgpuDevicePopErrorScope(device_, pcb); - while (!pr.done) wgpuInstanceProcessEvents(instance_); + while (!pop_result.done) wgpuInstanceProcessEvents(instance_); }; PopResult oom_pop, validation_pop; pop(oom_pop); @@ -205,11 +205,12 @@ void BufferPool::resolveProvisionalGrowth(bool failed) { (unsigned long long)(total_capacity_bytes() / (1024 * 1024)), sub_pools_.size()); } else { - sub_pools_[i].provisional = false; + SubPool& sub_pool = sub_pools_[i]; + sub_pool.provisional = false; std::fprintf(stderr, "[wgpu pool] added sub-buffer %zu (%llu MB); pool total now %llu MB\n", i, - (unsigned long long)(sub_pools_[i].capacity / (1024 * 1024)), + (unsigned long long)(sub_pool.capacity / (1024 * 1024)), (unsigned long long)(total_capacity_bytes() / (1024 * 1024))); } return; @@ -225,34 +226,34 @@ BufferPool::Slice BufferPool::alloc(uint64_t size, uint64_t align) { // adding another sub-buffer and retry once. for (int attempt = 0; attempt < 2; ++attempt) { for (size_t sp_idx = 0; sp_idx < sub_pools_.size(); ++sp_idx) { - SubPool& sp = sub_pools_[sp_idx]; + SubPool& sub_pool = sub_pools_[sp_idx]; // Web: never allocate out of a sub-buffer still awaiting OOM // validation — its handle may be a Dawn error buffer. - if (sp.provisional) continue; - for (size_t i = 0; i < sp.free_ranges.size(); ++i) { - const FreeRange& r = sp.free_ranges[i]; - const uint64_t aligned = (r.offset + (align - 1)) & ~(align - 1); - const uint64_t pad = aligned - r.offset; - if (pad >= r.size) continue; - if (size > r.size - pad) continue; + if (sub_pool.provisional) continue; + for (size_t i = 0; i < sub_pool.free_ranges.size(); ++i) { + const FreeRange& free_range = sub_pool.free_ranges[i]; + const uint64_t aligned = (free_range.offset + (align - 1)) & ~(align - 1); + const uint64_t alignment_padding = aligned - free_range.offset; + if (alignment_padding >= free_range.size) continue; + if (size > free_range.size - alignment_padding) continue; const uint64_t post_off = aligned + size; - const uint64_t post_size = (r.offset + r.size) - post_off; + const uint64_t post_size = (free_range.offset + free_range.size) - post_off; - if (pad == 0 && post_size == 0) { - sp.free_ranges.erase(sp.free_ranges.begin() + i); - } else if (pad == 0) { - sp.free_ranges[i] = {post_off, post_size}; + if (alignment_padding == 0 && post_size == 0) { + sub_pool.free_ranges.erase(sub_pool.free_ranges.begin() + i); + } else if (alignment_padding == 0) { + sub_pool.free_ranges[i] = {post_off, post_size}; } else if (post_size == 0) { - sp.free_ranges[i] = {r.offset, pad}; + sub_pool.free_ranges[i] = {free_range.offset, alignment_padding}; } else { - sp.free_ranges[i] = {r.offset, pad}; - sp.free_ranges.insert(sp.free_ranges.begin() + i + 1, + sub_pool.free_ranges[i] = {free_range.offset, alignment_padding}; + sub_pool.free_ranges.insert(sub_pool.free_ranges.begin() + i + 1, {post_off, post_size}); } - sp.used += size; - out.buffer = sp.buffer; + sub_pool.used += size; + out.buffer = sub_pool.buffer; out.offset = aligned; out.size = size; out.sub_idx = int(sp_idx); @@ -270,50 +271,56 @@ BufferPool::Slice BufferPool::alloc(uint64_t size, uint64_t align) { void BufferPool::free(const Slice& s) { if (!s.valid()) return; if (s.sub_idx < 0 || size_t(s.sub_idx) >= sub_pools_.size()) return; - SubPool& sp = sub_pools_[size_t(s.sub_idx)]; - assert(s.offset + s.size <= sp.capacity); + SubPool& sub_pool = sub_pools_[size_t(s.sub_idx)]; + assert(s.offset + s.size <= sub_pool.capacity); size_t i = 0; - while (i < sp.free_ranges.size() && sp.free_ranges[i].offset < s.offset) ++i; - sp.free_ranges.insert(sp.free_ranges.begin() + i, {s.offset, s.size}); - sp.used -= s.size; + while (i < sub_pool.free_ranges.size() && sub_pool.free_ranges[i].offset < s.offset) ++i; + sub_pool.free_ranges.insert(sub_pool.free_ranges.begin() + i, {s.offset, s.size}); + sub_pool.used -= s.size; - if (i + 1 < sp.free_ranges.size() - && sp.free_ranges[i].offset + sp.free_ranges[i].size == sp.free_ranges[i + 1].offset) { - sp.free_ranges[i].size += sp.free_ranges[i + 1].size; - sp.free_ranges.erase(sp.free_ranges.begin() + i + 1); + if (i + 1 < sub_pool.free_ranges.size() + && sub_pool.free_ranges[i].offset + sub_pool.free_ranges[i].size + == sub_pool.free_ranges[i + 1].offset) { + sub_pool.free_ranges[i].size += sub_pool.free_ranges[i + 1].size; + sub_pool.free_ranges.erase(sub_pool.free_ranges.begin() + i + 1); } if (i > 0 - && sp.free_ranges[i - 1].offset + sp.free_ranges[i - 1].size == sp.free_ranges[i].offset) { - sp.free_ranges[i - 1].size += sp.free_ranges[i].size; - sp.free_ranges.erase(sp.free_ranges.begin() + i); + && sub_pool.free_ranges[i - 1].offset + sub_pool.free_ranges[i - 1].size + == sub_pool.free_ranges[i].offset) { + sub_pool.free_ranges[i - 1].size += sub_pool.free_ranges[i].size; + sub_pool.free_ranges.erase(sub_pool.free_ranges.begin() + i); } } uint64_t BufferPool::total_capacity_bytes() const { - uint64_t s = 0; + uint64_t total_capacity = 0; // Skip provisional sub-pools (web, awaiting OOM validation) — their // capacity isn't usable yet, so counting it would mislead the // evictor's "is there room?" heuristics. - for (const auto& sp : sub_pools_) if (!sp.provisional) s += sp.capacity; - return s; + for (const auto& sub_pool : sub_pools_) { + if (!sub_pool.provisional) total_capacity += sub_pool.capacity; + } + return total_capacity; } uint64_t BufferPool::total_used_bytes() const { - uint64_t s = 0; - for (const auto& sp : sub_pools_) if (!sp.provisional) s += sp.used; - return s; + uint64_t total_used = 0; + for (const auto& sub_pool : sub_pools_) { + if (!sub_pool.provisional) total_used += sub_pool.used; + } + return total_used; } uint64_t BufferPool::largest_free_run_bytes() const { - uint64_t m = 0; - for (const auto& sp : sub_pools_) { - if (sp.provisional) continue; - for (const auto& r : sp.free_ranges) { - if (r.size > m) m = r.size; + uint64_t largest_free_run = 0; + for (const auto& sub_pool : sub_pools_) { + if (sub_pool.provisional) continue; + for (const auto& free_range : sub_pool.free_ranges) { + if (free_range.size > largest_free_run) largest_free_run = free_range.size; } } - return m; + return largest_free_run; } void BufferPool::addSubBufferForTesting(WGPUBuffer fake_buffer, uint64_t capacity) { diff --git a/src/ifcviewer/GeometryStreamer.cpp b/src/ifcviewer/GeometryStreamer.cpp index 1acd88023d..ff08aad348 100644 --- a/src/ifcviewer/GeometryStreamer.cpp +++ b/src/ifcviewer/GeometryStreamer.cpp @@ -42,29 +42,29 @@ struct MaterialInfo { }; static MaterialInfo materialFromStyle(const ifcopenshell::geometry::taxonomy::style::ptr& style) { - MaterialInfo m; - if (!style) return m; + MaterialInfo material; + if (!style) return material; const auto& color = style->get_color(); if (color) { - m.r = static_cast(color.r()); - m.g = static_cast(color.g()); - m.b = static_cast(color.b()); + material.r = static_cast(color.r()); + material.g = static_cast(color.g()); + material.b = static_cast(color.b()); } if (!std::isnan(style->transparency)) { - m.a = 1.0f - static_cast(style->transparency); + material.a = 1.0f - static_cast(style->transparency); } - return m; + return material; } -static inline uint32_t packRGBA8(const MaterialInfo& m) { - auto to_byte = [](float v) -> uint32_t { - float c = std::clamp(v, 0.0f, 1.0f); - return static_cast(c * 255.0f + 0.5f); +static inline uint32_t packRGBA8(const MaterialInfo& material) { + auto to_byte = [](float channel_value) -> uint32_t { + float clamped_value = std::clamp(channel_value, 0.0f, 1.0f); + return static_cast(clamped_value * 255.0f + 0.5f); }; - uint32_t r = to_byte(m.r); - uint32_t g = to_byte(m.g); - uint32_t b = to_byte(m.b); - uint32_t a = to_byte(m.a); + uint32_t r = to_byte(material.r); + uint32_t g = to_byte(material.g); + uint32_t b = to_byte(material.b); + uint32_t a = to_byte(material.a); // Little-endian byte layout [r,g,b,a] for GL_UNSIGNED_BYTE * 4 normalized. return r | (g << 8) | (b << 16) | (a << 24); } @@ -188,12 +188,12 @@ static MeshChunk buildMeshChunk(uint32_t model_id, chunk.indices.reserve(faces.size()); // Track local AABB as we emit vertices. - float amin[3] = { std::numeric_limits::max(), - std::numeric_limits::max(), - std::numeric_limits::max() }; - float amax[3] = { -std::numeric_limits::max(), - -std::numeric_limits::max(), - -std::numeric_limits::max() }; + float local_aabb_min[3] = { std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max() }; + float local_aabb_max[3] = { -std::numeric_limits::max(), + -std::numeric_limits::max(), + -std::numeric_limits::max() }; auto emit_vertex = [&](uint32_t orig_idx, int mat_id) -> uint32_t { const uint64_t key = make_key(orig_idx, mat_id); @@ -211,9 +211,12 @@ static MeshChunk buildMeshChunk(uint32_t model_id, chunk.vertices.push_back(px); chunk.vertices.push_back(py); chunk.vertices.push_back(pz); - if (px < amin[0]) amin[0] = px; if (px > amax[0]) amax[0] = px; - if (py < amin[1]) amin[1] = py; if (py > amax[1]) amax[1] = py; - if (pz < amin[2]) amin[2] = pz; if (pz > amax[2]) amax[2] = pz; + if (px < local_aabb_min[0]) local_aabb_min[0] = px; + if (px > local_aabb_max[0]) local_aabb_max[0] = px; + if (py < local_aabb_min[1]) local_aabb_min[1] = py; + if (py > local_aabb_max[1]) local_aabb_max[1] = py; + if (pz < local_aabb_min[2]) local_aabb_min[2] = pz; + if (pz > local_aabb_max[2]) local_aabb_max[2] = pz; if (orig_idx * 3 + 2 < normals.size()) { chunk.vertices.push_back(static_cast(normals[orig_idx * 3 + 0])); @@ -246,11 +249,11 @@ static MeshChunk buildMeshChunk(uint32_t model_id, } if (chunk.vertices.empty()) { - for (int a = 0; a < 3; ++a) amin[a] = amax[a] = 0.0f; + for (int a = 0; a < 3; ++a) local_aabb_min[a] = local_aabb_max[a] = 0.0f; } for (int a = 0; a < 3; ++a) { - chunk.local_aabb_min[a] = amin[a]; - chunk.local_aabb_max[a] = amax[a]; + chunk.local_aabb_min[a] = local_aabb_min[a]; + chunk.local_aabb_max[a] = local_aabb_max[a]; } return chunk; } @@ -527,7 +530,6 @@ void GeometryStreamer::run(const std::string& path, int num_threads) { emit errorOccurred(QString("Failed to create geometry iterator: %1").arg(e.what())); return false; } - if (!iterator->initialize()) { // No geometry survived this context for the remaining ids. // Subsequent contexts will pick them up; nothing to emit. @@ -604,15 +606,15 @@ void GeometryStreamer::run(const std::string& path, int num_threads) { MeshChunk mesh_chunk = buildMeshChunk(model_id_, local_mesh_id, tri_elem, offset); - MeshAabb ma; + MeshAabb mesh_aabb; for (int a = 0; a < 3; ++a) { - ma.lmin[a] = mesh_chunk.local_aabb_min[a]; - ma.lmax[a] = mesh_chunk.local_aabb_max[a]; - ma.offset[a] = offset[a]; + mesh_aabb.lmin[a] = mesh_chunk.local_aabb_min[a]; + mesh_aabb.lmax[a] = mesh_chunk.local_aabb_max[a]; + mesh_aabb.offset[a] = offset[a]; } - ma.has_offset = (offset.squaredNorm() > 0.0); + mesh_aabb.has_offset = (offset.squaredNorm() > 0.0); if (mesh_aabbs.size() <= local_mesh_id) mesh_aabbs.resize(local_mesh_id + 1); - mesh_aabbs[local_mesh_id] = ma; + mesh_aabbs[local_mesh_id] = mesh_aabb; if (!mesh_chunk.indices.empty()) { emit meshReady(std::move(mesh_chunk)); } @@ -626,11 +628,11 @@ void GeometryStreamer::run(const std::string& path, int num_threads) { Eigen::Matrix4d mat_d = tri_elem->transformation().data()->ccomponents(); if (mesh_aabbs[local_mesh_id].has_offset) { - const Eigen::Vector3d off( + const Eigen::Vector3d mesh_rebase_offset( mesh_aabbs[local_mesh_id].offset[0], mesh_aabbs[local_mesh_id].offset[1], mesh_aabbs[local_mesh_id].offset[2]); - mat_d.block<3, 1>(0, 3) += mat_d.block<3, 3>(0, 0) * off; + mat_d.block<3, 1>(0, 3) += mat_d.block<3, 3>(0, 0) * mesh_rebase_offset; } InstanceChunk inst; @@ -642,25 +644,25 @@ void GeometryStreamer::run(const std::string& path, int num_threads) { inst.transform[i] = mat_d.data()[i]; } - const MeshAabb& ma = mesh_aabbs[local_mesh_id]; + const MeshAabb& mesh_aabb = mesh_aabbs[local_mesh_id]; float mat_f[16]; for (int i = 0; i < 16; ++i) { mat_f[i] = static_cast(inst.transform[i]); } - worldAabbFromLocal(ma.lmin, ma.lmax, mat_f, + worldAabbFromLocal(mesh_aabb.lmin, mesh_aabb.lmax, mat_f, inst.world_aabb_min, inst.world_aabb_max); emit instanceReady(std::move(inst)); total_shapes++; yielded_count++; - const int p = total_count > 0 + const int progress_percent = total_count > 0 ? static_cast((100 * yielded_count) / total_count) : 100; - if (p != last_emitted_progress) { - last_emitted_progress = p; - progress_ = p; - emit progressChanged(p); + if (progress_percent != last_emitted_progress) { + last_emitted_progress = progress_percent; + progress_ = progress_percent; + emit progressChanged(progress_percent); } } while (iterator->next()); diff --git a/src/ifcviewer/InstanceCompose.cpp b/src/ifcviewer/InstanceCompose.cpp index 13e6069007..bd6ce53af8 100644 --- a/src/ifcviewer/InstanceCompose.cpp +++ b/src/ifcviewer/InstanceCompose.cpp @@ -79,16 +79,16 @@ bool findInstanceInModels( const std::unordered_map& 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; + for (const auto& [model_id, model_data] : models) { + auto it = model_data.object_id_to_instance.find(object_id); + if (it == model_data.object_id_to_instance.end()) continue; + const uint32_t instance_index = it->second; + if (instance_index >= model_data.instances.size()) continue; + const InstanceCpu& instance = model_data.instances[instance_index]; + out.model_id = model_id; + out.mesh_id = instance.mesh_id; std::memcpy(out.placement_transformation, - inst.placement_transformation, + instance.placement_transformation, sizeof(out.placement_transformation)); return true; } diff --git a/src/ifcviewer/SidecarBuilder.cpp b/src/ifcviewer/SidecarBuilder.cpp index ebafab97bb..c9b19d32bf 100644 --- a/src/ifcviewer/SidecarBuilder.cpp +++ b/src/ifcviewer/SidecarBuilder.cpp @@ -53,10 +53,10 @@ void SidecarBuilder::onMeshReady(const MeshChunk& chunk) { -std::numeric_limits::infinity(), -std::numeric_limits::infinity() }; for (size_t i = 0; i < n_verts; ++i) { - const float* v = chunk.vertices.data() + i * INSTANCED_VERTEX_STRIDE_FLOATS; + const float* vertex = chunk.vertices.data() + i * INSTANCED_VERTEX_STRIDE_FLOATS; for (int a = 0; a < 3; ++a) { - if (v[a] < bmin[a]) bmin[a] = v[a]; - if (v[a] > bmax[a]) bmax[a] = v[a]; + if (vertex[a] < bmin[a]) bmin[a] = vertex[a]; + if (vertex[a] > bmax[a]) bmax[a] = vertex[a]; } } float extent_recip[3]; @@ -99,25 +99,25 @@ void SidecarBuilder::onMeshReady(const MeshChunk& chunk) { } void SidecarBuilder::onInstanceReady(const InstanceChunk& chunk) { - InstanceCpu inst; - inst.mesh_id = chunk.local_mesh_id; - inst.object_id = chunk.object_id; - inst.color_override_rgba8 = chunk.color_override_rgba8; - inst.model_id = chunk.model_id; + InstanceCpu instance; + instance.mesh_id = chunk.local_mesh_id; + instance.object_id = chunk.object_id; + instance.color_override_rgba8 = chunk.color_override_rgba8; + instance.model_id = chunk.model_id; // The streamer's chunk.transform is the double-precision // placement_transformation. The cached float transform/world_aabb is only // an identity-stage baseline; applyCachedModel recomposes from placement // against the consumer's stage matrices at load time. - std::memcpy(inst.placement_transformation, chunk.transform, - sizeof(inst.placement_transformation)); + std::memcpy(instance.placement_transformation, chunk.transform, + sizeof(instance.placement_transformation)); for (int i = 0; i < 16; ++i) { - inst.transform[i] = static_cast(chunk.transform[i]); + instance.transform[i] = static_cast(chunk.transform[i]); } - std::memcpy(inst.world_aabb_min, chunk.world_aabb_min, sizeof(inst.world_aabb_min)); - std::memcpy(inst.world_aabb_max, chunk.world_aabb_max, sizeof(inst.world_aabb_max)); + std::memcpy(instance.world_aabb_min, chunk.world_aabb_min, sizeof(instance.world_aabb_min)); + std::memcpy(instance.world_aabb_max, chunk.world_aabb_max, sizeof(instance.world_aabb_max)); - sidecar_data_.instances.push_back(inst); + sidecar_data_.instances.push_back(instance); } SidecarData SidecarBuilder::finalize(const ModelGeoref& georef, diff --git a/src/ifcviewer/SidecarCache.cpp b/src/ifcviewer/SidecarCache.cpp index 666097bdc3..3ff1a98b24 100644 --- a/src/ifcviewer/SidecarCache.cpp +++ b/src/ifcviewer/SidecarCache.cpp @@ -59,50 +59,61 @@ static constexpr int kSidecarZstdLevel = 19; // --- In-memory serialisation (a block is built in RAM, then compressed) ------ template -static void appendVec(std::vector& b, const std::vector& v) { - std::uint32_t n = static_cast(v.size()); - const auto* np = reinterpret_cast(&n); - b.insert(b.end(), np, np + 4); - if (n > 0) { - const auto* p = reinterpret_cast(v.data()); - b.insert(b.end(), p, p + std::size_t(sizeof(T)) * n); +static void appendVec(std::vector& buffer, const std::vector& values) { + std::uint32_t count = static_cast(values.size()); + const auto* count_bytes = reinterpret_cast(&count); + buffer.insert(buffer.end(), count_bytes, count_bytes + 4); + if (count > 0) { + const auto* value_bytes = reinterpret_cast(values.data()); + buffer.insert(buffer.end(), value_bytes, value_bytes + std::size_t(sizeof(T)) * count); } } -static void appendBytes(std::vector& b, const void* p, std::size_t n) { - const auto* c = static_cast(p); - b.insert(b.end(), c, c + n); +static void appendBytes(std::vector& buffer, const void* data, std::size_t byte_count) { + const auto* bytes = static_cast(data); + buffer.insert(buffer.end(), bytes, bytes + byte_count); } // Pull one chunk's geometry out of the whole-model vertex/index arrays into the // chunk-LOCAL layout applyStreamedChunk expects: vertices of its meshes in chunk // order, then indices as LOD0 (per mesh) followed by LOD1 (per mesh). -static void extractChunkGeometry(const SidecarData& d, const SidecarChunk& c, +static void extractChunkGeometry(const SidecarData& sidecar_data, const SidecarChunk& sidecar_chunk, std::vector& vbytes, std::vector& ibytes) { vbytes.clear(); ibytes.clear(); - const std::uint32_t end = c.first_mesh + c.mesh_count; - for (std::uint32_t mi = c.first_mesh; mi < end && mi < d.meshes.size(); ++mi) { - const MeshInfo& m = d.meshes[mi]; - const std::size_t voff = m.vbo_byte_offset; - const std::size_t vn = std::size_t(m.vertex_count) * INSTANCED_VERTEX_STRIDE_BYTES; - if (voff + vn <= d.vertices.size()) - vbytes.insert(vbytes.end(), d.vertices.begin() + voff, - d.vertices.begin() + voff + vn); + const std::uint32_t end = sidecar_chunk.first_mesh + sidecar_chunk.mesh_count; + for (std::uint32_t mesh_index = sidecar_chunk.first_mesh; + mesh_index < end && mesh_index < sidecar_data.meshes.size(); + ++mesh_index) { + const MeshInfo& mesh_info = sidecar_data.meshes[mesh_index]; + const std::size_t vertex_offset = mesh_info.vbo_byte_offset; + const std::size_t vertex_byte_count = + std::size_t(mesh_info.vertex_count) * INSTANCED_VERTEX_STRIDE_BYTES; + if (vertex_offset + vertex_byte_count <= sidecar_data.vertices.size()) + vbytes.insert(vbytes.end(), sidecar_data.vertices.begin() + vertex_offset, + sidecar_data.vertices.begin() + vertex_offset + vertex_byte_count); } auto appendIdx = [&](std::size_t first_u32, std::size_t count) { - if (first_u32 + count > d.indices.size()) return; - const auto* p = reinterpret_cast(d.indices.data() + first_u32); - ibytes.insert(ibytes.end(), p, p + count * sizeof(std::uint32_t)); + if (first_u32 + count > sidecar_data.indices.size()) return; + const auto* index_bytes = + reinterpret_cast(sidecar_data.indices.data() + first_u32); + ibytes.insert(ibytes.end(), index_bytes, index_bytes + count * sizeof(std::uint32_t)); }; - for (std::uint32_t mi = c.first_mesh; mi < end && mi < d.meshes.size(); ++mi) { - const MeshInfo& m = d.meshes[mi]; - if (m.index_count) appendIdx(m.ebo_byte_offset / sizeof(std::uint32_t), m.index_count); + for (std::uint32_t mesh_index = sidecar_chunk.first_mesh; + mesh_index < end && mesh_index < sidecar_data.meshes.size(); + ++mesh_index) { + const MeshInfo& mesh_info = sidecar_data.meshes[mesh_index]; + if (mesh_info.index_count) { + appendIdx(mesh_info.ebo_byte_offset / sizeof(std::uint32_t), mesh_info.index_count); + } } - for (std::uint32_t mi = c.first_mesh; mi < end && mi < d.meshes.size(); ++mi) { - const MeshInfo& m = d.meshes[mi]; - if (m.lod1_index_count) - appendIdx(m.lod1_ebo_byte_offset / sizeof(std::uint32_t), m.lod1_index_count); + for (std::uint32_t mesh_index = sidecar_chunk.first_mesh; + mesh_index < end && mesh_index < sidecar_data.meshes.size(); + ++mesh_index) { + const MeshInfo& mesh_info = sidecar_data.meshes[mesh_index]; + if (mesh_info.lod1_index_count) { + appendIdx(mesh_info.lod1_ebo_byte_offset / sizeof(std::uint32_t), mesh_info.lod1_index_count); + } } } #endif // !__EMSCRIPTEN__ (bake-only serialisation helpers) @@ -118,14 +129,14 @@ struct SidecarHeader { // foo.ifcdb -> foo.ifcview // foo (no ext) -> foo.ifcview static std::string sidecarPath(const std::string& ifc_path) { - std::string p = ifc_path; - while (!p.empty() && (p.back() == '/' || p.back() == '\\')) p.pop_back(); - auto slash = p.find_last_of("/\\"); - auto dot = p.find_last_of('.'); + std::string path = ifc_path; + while (!path.empty() && (path.back() == '/' || path.back() == '\\')) path.pop_back(); + auto slash = path.find_last_of("/\\"); + auto dot = path.find_last_of('.'); std::string stem = (dot != std::string::npos && (slash == std::string::npos || dot > slash)) - ? p.substr(0, dot) - : p; + ? path.substr(0, dot) + : path; return stem + ".ifcview"; } @@ -152,18 +163,18 @@ bool writeSidecar(const std::string& ifc_path, const SidecarData& data) { FILE* f = fopen(path.c_str(), "wb"); if (!f) return false; - auto wr = [&](const void* p, std::size_t n) { - return fwrite(p, 1, n, f) == n; + auto write_bytes = [&](const void* data, std::size_t byte_count) { + return fwrite(data, 1, byte_count, f) == byte_count; }; - auto wrU64 = [&](std::uint64_t v) { return wr(&v, sizeof(v)); }; + auto wrU64 = [&](std::uint64_t v) { return write_bytes(&v, sizeof(v)); }; auto wrBlock = [&](const std::vector& raw) -> bool { auto z = SidecarCompress::compress(raw.data(), raw.size(), kSidecarZstdLevel); if (raw.size() > 0 && z.empty()) return false; // compress failed - return wrU64(z.size()) && wrU64(raw.size()) && (z.empty() || wr(z.data(), z.size())); + return wrU64(z.size()) && wrU64(raw.size()) && (z.empty() || write_bytes(z.data(), z.size())); }; SidecarHeader hdr = { SIDECAR_MAGIC, SIDECAR_VERSION, SIDECAR_ENDIAN }; - if (!wr(&hdr, sizeof(hdr))) { fclose(f); return false; } + if (!write_bytes(&hdr, sizeof(hdr))) { fclose(f); return false; } // --- Geometry section: per-chunk zstd(vertex) + zstd(index) frames ------- // Offsets in the chunk TOC are relative to the geometry section start, so @@ -174,19 +185,19 @@ bool writeSidecar(const std::string& ifc_path, const SidecarData& data) { std::vector chunks = data.chunks; // fill blob offsets below std::vector vraw, iraw; - for (auto& c : chunks) { - extractChunkGeometry(data, c, vraw, iraw); + for (auto& sidecar_chunk : chunks) { + extractChunkGeometry(data, sidecar_chunk, vraw, iraw); auto vz = SidecarCompress::compress(vraw.data(), vraw.size(), kSidecarZstdLevel); auto iz = SidecarCompress::compress(iraw.data(), iraw.size(), kSidecarZstdLevel); if ((vraw.size() && vz.empty()) || (iraw.size() && iz.empty())) { fclose(f); return false; } - c.v_comp_off = std::uint64_t(ftell(f) - geom_start); - c.v_comp_size = vz.size(); - c.v_raw_size = vraw.size(); - if (!vz.empty() && !wr(vz.data(), vz.size())) { fclose(f); return false; } - c.i_comp_off = std::uint64_t(ftell(f) - geom_start); - c.i_comp_size = iz.size(); - c.i_raw_size = iraw.size(); - if (!iz.empty() && !wr(iz.data(), iz.size())) { fclose(f); return false; } + sidecar_chunk.v_comp_off = std::uint64_t(ftell(f) - geom_start); + sidecar_chunk.v_comp_size = vz.size(); + sidecar_chunk.v_raw_size = vraw.size(); + if (!vz.empty() && !write_bytes(vz.data(), vz.size())) { fclose(f); return false; } + sidecar_chunk.i_comp_off = std::uint64_t(ftell(f) - geom_start); + sidecar_chunk.i_comp_size = iz.size(); + sidecar_chunk.i_raw_size = iraw.size(); + if (!iz.empty() && !write_bytes(iz.data(), iz.size())) { fclose(f); return false; } } const long geom_end = ftell(f); if (geom_start < 0 || geom_end < 0) { fclose(f); return false; } @@ -195,23 +206,23 @@ bool writeSidecar(const std::string& ifc_path, const SidecarData& data) { if (fseek(f, geom_end, SEEK_SET) != 0) { fclose(f); return false; } // --- Critical metadata block (zstd): meshes, instances, georef, chunk TOC - std::vector crit; - appendVec(crit, data.meshes); - appendVec(crit, data.instances); - appendBytes(crit, &data.has_coordinate_operation, 4); - appendBytes(crit, data.coordinate_operation_meters, sizeof(double) * 16); - appendBytes(crit, &data.project_length_to_meters, sizeof(double)); - appendBytes(crit, &data.map_unit_to_meters, sizeof(double)); - appendVec(crit, chunks); - if (!wrBlock(crit)) { fclose(f); return false; } + std::vector critical_metadata; + appendVec(critical_metadata, data.meshes); + appendVec(critical_metadata, data.instances); + appendBytes(critical_metadata, &data.has_coordinate_operation, 4); + appendBytes(critical_metadata, data.coordinate_operation_meters, sizeof(double) * 16); + appendBytes(critical_metadata, &data.project_length_to_meters, sizeof(double)); + appendBytes(critical_metadata, &data.map_unit_to_meters, sizeof(double)); + appendVec(critical_metadata, chunks); + if (!wrBlock(critical_metadata)) { fclose(f); return false; } // --- Deferred metadata block (zstd): element tree + string table --------- - std::vector def; - appendVec(def, data.elements); + std::vector deferred_metadata; + appendVec(deferred_metadata, data.elements); std::uint32_t stbl_len = static_cast(data.string_table.size()); - appendBytes(def, &stbl_len, 4); - appendBytes(def, data.string_table.data(), stbl_len); - if (!wrBlock(def)) { fclose(f); return false; } + appendBytes(deferred_metadata, &stbl_len, 4); + appendBytes(deferred_metadata, data.string_table.data(), stbl_len); + if (!wrBlock(deferred_metadata)) { fclose(f); return false; } fclose(f); return true; @@ -257,28 +268,30 @@ std::optional readSidecar(const std::string& ifc_path) { if (hdr.magic != SIDECAR_MAGIC || hdr.version != SIDECAR_VERSION || hdr.endian != SIDECAR_ENDIAN) return fail(); - auto rd = [&](void* p, std::size_t k) { return fread(p, 1, k, f) == k; }; - auto rdU64 = [&](std::uint64_t& v) { return rd(&v, sizeof(v)); }; + auto read_bytes = [&](void* data, std::size_t byte_count) { + return fread(data, 1, byte_count, f) == byte_count; + }; + auto rdU64 = [&](std::uint64_t& v) { return read_bytes(&v, sizeof(v)); }; std::uint64_t geom_bytes = 0; if (!rdU64(geom_bytes)) return fail(); std::vector geom(static_cast(geom_bytes)); - if (geom_bytes && !rd(geom.data(), geom.size())) return fail(); + if (geom_bytes && !read_bytes(geom.data(), geom.size())) return fail(); auto readBlock = [&](std::vector& out) -> bool { std::uint64_t comp = 0, raw = 0; if (!rdU64(comp) || !rdU64(raw)) return false; std::vector z(static_cast(comp)); - if (comp && !rd(z.data(), z.size())) return false; + if (comp && !read_bytes(z.data(), z.size())) return false; out.assign(std::size_t(raw), 0); return SidecarCompress::decompress(z.data(), z.size(), out.data(), out.size()); }; - std::vector crit, def; - if (!readBlock(crit) || !readBlock(def)) return fail(); + std::vector critical_metadata, deferred_metadata; + if (!readBlock(critical_metadata) || !readBlock(deferred_metadata)) return fail(); fclose(f); SidecarData data; - BufReader cr{ crit.data(), crit.size() }; + BufReader cr{ critical_metadata.data(), critical_metadata.size() }; if (!cr.takeVec(data.meshes)) return std::nullopt; if (!cr.takeVec(data.instances)) return std::nullopt; if (!cr.take(&data.has_coordinate_operation, 4)) return std::nullopt; @@ -287,7 +300,7 @@ std::optional readSidecar(const std::string& ifc_path) { if (!cr.take(&data.map_unit_to_meters, sizeof(double))) return std::nullopt; if (!cr.takeVec(data.chunks)) return std::nullopt; - BufReader dr{ def.data(), def.size() }; + BufReader dr{ deferred_metadata.data(), deferred_metadata.size() }; if (!dr.takeVec(data.elements)) return std::nullopt; std::uint32_t stbl_len = 0; if (!dr.take(&stbl_len, 4)) return std::nullopt; @@ -296,46 +309,68 @@ std::optional readSidecar(const std::string& ifc_path) { // Reconstruct the whole-model vertex/index arrays from the per-chunk blobs. std::size_t vsize = 0, isize = 0; - for (const auto& m : data.meshes) { + for (const auto& mesh_info : data.meshes) { vsize = std::max(vsize, - std::size_t(m.vbo_byte_offset) + std::size_t(m.vertex_count) * INSTANCED_VERTEX_STRIDE_BYTES); - isize = std::max(isize, m.ebo_byte_offset / sizeof(std::uint32_t) + m.index_count); - if (m.lod1_index_count) - isize = std::max(isize, m.lod1_ebo_byte_offset / sizeof(std::uint32_t) + m.lod1_index_count); + std::size_t(mesh_info.vbo_byte_offset) + + std::size_t(mesh_info.vertex_count) * INSTANCED_VERTEX_STRIDE_BYTES); + isize = std::max( + isize, mesh_info.ebo_byte_offset / sizeof(std::uint32_t) + mesh_info.index_count); + if (mesh_info.lod1_index_count) { + isize = std::max( + isize, + mesh_info.lod1_ebo_byte_offset / sizeof(std::uint32_t) + mesh_info.lod1_index_count); + } } data.vertices.assign(vsize, 0); data.indices.assign(isize, 0); - for (const auto& c : data.chunks) { - if (c.v_comp_off + c.v_comp_size > geom.size() || - c.i_comp_off + c.i_comp_size > geom.size()) return std::nullopt; - std::vector vraw(static_cast(c.v_raw_size)); - std::vector iraw(static_cast(c.i_raw_size)); - if (!SidecarCompress::decompress(geom.data() + c.v_comp_off, c.v_comp_size, vraw.data(), vraw.size()) || - !SidecarCompress::decompress(geom.data() + c.i_comp_off, c.i_comp_size, iraw.data(), iraw.size())) + for (const auto& sidecar_chunk : data.chunks) { + if (sidecar_chunk.v_comp_off + sidecar_chunk.v_comp_size > geom.size() || + sidecar_chunk.i_comp_off + sidecar_chunk.i_comp_size > geom.size()) return std::nullopt; + std::vector vraw(static_cast(sidecar_chunk.v_raw_size)); + std::vector iraw(static_cast(sidecar_chunk.i_raw_size)); + if (!SidecarCompress::decompress( + geom.data() + sidecar_chunk.v_comp_off, sidecar_chunk.v_comp_size, vraw.data(), vraw.size()) || + !SidecarCompress::decompress( + geom.data() + sidecar_chunk.i_comp_off, sidecar_chunk.i_comp_size, iraw.data(), iraw.size())) return std::nullopt; const auto* iu = reinterpret_cast(iraw.data()); std::size_t vcur = 0, icur = 0; - const std::uint32_t end = c.first_mesh + c.mesh_count; - for (std::uint32_t mi = c.first_mesh; mi < end && mi < data.meshes.size(); ++mi) { - const MeshInfo& m = data.meshes[mi]; - const std::size_t vn = std::size_t(m.vertex_count) * INSTANCED_VERTEX_STRIDE_BYTES; - if (vcur + vn <= vraw.size() && m.vbo_byte_offset + vn <= data.vertices.size()) - std::memcpy(&data.vertices[m.vbo_byte_offset], vraw.data() + vcur, vn); - vcur += vn; + const std::uint32_t end = sidecar_chunk.first_mesh + sidecar_chunk.mesh_count; + for (std::uint32_t mesh_index = sidecar_chunk.first_mesh; + mesh_index < end && mesh_index < data.meshes.size(); + ++mesh_index) { + const MeshInfo& mesh_info = data.meshes[mesh_index]; + const std::size_t vertex_byte_count = + std::size_t(mesh_info.vertex_count) * INSTANCED_VERTEX_STRIDE_BYTES; + if (vcur + vertex_byte_count <= vraw.size() && + mesh_info.vbo_byte_offset + vertex_byte_count <= data.vertices.size()) { + std::memcpy(&data.vertices[mesh_info.vbo_byte_offset], vraw.data() + vcur, vertex_byte_count); + } + vcur += vertex_byte_count; } - for (std::uint32_t mi = c.first_mesh; mi < end && mi < data.meshes.size(); ++mi) { - const MeshInfo& m = data.meshes[mi]; - if (!m.index_count) continue; - if (icur + m.index_count <= iraw.size() / 4) - std::memcpy(&data.indices[m.ebo_byte_offset / sizeof(std::uint32_t)], iu + icur, m.index_count * 4); - icur += m.index_count; + for (std::uint32_t mesh_index = sidecar_chunk.first_mesh; + mesh_index < end && mesh_index < data.meshes.size(); + ++mesh_index) { + const MeshInfo& mesh_info = data.meshes[mesh_index]; + if (!mesh_info.index_count) continue; + if (icur + mesh_info.index_count <= iraw.size() / 4) { + std::memcpy(&data.indices[mesh_info.ebo_byte_offset / sizeof(std::uint32_t)], + iu + icur, + mesh_info.index_count * 4); + } + icur += mesh_info.index_count; } - for (std::uint32_t mi = c.first_mesh; mi < end && mi < data.meshes.size(); ++mi) { - const MeshInfo& m = data.meshes[mi]; - if (!m.lod1_index_count) continue; - if (icur + m.lod1_index_count <= iraw.size() / 4) - std::memcpy(&data.indices[m.lod1_ebo_byte_offset / sizeof(std::uint32_t)], iu + icur, m.lod1_index_count * 4); - icur += m.lod1_index_count; + for (std::uint32_t mesh_index = sidecar_chunk.first_mesh; + mesh_index < end && mesh_index < data.meshes.size(); + ++mesh_index) { + const MeshInfo& mesh_info = data.meshes[mesh_index]; + if (!mesh_info.lod1_index_count) continue; + if (icur + mesh_info.lod1_index_count <= iraw.size() / 4) { + std::memcpy(&data.indices[mesh_info.lod1_ebo_byte_offset / sizeof(std::uint32_t)], + iu + icur, + mesh_info.lod1_index_count * 4); + } + icur += mesh_info.lod1_index_count; } } return data; diff --git a/src/ifcviewer/SidecarLayout.cpp b/src/ifcviewer/SidecarLayout.cpp index c7f699440b..a80abc6216 100644 --- a/src/ifcviewer/SidecarLayout.cpp +++ b/src/ifcviewer/SidecarLayout.cpp @@ -26,37 +26,42 @@ #include void reorderSidecarByMorton(SidecarData& sd) { - const std::size_t n = sd.meshes.size(); - if (n < 2) return; + const std::size_t mesh_count = sd.meshes.size(); + if (mesh_count < 2) return; // Per-mesh centroid + instance count, exactly as the loader computes them // before chunk planning (average of instance world-AABB centres). - std::vector cx(n, 0.0f), cy(n, 0.0f), cz(n, 0.0f); - std::vector cnt(n, 0); + std::vector mesh_centroid_x(mesh_count, 0.0f), + mesh_centroid_y(mesh_count, 0.0f), + mesh_centroid_z(mesh_count, 0.0f); + std::vector mesh_instance_count(mesh_count, 0); for (const auto& inst : sd.instances) { - if (inst.mesh_id >= n) continue; - cx[inst.mesh_id] += 0.5f * (inst.world_aabb_min[0] + inst.world_aabb_max[0]); - cy[inst.mesh_id] += 0.5f * (inst.world_aabb_min[1] + inst.world_aabb_max[1]); - cz[inst.mesh_id] += 0.5f * (inst.world_aabb_min[2] + inst.world_aabb_max[2]); - ++cnt[inst.mesh_id]; + if (inst.mesh_id >= mesh_count) continue; + mesh_centroid_x[inst.mesh_id] += 0.5f * (inst.world_aabb_min[0] + inst.world_aabb_max[0]); + mesh_centroid_y[inst.mesh_id] += 0.5f * (inst.world_aabb_min[1] + inst.world_aabb_max[1]); + mesh_centroid_z[inst.mesh_id] += 0.5f * (inst.world_aabb_min[2] + inst.world_aabb_max[2]); + ++mesh_instance_count[inst.mesh_id]; } - for (std::size_t i = 0; i < n; ++i) { - if (cnt[i] > 0) { - const float inv = 1.0f / float(cnt[i]); - cx[i] *= inv; cy[i] *= inv; cz[i] *= inv; + for (std::size_t i = 0; i < mesh_count; ++i) { + if (mesh_instance_count[i] > 0) { + const float inv = 1.0f / float(mesh_instance_count[i]); + mesh_centroid_x[i] *= inv; + mesh_centroid_y[i] *= inv; + mesh_centroid_z[i] *= inv; } } // order[new_id] = old mesh id, in the loader's Morton order. const std::vector order = - ChunkPlanner::sortMeshIdsByMorton(n, cx, cy, cz, cnt); + ChunkPlanner::sortMeshIdsByMorton( + mesh_count, mesh_centroid_x, mesh_centroid_y, mesh_centroid_z, mesh_instance_count); // Greedy-pack the sorted order into chunks (the same plan the loader used // to derive). Each chunk is a CONSECUTIVE run of `order`, so once we lay // meshes out in `order` the chunk is a contiguous mesh range — recorded in // the TOC as {first_mesh, mesh_count}. - std::vector mesh_vertex_count(n, 0); - for (std::size_t i = 0; i < n; ++i) mesh_vertex_count[i] = sd.meshes[i].vertex_count; + std::vector mesh_vertex_count(mesh_count, 0); + for (std::size_t i = 0; i < mesh_count; ++i) mesh_vertex_count[i] = sd.meshes[i].vertex_count; const std::vector> packed = ChunkPlanner::greedyPackChunks( order, mesh_vertex_count, INSTANCED_VERTEX_STRIDE_BYTES, WGPU_CHUNK_VERTEX_BYTES_LIMIT); @@ -74,58 +79,61 @@ void reorderSidecarByMorton(SidecarData& sd) { // MeshInfo.first_instance: the baker leaves it 0 for every mesh and stores // instances ungrouped, so first_instance describes nothing. Grouping here // by mesh_id both reorders instances correctly AND fixes first_instance. - std::vector> insts_by_mesh(n); - for (std::uint32_t ii = 0; ii < sd.instances.size(); ++ii) { - const std::uint32_t mid = sd.instances[ii].mesh_id; - if (mid < n) insts_by_mesh[mid].push_back(ii); + std::vector> insts_by_mesh(mesh_count); + for (std::uint32_t instance_index = 0; instance_index < sd.instances.size(); ++instance_index) { + const std::uint32_t mesh_id = sd.instances[instance_index].mesh_id; + if (mesh_id < mesh_count) insts_by_mesh[mesh_id].push_back(instance_index); } std::vector new_vertices; new_vertices.reserve(sd.vertices.size()); std::vector new_indices; new_indices.reserve(sd.indices.size()); - std::vector new_meshes(n); + std::vector new_meshes(mesh_count); std::vector new_instances; new_instances.reserve(sd.instances.size()); // Pass A: vertices + LOD0 indices + instances, mesh-by-mesh in the new // order, recording the new offsets on each MeshInfo. - for (std::uint32_t ni = 0; ni < n; ++ni) { - const std::uint32_t old = order[ni]; - const MeshInfo& om = sd.meshes[old]; - MeshInfo nm = om; // carries AABB; offsets/instance fields overwritten below + for (std::uint32_t new_mesh_index = 0; new_mesh_index < mesh_count; ++new_mesh_index) { + const std::uint32_t old = order[new_mesh_index]; + const MeshInfo& old_mesh_info = sd.meshes[old]; + MeshInfo new_mesh_info = old_mesh_info; // carries AABB; offsets/instance fields overwritten below - nm.vbo_byte_offset = std::uint32_t(new_vertices.size()); - const std::size_t vbytes = std::size_t(om.vertex_count) * INSTANCED_VERTEX_STRIDE_BYTES; + new_mesh_info.vbo_byte_offset = std::uint32_t(new_vertices.size()); + const std::size_t vbytes = std::size_t(old_mesh_info.vertex_count) * INSTANCED_VERTEX_STRIDE_BYTES; new_vertices.insert(new_vertices.end(), - sd.vertices.begin() + om.vbo_byte_offset, - sd.vertices.begin() + om.vbo_byte_offset + vbytes); + sd.vertices.begin() + old_mesh_info.vbo_byte_offset, + sd.vertices.begin() + old_mesh_info.vbo_byte_offset + vbytes); - nm.ebo_byte_offset = std::uint32_t(new_indices.size() * sizeof(std::uint32_t)); - const std::size_t i0 = om.ebo_byte_offset / sizeof(std::uint32_t); + new_mesh_info.ebo_byte_offset = std::uint32_t(new_indices.size() * sizeof(std::uint32_t)); + const std::size_t i0 = old_mesh_info.ebo_byte_offset / sizeof(std::uint32_t); new_indices.insert(new_indices.end(), sd.indices.begin() + i0, - sd.indices.begin() + i0 + om.index_count); + sd.indices.begin() + i0 + old_mesh_info.index_count); - nm.first_instance = std::uint32_t(new_instances.size()); - nm.instance_count = std::uint32_t(insts_by_mesh[old].size()); - for (std::uint32_t ii : insts_by_mesh[old]) { - InstanceCpu ic = sd.instances[ii]; - ic.mesh_id = ni; - new_instances.push_back(ic); + new_mesh_info.first_instance = std::uint32_t(new_instances.size()); + new_mesh_info.instance_count = std::uint32_t(insts_by_mesh[old].size()); + for (std::uint32_t instance_index : insts_by_mesh[old]) { + InstanceCpu instance = sd.instances[instance_index]; + instance.mesh_id = new_mesh_index; + new_instances.push_back(instance); } - new_meshes[ni] = nm; + new_meshes[new_mesh_index] = new_mesh_info; } // Pass B: LOD1 indices appended after all LOD0 (same global layout as the // baker), in the new order, so a chunk's LOD1 slice is contiguous too. - for (std::uint32_t ni = 0; ni < n; ++ni) { - const MeshInfo& om = sd.meshes[order[ni]]; - MeshInfo& nm = new_meshes[ni]; - if (om.lod1_index_count == 0) { nm.lod1_ebo_byte_offset = 0; continue; } - nm.lod1_ebo_byte_offset = std::uint32_t(new_indices.size() * sizeof(std::uint32_t)); - const std::size_t l0 = om.lod1_ebo_byte_offset / sizeof(std::uint32_t); + for (std::uint32_t new_mesh_index = 0; new_mesh_index < mesh_count; ++new_mesh_index) { + const MeshInfo& old_mesh_info = sd.meshes[order[new_mesh_index]]; + MeshInfo& new_mesh_info = new_meshes[new_mesh_index]; + if (old_mesh_info.lod1_index_count == 0) { + new_mesh_info.lod1_ebo_byte_offset = 0; + continue; + } + new_mesh_info.lod1_ebo_byte_offset = std::uint32_t(new_indices.size() * sizeof(std::uint32_t)); + const std::size_t l0 = old_mesh_info.lod1_ebo_byte_offset / sizeof(std::uint32_t); new_indices.insert(new_indices.end(), sd.indices.begin() + l0, - sd.indices.begin() + l0 + om.lod1_index_count); + sd.indices.begin() + l0 + old_mesh_info.lod1_index_count); } sd.vertices = std::move(new_vertices); diff --git a/src/ifcviewer/StreamingLoader.cpp b/src/ifcviewer/StreamingLoader.cpp index 3238e5f321..dea53f79c3 100644 --- a/src/ifcviewer/StreamingLoader.cpp +++ b/src/ifcviewer/StreamingLoader.cpp @@ -52,25 +52,25 @@ struct SidecarHeaderRaw { // walks the metadata tail through one of these so a truncated buffer fails // cleanly (return false) instead of reading out of bounds. struct BufCursor { - const uint8_t* p; - size_t remaining; + const uint8_t* cursor; + size_t remaining_bytes; bool take(void* dst, size_t bytes) { - if (bytes > remaining) return false; - std::memcpy(dst, p, bytes); - p += bytes; - remaining -= bytes; + if (bytes > remaining_bytes) return false; + std::memcpy(dst, cursor, bytes); + cursor += bytes; + remaining_bytes -= bytes; return true; } // Read a uint32 length prefix followed by length*sizeof(T) elements. template - bool takeVec(std::vector& v) { + bool takeVec(std::vector& values) { uint32_t n; if (!take(&n, 4)) return false; - if (uint64_t(n) * sizeof(T) > remaining) return false; - v.resize(n); - if (n > 0 && !take(v.data(), size_t(n) * sizeof(T))) return false; + if (uint64_t(n) * sizeof(T) > remaining_bytes) return false; + values.resize(n); + if (n > 0 && !take(values.data(), size_t(n) * sizeof(T))) return false; return true; } }; @@ -119,7 +119,7 @@ bool parseSidecarDeferred(const uint8_t* data, size_t n, SidecarData& out) { if (!c.takeVec(out.elements)) return false; uint32_t stbl_len = 0; if (!c.take(&stbl_len, 4)) return false; - if (stbl_len > c.remaining) return false; + if (stbl_len > c.remaining_bytes) return false; out.string_table.resize(stbl_len); if (stbl_len > 0 && !c.take(out.string_table.data(), stbl_len)) return false; return true; diff --git a/src/ifcviewer/StreamingThread.cpp b/src/ifcviewer/StreamingThread.cpp index 15ef91d919..39b136c0aa 100644 --- a/src/ifcviewer/StreamingThread.cpp +++ b/src/ifcviewer/StreamingThread.cpp @@ -26,23 +26,23 @@ StreamingThread::~StreamingThread() { } void StreamingThread::start() { - std::unique_lock lk(mu_); + std::unique_lock lock(mu_); if (running_) return; shutdown_ = false; running_ = true; - lk.unlock(); + lock.unlock(); worker_ = std::thread(&StreamingThread::workerLoop, this); } void StreamingThread::stop() { { - std::unique_lock lk(mu_); + std::unique_lock lock(mu_); if (!running_) return; shutdown_ = true; } cv_.notify_all(); if (worker_.joinable()) worker_.join(); - std::unique_lock lk(mu_); + std::unique_lock lock(mu_); running_ = false; requests_.clear(); results_.clear(); @@ -50,7 +50,7 @@ void StreamingThread::stop() { bool StreamingThread::enqueue(Request req) { { - std::unique_lock lk(mu_); + std::unique_lock lock(mu_); if (!running_ || shutdown_) return false; requests_.push_back(std::move(req)); } @@ -59,20 +59,20 @@ bool StreamingThread::enqueue(Request req) { } std::vector StreamingThread::drainResults() { - std::vector out; + std::vector results; { - std::unique_lock lk(mu_); - out.reserve(results_.size()); + std::unique_lock lock(mu_); + results.reserve(results_.size()); while (!results_.empty()) { - out.push_back(std::move(results_.front())); + results.push_back(std::move(results_.front())); results_.pop_front(); } } - return out; + return results; } std::size_t StreamingThread::inFlightApprox() const { - std::unique_lock lk(mu_); + std::unique_lock lock(mu_); return requests_.size() + (in_progress_ ? 1u : 0u); } @@ -80,8 +80,8 @@ void StreamingThread::workerLoop() { for (;;) { Request req; { - std::unique_lock lk(mu_); - cv_.wait(lk, [this]() { return shutdown_ || !requests_.empty(); }); + std::unique_lock lock(mu_); + cv_.wait(lock, [this]() { return shutdown_ || !requests_.empty(); }); if (shutdown_ && requests_.empty()) return; req = std::move(requests_.front()); requests_.pop_front(); @@ -94,18 +94,18 @@ void StreamingThread::workerLoop() { // us. The vbytes / idx buffers are allocated here on the worker // thread — they cross back to the main thread when the result // is drained and applied (pool.alloc + queueWriteBuffer). - Result res; - res.model_id = req.model_id; - res.chunk_idx = req.chunk_idx; - res.success = readChunkGeometryCompressed( + Result result; + result.model_id = req.model_id; + result.chunk_idx = req.chunk_idx; + result.success = readChunkGeometryCompressed( req.file_path, req.geometry_section_offset, req.v_comp_off, req.v_comp_size, req.v_raw_size, req.i_comp_off, req.i_comp_size, req.i_raw_size, - res.vbytes, res.idx); + result.vbytes, result.idx); { - std::unique_lock lk(mu_); - results_.push_back(std::move(res)); + std::unique_lock lock(mu_); + results_.push_back(std::move(result)); in_progress_ = false; } }