mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
ifcviewer-web: pick logs the object's IFC GUID via the on-demand deferred fetch
First real consumer of the v15 deferred property block, and an end-to-end demonstration that on-demand property loading works. On a left-click pick, logSelectedObjectGuidWeb ensures the owning model's deferred block is loaded (loadDeferredMetadataWeb — a network fetch the FIRST time, cached after) and logs the picked object's GUID to the console. Fix uncovered while wiring it: applyCachedModel rebases instance object_ids to a per-model global base (object_id_base) to keep them unique across models, but the deferred elements carry the sidecar's original local ids — so a lookup by the picked (global) id missed. Store object_id_base on the model and rebase the elements by it when the deferred block loads. Verified: with a streamed model, the deferred block is fetched ONLY after the first pick (not at load), and the pick logs a valid 22-char IFC GUID. 111/111 unit + 6/6 web smoke pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -131,6 +131,9 @@ EM_BOOL onMouseUp(int, const EmscriptenMouseEvent* e, void* user) {
|
||||
const bool remove = e->ctrlKey;
|
||||
app->core.pickObjectAtAsync(px, py, [app, add, remove](std::uint32_t id) {
|
||||
app->core.applyPickToSelection(id, add, remove);
|
||||
// Demo the v15 on-demand deferred fetch: log the picked object's
|
||||
// IFC GUID (first pick fetches the property block off the network).
|
||||
if (id != 0) app->core.logSelectedObjectGuidWeb(id);
|
||||
app->host.requestFrame();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -299,6 +299,10 @@ struct ModelGpuData {
|
||||
uint64_t deferred_meta_offset = 0;
|
||||
uint64_t deferred_meta_bytes = 0;
|
||||
bool deferred_meta_loaded = false;
|
||||
// applyCachedModel rebases instance object_ids by this base to keep them
|
||||
// globally unique across models; deferred elements carry the sidecar's
|
||||
// original (local) ids, so they're rebased by the same amount on load.
|
||||
uint32_t object_id_base = 0;
|
||||
|
||||
// For each mesh in meshes[], the chunk it lives in plus the chunk-local
|
||||
// offsets into that chunk's vertex_storage and index_buffer. Populated
|
||||
|
||||
@@ -3008,6 +3008,7 @@ void ViewportCore::applyCachedModel(std::uint32_t model_id,
|
||||
inst_gpu.push_back(ig);
|
||||
}
|
||||
next_object_id_ = object_id_base + max_local_id + 1;
|
||||
m.object_id_base = object_id_base; // deferred elements rebase to match
|
||||
const std::size_t inst_storage_bytes = inst_gpu.size() * sizeof(InstanceGpu);
|
||||
m.instance_storage = createBufferWithData(
|
||||
device_, queue_,
|
||||
@@ -3603,12 +3604,42 @@ void ViewportCore::loadDeferredMetadataWeb(std::uint32_t model_id,
|
||||
}
|
||||
mit->second.elements = std::move(tmp.elements);
|
||||
mit->second.string_table = std::move(tmp.string_table);
|
||||
// Rebase element object_ids to the model's global id space so they
|
||||
// match the (already-rebased) instance ids used by pick/selection.
|
||||
const std::uint32_t base = mit->second.object_id_base;
|
||||
for (auto& e : mit->second.elements) e.object_id += base;
|
||||
mit->second.deferred_meta_loaded = true;
|
||||
Log::info() << "ifcviewer-web: loaded deferred metadata ("
|
||||
<< mit->second.elements.size() << " elements)";
|
||||
if (done) done(true);
|
||||
});
|
||||
}
|
||||
|
||||
void ViewportCore::logSelectedObjectGuidWeb(std::uint32_t object_id) {
|
||||
InstanceCompose::InstanceLookup lk;
|
||||
if (!findInstance(object_id, lk)) return; // empty pick / unknown id
|
||||
const std::uint32_t model_id = lk.model_id;
|
||||
loadDeferredMetadataWeb(model_id, [this, object_id, model_id](bool ok) {
|
||||
if (!ok) {
|
||||
Log::warn() << "pick: deferred property fetch failed for object " << object_id;
|
||||
return;
|
||||
}
|
||||
auto it = models_gpu_.find(model_id);
|
||||
if (it == models_gpu_.end()) return;
|
||||
const ModelGpuData& m = it->second;
|
||||
for (const auto& e : m.elements) {
|
||||
if (e.object_id != object_id) continue;
|
||||
std::string guid =
|
||||
(e.guid_length > 0 &&
|
||||
std::size_t(e.guid_offset) + e.guid_length <= m.string_table.size())
|
||||
? m.string_table.substr(e.guid_offset, e.guid_length)
|
||||
: std::string("(none)");
|
||||
Log::info() << "pick: object " << object_id << " GUID " << guid;
|
||||
return;
|
||||
}
|
||||
Log::info() << "pick: object " << object_id << " not in element table";
|
||||
});
|
||||
}
|
||||
#endif // __EMSCRIPTEN__
|
||||
|
||||
void ViewportCore::streamingProgress(int& resident_chunks, int& total_chunks) const {
|
||||
|
||||
@@ -375,10 +375,16 @@ public:
|
||||
// table) for a web-streamed model — what a UI (object tree / selected-name
|
||||
// / search) needs, fetched only when asked so first paint never waits on
|
||||
// it. Populates ModelGpuData.elements/string_table; fires done(ok). At most
|
||||
// one fetch per model. Currently unwired (no consumer yet) but complete.
|
||||
// one fetch per model.
|
||||
void loadDeferredMetadataWeb(std::uint32_t model_id,
|
||||
std::function<void(bool)> done = {});
|
||||
|
||||
// Demo consumer of the deferred fetch: on pick, ensure the owning model's
|
||||
// property block is loaded (loadDeferredMetadataWeb — fetched once, on
|
||||
// demand), then log the picked object's IFC GUID. The first pick triggers
|
||||
// the network fetch; later picks reuse the cached element table.
|
||||
void logSelectedObjectGuidWeb(std::uint32_t object_id);
|
||||
|
||||
// Kick off the async read of one chunk's vertex + index byte ranges (from
|
||||
// the active web source). applyStreamedChunk runs in the JS completion
|
||||
// callback; c.is_loading is held until then. No-op if the model/chunk
|
||||
|
||||
Reference in New Issue
Block a user