diff --git a/src/ifcviewer-wgpu/WgpuViewportWindow.cpp b/src/ifcviewer-wgpu/WgpuViewportWindow.cpp index b820e89034..59d8b7015a 100644 --- a/src/ifcviewer-wgpu/WgpuViewportWindow.cpp +++ b/src/ifcviewer-wgpu/WgpuViewportWindow.cpp @@ -1941,6 +1941,9 @@ uint32_t WgpuViewportWindow::cullModelCpuCompute(WgpuModelGpuData& m, for (uint32_t i = 0; i < uint32_t(m.instances.size()); ++i) { const auto& inst = m.instances[i]; if (inst.mesh_id >= m.meshes.size()) continue; + // Cheapest possible cull first: explicit user-hidden flag. Skips + // every downstream cost (frustum / HiZ / draw / pick). + if (visibility_.isHidden(inst.object_id)) continue; if (!aabbInFrustum(inst.world_aabb_min, inst.world_aabb_max, planes)) continue; const MeshInfo& mesh = m.meshes[inst.mesh_id]; @@ -2972,6 +2975,54 @@ void WgpuViewportWindow::mouseMoveEvent(QMouseEvent* event) { } } +void WgpuViewportWindow::keyPressEvent(QKeyEvent* event) { + // Visibility shortcuts, modelled on the GL viewer: + // H — hide selected + // Shift+H — show all (clear hidden set) + // I — isolate selected (hide everything not currently selected) + // None of these are useful without a selection (except show-all), so we + // skip silently rather than burning a cull on an empty mutation. + const auto mods = event->modifiers(); + const int key = event->key(); + + if (key == Qt::Key_H && (mods & Qt::ShiftModifier)) { + if (visibility_.hiddenCount() == 0) return; + visibility_.clear(); + qInfo() << "[wgpu] show all"; + requestUpdate(); + return; + } + if (key == Qt::Key_H) { + if (selection_.count() == 0) return; + for (uint32_t id : selection_.ids()) visibility_.hide(id); + const size_t n = selection_.count(); + selection_.clear(); // hiding deselects, matching GL behaviour + qInfo().noquote().nospace() << "[wgpu] hid " << n << " selected"; + requestUpdate(); + return; + } + if (key == Qt::Key_I) { + if (selection_.count() == 0) return; + // Walk every instance across all models; hide those NOT in selection. + size_t hidden_now = 0; + for (auto& [mid, m] : models_gpu_) { + for (const auto& inst : m.instances) { + if (selection_.contains(inst.object_id)) continue; + if (!visibility_.isHidden(inst.object_id)) { + visibility_.hide(inst.object_id); + ++hidden_now; + } + } + } + qInfo().noquote().nospace() << "[wgpu] isolated " << selection_.count() + << " (hid " << hidden_now << " others)"; + requestUpdate(); + return; + } + + QWindow::keyPressEvent(event); +} + void WgpuViewportWindow::wheelEvent(QWheelEvent* event) { // 120 = one notch on a typical mouse. Each notch zooms ~12% in/out; // sign matches conventional "wheel up = zoom in". diff --git a/src/ifcviewer-wgpu/WgpuViewportWindow.h b/src/ifcviewer-wgpu/WgpuViewportWindow.h index 5a6d256f57..7a932a4d5c 100644 --- a/src/ifcviewer-wgpu/WgpuViewportWindow.h +++ b/src/ifcviewer-wgpu/WgpuViewportWindow.h @@ -35,6 +35,7 @@ #include "SidecarCache.h" #include "WgpuModelGpuData.h" #include "WgpuSelectionState.h" +#include "WgpuVisibilityState.h" // Stage-2 wgpu viewport: opens a native QWindow, brings up a wgpu instance/ // adapter/device, configures a surface against the platform-native window @@ -106,6 +107,7 @@ protected: void mouseReleaseEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void wheelEvent(QWheelEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; private: bool initWgpu(); @@ -232,6 +234,13 @@ private: WgpuSelectionState selection_; std::vector selection_flags_scratch_; + // Per-element visibility. Consulted in cullModelCpuCompute to drop + // hidden instances before they're added to visible_draws — keeps + // hidden geometry out of cost on every axis (no draw, no depth, no + // pick). Mutated on the main thread between renders; cull workers + // read concurrently which is safe as long as no concurrent writes. + WgpuVisibilityState visibility_; + // Depth attachment (4× MSAA), recreated on surface resize. WGPUTexture depth_texture_ = nullptr; WGPUTextureView depth_view_ = nullptr; diff --git a/src/ifcviewer-wgpu/WgpuVisibilityState.h b/src/ifcviewer-wgpu/WgpuVisibilityState.h new file mode 100644 index 0000000000..e0895b7a89 --- /dev/null +++ b/src/ifcviewer-wgpu/WgpuVisibilityState.h @@ -0,0 +1,59 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#ifndef WGPUVISIBILITYSTATE_H +#define WGPUVISIBILITYSTATE_H + +#include +#include + +// CPU-side per-element visibility. Mirrors src/ifcviewer/Visibility.h shape +// but pure stdlib so it can move into ifcviewer-core later. +// +// Consulted in cullModelCpuCompute: instances whose object_id is in +// hidden_ids_ are dropped from the visible-draws list entirely (no GPU +// work, no triangle in the depth buffer, no pick hit). Concurrent reads +// from multiple cull worker threads are safe as long as no mutations +// happen during render — which is the case here (input handlers +// requestUpdate after mutating, render then reads). +class WgpuVisibilityState { +public: + bool isHidden(uint32_t object_id) const { + return hidden_ids_.count(object_id) > 0; + } + + void hide(uint32_t object_id) { + if (object_id == 0) return; + hidden_ids_.insert(object_id); + } + + void show(uint32_t object_id) { + hidden_ids_.erase(object_id); + } + + void clear() { hidden_ids_.clear(); } + + size_t hiddenCount() const { return hidden_ids_.size(); } + const std::unordered_set& hiddenIds() const { return hidden_ids_; } + +private: + std::unordered_set hidden_ids_; +}; + +#endif // WGPUVISIBILITYSTATE_H