mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-22 17:01:04 +00:00
Route bonsai through wgpu; delete the GL backend
Bonsai now drives the wgpu viewport for both sidecar and direct-IFC
loads. The GL viewer and its supporting state classes are gone.
SceneLoader rewire:
- Takes WgpuViewportWindow* instead of ViewportWindow*.
- Sidecar path reads metadata only (readSidecarMetadataOnly) and hands
the StreamingSidecar off to the new applyCachedModel. Field accesses
inside applySidecarData go through .meta.
- Direct-IFC path uses the wgpu A-path (upload{Mesh,Instance}Chunk +
finalizeModel). The applyLodExtension call is dropped — wgpu has no
live LOD1 splice; LOD1 still lands in the on-disk sidecar for the
next open.
Bonsai migration:
- ViewportWindow → WgpuViewportWindow across MainWindow, Measurement,
SessionState, and every modules/*/{Commands,Panel,View}.{h,cpp} —
116 sites total. Same s/OverlayRenderer::/WgpuOverlayRenderer::/
rename, 12 sites.
- Includes flipped from ../ifcviewer/ViewportWindow.h to
../ifcviewer-wgpu/WgpuViewportWindow.h. OverlayRenderer.h include
dropped (transitively reached via the viewport header).
- BonsaiViewer links IfcViewerWgpu in addition to IfcViewer for the
duration of the migration; the GL-side IfcViewer also publicly links
IfcViewerWgpu so SceneLoader can resolve WgpuViewportWindow.
GL backend deletion:
- src/ifcviewer/ViewportWindow.{cpp,h}, BvhAccel.*, OverlayRenderer.*,
Selection.*, Visibility.* all gone.
- src/ifcviewer-minimal/ removed entirely (MinimalWindow drove the GL
viewport).
- src/ifcviewer/tests: test_bvh_accel, test_selection, test_visibility
removed. The first has no replacement (wgpu doesn't use a per-instance
BVH); the latter two are ported separately. test_lod_builder,
test_sidecar_cache, test_instanced_geometry, test_federation remain
(backend-agnostic).
- IfcViewer's CMakeLists drops OpenGL, Qt::OpenGL, Qt::Widgets — none
of the surviving translation units reach for them.
Build flag plumbing:
- BUILD_BONSAIVIEWER now auto-enables BUILD_BONSAIVIEWER_WGPU since
SceneLoader requires the wgpu lib for its WgpuViewportWindow* arg.
- The wgpu subprojects add_subdirectory ahead of the GL one so
IfcViewerWgpu exists when IfcViewer's link evaluates.
- src/ifcviewer-minimal subdir reference removed from cmake/CMakeLists.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "Measurement.h"
|
||||
|
||||
#include "ViewportWindow.h"
|
||||
#include "WgpuViewportWindow.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <Eigen/Dense>
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
namespace {
|
||||
|
||||
double meshLocalVolume(const ViewportWindow::MeshTriangles& tris) {
|
||||
double meshLocalVolume(const WgpuViewportWindow::MeshTriangles& tris) {
|
||||
// Signed tetrahedra from the origin: V = sum( a · (b × c) ) / 6.
|
||||
// Absolute value at the end so winding convention doesn't matter.
|
||||
double sum = 0.0;
|
||||
@@ -67,7 +67,7 @@ double det3(const double M[16]) {
|
||||
|
||||
} // namespace
|
||||
|
||||
double volumeOfObjects(ViewportWindow& vp,
|
||||
double volumeOfObjects(WgpuViewportWindow& vp,
|
||||
const std::vector<uint32_t>& object_ids) {
|
||||
if (object_ids.empty()) return 0.0;
|
||||
|
||||
@@ -77,14 +77,14 @@ double volumeOfObjects(ViewportWindow& vp,
|
||||
std::unordered_map<uint64_t, std::vector<double>> by_mesh;
|
||||
by_mesh.reserve(object_ids.size());
|
||||
for (uint32_t oid : object_ids) {
|
||||
ViewportWindow::InstanceLookup lk;
|
||||
WgpuViewportWindow::InstanceLookup lk;
|
||||
if (!vp.findInstance(oid, lk)) continue;
|
||||
const uint64_t key = (uint64_t(lk.model_id) << 32) | lk.mesh_id;
|
||||
by_mesh[key].push_back(std::abs(det3(lk.placement_transformation)));
|
||||
}
|
||||
|
||||
double total = 0.0;
|
||||
ViewportWindow::MeshTriangles tris;
|
||||
WgpuViewportWindow::MeshTriangles tris;
|
||||
for (const auto& [key, dets] : by_mesh) {
|
||||
const uint32_t model_id = uint32_t(key >> 32);
|
||||
const uint32_t mesh_id = uint32_t(key & 0xffffffffu);
|
||||
@@ -96,7 +96,7 @@ double volumeOfObjects(ViewportWindow& vp,
|
||||
}
|
||||
|
||||
std::vector<std::pair<uint32_t, double>>
|
||||
volumesPerObject(ViewportWindow& vp,
|
||||
volumesPerObject(WgpuViewportWindow& vp,
|
||||
const std::vector<uint32_t>& object_ids) {
|
||||
std::vector<std::pair<uint32_t, double>> out;
|
||||
if (object_ids.empty()) return out;
|
||||
@@ -108,9 +108,9 @@ volumesPerObject(ViewportWindow& vp,
|
||||
std::unordered_map<uint64_t, double> mesh_vol_local;
|
||||
mesh_vol_local.reserve(object_ids.size());
|
||||
|
||||
ViewportWindow::MeshTriangles tris;
|
||||
WgpuViewportWindow::MeshTriangles tris;
|
||||
for (uint32_t oid : object_ids) {
|
||||
ViewportWindow::InstanceLookup lk;
|
||||
WgpuViewportWindow::InstanceLookup lk;
|
||||
if (!vp.findInstance(oid, lk)) continue;
|
||||
|
||||
const uint64_t key = (uint64_t(lk.model_id) << 32) | lk.mesh_id;
|
||||
@@ -238,7 +238,7 @@ constexpr double kCoplanarDot = 0.9999; // ~0.81° tolerance
|
||||
|
||||
AreaMeasurement::AreaMeasurement() = default;
|
||||
|
||||
void AreaMeasurement::clear(ViewportWindow& vp) {
|
||||
void AreaMeasurement::clear(WgpuViewportWindow& vp) {
|
||||
mesh_cache_.clear();
|
||||
selected_.clear();
|
||||
total_area_m2_ = 0.0;
|
||||
@@ -246,7 +246,7 @@ void AreaMeasurement::clear(ViewportWindow& vp) {
|
||||
vp.setOverlayLabels({});
|
||||
}
|
||||
|
||||
void AreaMeasurement::rebuildHighlight(ViewportWindow& vp) {
|
||||
void AreaMeasurement::rebuildHighlight(WgpuViewportWindow& vp) {
|
||||
// Push every selected triangle's three world-space vertices to the
|
||||
// overlay. Mesh-local positions × per-instance composed transform.
|
||||
std::vector<float> world_xyz;
|
||||
@@ -287,7 +287,7 @@ void AreaMeasurement::rebuildHighlight(ViewportWindow& vp) {
|
||||
by_object[object_id].push_back(&sel);
|
||||
}
|
||||
|
||||
std::vector<OverlayRenderer::Label> labels;
|
||||
std::vector<WgpuOverlayRenderer::Label> labels;
|
||||
for (const auto& [obj_id, sels] : by_object) {
|
||||
if (sels.empty()) continue;
|
||||
// All tris belonging to one object share its mesh + transform.
|
||||
@@ -350,7 +350,7 @@ void AreaMeasurement::rebuildHighlight(ViewportWindow& vp) {
|
||||
|
||||
// Centroid → world via the instance's composed transform.
|
||||
const float* M = any.composed_transform;
|
||||
OverlayRenderer::Label lbl;
|
||||
WgpuOverlayRenderer::Label lbl;
|
||||
lbl.world_pos[0] = float(M[0]*cx + M[4]*cy + M[8]*cz + M[12]);
|
||||
lbl.world_pos[1] = float(M[1]*cx + M[5]*cy + M[9]*cz + M[13]);
|
||||
lbl.world_pos[2] = float(M[2]*cx + M[6]*cy + M[10]*cz + M[14]);
|
||||
@@ -361,14 +361,14 @@ void AreaMeasurement::rebuildHighlight(ViewportWindow& vp) {
|
||||
vp.setOverlayLabels(labels);
|
||||
}
|
||||
|
||||
AreaMeasurement::MeshCache* AreaMeasurement::meshCache(ViewportWindow& vp,
|
||||
AreaMeasurement::MeshCache* AreaMeasurement::meshCache(WgpuViewportWindow& vp,
|
||||
uint32_t model_id,
|
||||
uint32_t mesh_id) {
|
||||
const uint64_t key = (uint64_t(model_id) << 32) | uint64_t(mesh_id);
|
||||
auto it = mesh_cache_.find(key);
|
||||
if (it != mesh_cache_.end()) return &it->second;
|
||||
|
||||
ViewportWindow::MeshTriangles tris;
|
||||
WgpuViewportWindow::MeshTriangles tris;
|
||||
if (!vp.readbackMeshTriangles(model_id, mesh_id, tris)) return nullptr;
|
||||
|
||||
MeshCache c;
|
||||
@@ -397,8 +397,8 @@ AreaMeasurement::MeshCache* AreaMeasurement::meshCache(ViewportWindow& vp,
|
||||
return &mesh_cache_.emplace(key, std::move(c)).first->second;
|
||||
}
|
||||
|
||||
void AreaMeasurement::onPick(ViewportWindow& vp, int x, int y, bool alt) {
|
||||
ViewportWindow::MeshLocalPick pick;
|
||||
void AreaMeasurement::onPick(WgpuViewportWindow& vp, int x, int y, bool alt) {
|
||||
WgpuViewportWindow::MeshLocalPick pick;
|
||||
if (!vp.pickMeshLocalAt(x, y, pick)) return;
|
||||
|
||||
MeshCache* cache = meshCache(vp, pick.model_id, pick.mesh_id);
|
||||
@@ -607,10 +607,10 @@ constexpr float DOT_HALO = 1.0f;
|
||||
constexpr float DASH_PERIOD = 9.0f; // px
|
||||
constexpr float DASH_ON_RATIO = 0.55f; // 5 on, 4 off
|
||||
|
||||
OverlayRenderer::LineGroup makeGroup(std::vector<float> xyz,
|
||||
WgpuOverlayRenderer::LineGroup makeGroup(std::vector<float> xyz,
|
||||
float r, float g, float b,
|
||||
bool dashed = false) {
|
||||
OverlayRenderer::LineGroup gp;
|
||||
WgpuOverlayRenderer::LineGroup gp;
|
||||
gp.world_xyz = std::move(xyz);
|
||||
gp.color[0] = r; gp.color[1] = g; gp.color[2] = b; gp.color[3] = 1.0f;
|
||||
gp.stroke_color[0] = 0.0f; gp.stroke_color[1] = 0.0f;
|
||||
@@ -635,10 +635,10 @@ void pushSeg(std::vector<float>& xyz,
|
||||
xyz.insert(xyz.end(), b.begin(), b.end());
|
||||
}
|
||||
|
||||
OverlayRenderer::Label makeLabel(const std::array<float, 3>& a,
|
||||
WgpuOverlayRenderer::Label makeLabel(const std::array<float, 3>& a,
|
||||
const std::array<float, 3>& b,
|
||||
const QString& text) {
|
||||
OverlayRenderer::Label lbl;
|
||||
WgpuOverlayRenderer::Label lbl;
|
||||
lbl.world_pos[0] = 0.5f * (a[0] + b[0]);
|
||||
lbl.world_pos[1] = 0.5f * (a[1] + b[1]);
|
||||
lbl.world_pos[2] = 0.5f * (a[2] + b[2]);
|
||||
@@ -646,7 +646,7 @@ OverlayRenderer::Label makeLabel(const std::array<float, 3>& a,
|
||||
return lbl;
|
||||
}
|
||||
|
||||
void pushDots(ViewportWindow& vp, const std::vector<float>& xyz) {
|
||||
void pushDots(WgpuViewportWindow& vp, const std::vector<float>& xyz) {
|
||||
vp.setOverlayPoints(xyz,
|
||||
/*inner*/ 1.0f, 1.0f, 1.0f, 1.0f,
|
||||
/*size*/ DOT_SIZE,
|
||||
@@ -656,7 +656,7 @@ void pushDots(ViewportWindow& vp, const std::vector<float>& xyz) {
|
||||
|
||||
} // namespace
|
||||
|
||||
void LengthMeasurement::clear(ViewportWindow& vp) {
|
||||
void LengthMeasurement::clear(WgpuViewportWindow& vp) {
|
||||
points_.clear();
|
||||
normals_.clear();
|
||||
vp.setOverlayPoints({}, 0,0,0,0, 0, 0,0,0,0, 0);
|
||||
@@ -665,8 +665,8 @@ void LengthMeasurement::clear(ViewportWindow& vp) {
|
||||
vp.setHudText(QString());
|
||||
}
|
||||
|
||||
void LengthMeasurement::onPick(ViewportWindow& vp, int x, int y, bool /*alt*/) {
|
||||
ViewportWindow::MeshLocalPick pick;
|
||||
void LengthMeasurement::onPick(WgpuViewportWindow& vp, int x, int y, bool /*alt*/) {
|
||||
WgpuViewportWindow::MeshLocalPick pick;
|
||||
if (!vp.pickMeshLocalAt(x, y, pick)) return;
|
||||
points_.push_back({pick.world_pos[0], pick.world_pos[1], pick.world_pos[2]});
|
||||
normals_.push_back({pick.world_normal[0], pick.world_normal[1], pick.world_normal[2]});
|
||||
@@ -676,14 +676,14 @@ void LengthMeasurement::onPick(ViewportWindow& vp, int x, int y, bool /*alt*/) {
|
||||
rebuildOverlay(vp);
|
||||
}
|
||||
|
||||
void LengthMeasurement::removeLastPoint(ViewportWindow& vp) {
|
||||
void LengthMeasurement::removeLastPoint(WgpuViewportWindow& vp) {
|
||||
if (points_.empty()) return;
|
||||
points_.pop_back();
|
||||
if (!normals_.empty()) normals_.pop_back();
|
||||
rebuildOverlay(vp);
|
||||
}
|
||||
|
||||
void LengthMeasurement::rebuildOverlay(ViewportWindow& vp) {
|
||||
void LengthMeasurement::rebuildOverlay(WgpuViewportWindow& vp) {
|
||||
if (points_.size() == 1 && normals_.size() == 1) {
|
||||
rebuildLaserOverlay(vp);
|
||||
return;
|
||||
@@ -694,8 +694,8 @@ void LengthMeasurement::rebuildOverlay(ViewportWindow& vp) {
|
||||
for (const auto& p : points_) pushDot(pts_xyz, p);
|
||||
pushDots(vp, pts_xyz);
|
||||
|
||||
std::vector<OverlayRenderer::LineGroup> groups;
|
||||
std::vector<OverlayRenderer::Label> labels;
|
||||
std::vector<WgpuOverlayRenderer::LineGroup> groups;
|
||||
std::vector<WgpuOverlayRenderer::Label> labels;
|
||||
const size_t n = points_.size();
|
||||
|
||||
if (n == 2) {
|
||||
@@ -823,7 +823,7 @@ const char* dominantAxisLabel(const float v[3]) {
|
||||
|
||||
} // namespace
|
||||
|
||||
void LengthMeasurement::rebuildLaserOverlay(ViewportWindow& vp) {
|
||||
void LengthMeasurement::rebuildLaserOverlay(WgpuViewportWindow& vp) {
|
||||
const auto& wp = first_pick_.world_pos; // float[3] world click
|
||||
const auto& n = first_pick_.world_normal; // float[3] world normal
|
||||
|
||||
@@ -855,8 +855,8 @@ void LengthMeasurement::rebuildLaserOverlay(ViewportWindow& vp) {
|
||||
n[0]*t1[1] - n[1]*t1[0],
|
||||
};
|
||||
|
||||
std::vector<OverlayRenderer::LineGroup> groups;
|
||||
std::vector<OverlayRenderer::Label> labels;
|
||||
std::vector<WgpuOverlayRenderer::LineGroup> groups;
|
||||
std::vector<WgpuOverlayRenderer::Label> labels;
|
||||
QStringList hud_lines;
|
||||
hud_lines << QStringLiteral("Laser measure (click another point for distance)");
|
||||
double enh[3] = {0.0, 0.0, 0.0};
|
||||
@@ -873,7 +873,7 @@ void LengthMeasurement::rebuildLaserOverlay(ViewportWindow& vp) {
|
||||
// co-normal neighbours, then project each patch vertex into the
|
||||
// (t1, t2) basis to get the bounding extent of the face. Stops
|
||||
// exactly at the face edge (no overshoot into adjacent geometry).
|
||||
ViewportWindow::MeshTriangles tris;
|
||||
WgpuViewportWindow::MeshTriangles tris;
|
||||
bool have_extent = false;
|
||||
double min_t1 = 0.0, max_t1 = 0.0, min_t2 = 0.0, max_t2 = 0.0;
|
||||
if (vp.readbackMeshTriangles(first_pick_.model_id, first_pick_.mesh_id, tris)) {
|
||||
@@ -1008,7 +1008,7 @@ void LengthMeasurement::rebuildLaserOverlay(ViewportWindow& vp) {
|
||||
wp[1] + NUDGE * n[1],
|
||||
wp[2] + NUDGE * n[2],
|
||||
};
|
||||
ViewportWindow::RaycastHit hit;
|
||||
WgpuViewportWindow::RaycastHit hit;
|
||||
if (vp.raycast(ro, n, hit)) {
|
||||
const double dist = double(hit.distance) + double(NUDGE);
|
||||
const std::array<float, 3> a = {wp[0], wp[1], wp[2]};
|
||||
|
||||
Reference in New Issue
Block a user