mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 10:06:47 +00:00
ifcviewer: move section_planes_ + xray_alpha_cap_ + updateFrameUniforms into ViewportCore (#84-m)
Per-frame uniform packing now lives in ViewportCore::updateFrameUniforms, which reads the camera (via the already-migrated buildViewProj), the section_planes_ vector, and the xray_alpha_cap_ scalar — all of which have moved into ViewportCore alongside frame_uniform_buffer_. ViewportWindow keeps reference-aliases on section_planes_ and xray_alpha_cap_ so the section-tool and X-ray toggle (still Qt-input- bound, still living in VW) keep compiling unchanged. The render-path caller in VW::render now does core_.updateFrameUniforms(). Extracted SectionPlane into its own Qt-free header (SectionPlane.h) so ViewportCore doesn't have to include OverlayRenderer.h's QString / QHash. OverlayRenderer.h re-exports it.
This commit is contained in:
@@ -157,6 +157,7 @@ set(IFCVIEWER_CORE_HEADERS
|
||||
LodBuilder.h
|
||||
Stopwatch.h
|
||||
ModelGpuData.h
|
||||
SectionPlane.h
|
||||
SelectionState.h
|
||||
SidecarCache.h
|
||||
StreamingLoader.h
|
||||
|
||||
@@ -45,17 +45,7 @@ struct OverlayFrame {
|
||||
int device_pixel_ratio = 1;
|
||||
};
|
||||
|
||||
// One section plane as the visualizer consumes it. The viewport owns the
|
||||
// authoritative state vector (the section tool mutates it); the overlay
|
||||
// reads from a non-owning span every frame. Held by value because the
|
||||
// struct is small and copies happen at most six times per frame.
|
||||
struct SectionPlane {
|
||||
Eigen::Vector3f n = Eigen::Vector3f::UnitZ(); // unit normal
|
||||
float d = 0.0f; // -dot(n, origin)
|
||||
Eigen::Vector3f origin = Eigen::Vector3f::Zero(); // surface point at the
|
||||
// moment the plane was added
|
||||
float visual_radius = 0.0f;
|
||||
};
|
||||
#include "SectionPlane.h"
|
||||
|
||||
// All viewport overlays in one place: axis indicator (corner + pivot),
|
||||
// section plane gizmos, and the marquee drag rect. Mirrors GL's
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef IFCVIEWER_SECTIONPLANE_H
|
||||
#define IFCVIEWER_SECTIONPLANE_H
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
// One section plane as the renderer + overlay visualiser consume it. Lives
|
||||
// in its own Qt-free header so ViewportCore can include it without
|
||||
// dragging in OverlayRenderer's QString / QHash dependencies.
|
||||
//
|
||||
// The viewport owns the authoritative state vector (the section tool
|
||||
// mutates it; the per-frame uniform packs it for the WGSL shader; the
|
||||
// overlay reads from a non-owning span every frame). Held by value
|
||||
// because the struct is small and copies happen at most six times per
|
||||
// frame (kMaxSectionPlanes).
|
||||
struct SectionPlane {
|
||||
Eigen::Vector3f n = Eigen::Vector3f::UnitZ(); // unit normal
|
||||
float d = 0.0f; // -dot(n, origin)
|
||||
Eigen::Vector3f origin = Eigen::Vector3f::Zero(); // surface point at the
|
||||
// moment the plane was added
|
||||
float visual_radius = 0.0f;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1121,6 +1121,44 @@ void ViewportCore::uploadSelectionFlagsIfDirty() {
|
||||
selection_.markClean();
|
||||
}
|
||||
|
||||
void ViewportCore::updateFrameUniforms() {
|
||||
Eigen::Matrix4f view, proj;
|
||||
buildViewProj(view, proj);
|
||||
|
||||
const Eigen::Matrix4f view_proj = proj * view;
|
||||
|
||||
FrameUniforms u = {};
|
||||
std::memcpy(u.view_proj, view_proj.data(), 16 * sizeof(float));
|
||||
|
||||
// Values match the GL viewport's main fragment shader so a side-by-side
|
||||
// diff of the two backends only shows what the wgpu pipeline has yet to
|
||||
// implement (edge silhouette pass, MSAA polish, etc.) — not lighting
|
||||
// model differences. Key + fill are ~unit-length, ~120° apart.
|
||||
Eigen::Vector3f L( 0.3f, 0.5f, 0.8f); L.normalize();
|
||||
Eigen::Vector3f F(-0.3f, -0.5f, 0.8f); F.normalize();
|
||||
u.light_dir[0] = L.x(); u.light_dir[1] = L.y(); u.light_dir[2] = L.z(); u.light_dir[3] = 0;
|
||||
u.fill_dir [0] = F.x(); u.fill_dir [1] = F.y(); u.fill_dir [2] = F.z(); u.fill_dir [3] = 0;
|
||||
u.sky_color [0] = 0.55f; u.sky_color [1] = 0.60f; u.sky_color [2] = 0.70f;
|
||||
u.ground_color[0] = 0.35f; u.ground_color[1] = 0.32f; u.ground_color[2] = 0.28f;
|
||||
|
||||
// Pack active section planes. `is_section_clipped` (WGSL) reads
|
||||
// u.clip_count and u.clip_planes[0..clip_count) and discards
|
||||
// fragments on the positive side.
|
||||
const int n = std::min<int>(int(section_planes_.size()), kMaxSectionPlanes);
|
||||
u.clip_count = n;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
const SectionPlane& p = section_planes_[i];
|
||||
u.clip_planes[i][0] = p.n.x();
|
||||
u.clip_planes[i][1] = p.n.y();
|
||||
u.clip_planes[i][2] = p.n.z();
|
||||
u.clip_planes[i][3] = p.d;
|
||||
}
|
||||
u.xray_alpha_cap = xray_alpha_cap_;
|
||||
u._pad_xray[0] = u._pad_xray[1] = u._pad_xray[2] = 0.0f;
|
||||
|
||||
wgpuQueueWriteBuffer(queue_, frame_uniform_buffer_, 0, &u, sizeof(u));
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Lifecycle (#84-l): initWgpu + probeAndCreatePool + shutdown
|
||||
// ===========================================================================
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "InstanceCompose.h"
|
||||
#include "InstancedGeometry.h"
|
||||
#include "ModelGpuData.h"
|
||||
#include "SectionPlane.h"
|
||||
#include "SelectionState.h"
|
||||
#include "StreamingThread.h"
|
||||
#include "ViewportHost.h"
|
||||
@@ -223,6 +224,12 @@ public:
|
||||
void ensureSelectionFlagsBuffer();
|
||||
void uploadSelectionFlagsIfDirty();
|
||||
|
||||
// Build the FrameUniforms struct (view-proj + lighting + section
|
||||
// planes + xray cap) from current camera + section_planes_ +
|
||||
// xray_alpha_cap_ and upload it via the queue. Called once per
|
||||
// render() at frame start, before any draw encode.
|
||||
void updateFrameUniforms();
|
||||
|
||||
// ---- wgpu lifecycle ----------------------------------------------------
|
||||
//
|
||||
// initWgpu brings up the wgpu instance, gets the platform surface
|
||||
@@ -320,6 +327,20 @@ private:
|
||||
uint32_t selection_flags_capacity_ = 0; // u32 entries
|
||||
std::vector<uint32_t> selection_flags_scratch_;
|
||||
|
||||
// Active world-space section planes (up to kMaxSectionPlanes); packed
|
||||
// into the per-frame uniform every render and consumed by the WGSL
|
||||
// is_section_clipped fragment gate. The section tool in
|
||||
// ViewportWindow mutates this through addSectionPlaneAtSurface /
|
||||
// removeSectionPlane (still Qt-bound — they wire into the input
|
||||
// path). Reading happens here.
|
||||
std::vector<SectionPlane> section_planes_;
|
||||
|
||||
// X-ray mode alpha clamp: when < 1.0 every instance routes through
|
||||
// the transparent pass with fragment.a clamped to min(in.color.a, cap).
|
||||
// Toggled by ViewportWindow::toggleXray; consumed by cull
|
||||
// (transparent-pass classifier) and updateFrameUniforms.
|
||||
float xray_alpha_cap_ = 1.0f;
|
||||
|
||||
// Selection + per-element visibility state machines. Pure CPU
|
||||
// bookkeeping today (no GPU touch beyond the readback uploaded via
|
||||
// selection_flags_buffer_). Mutated on the main thread between
|
||||
|
||||
@@ -271,6 +271,8 @@ ViewportWindow::ViewportWindow(QWindow* parent)
|
||||
selection_flags_buffer_ (core_.selection_flags_buffer_),
|
||||
selection_flags_capacity_(core_.selection_flags_capacity_),
|
||||
selection_flags_scratch_ (core_.selection_flags_scratch_),
|
||||
section_planes_ (core_.section_planes_),
|
||||
xray_alpha_cap_ (core_.xray_alpha_cap_),
|
||||
selection_ (core_.selection_),
|
||||
visibility_ (core_.visibility_) {
|
||||
// wgpu doesn't need a GL context; we just need a real native window
|
||||
@@ -3941,7 +3943,7 @@ void ViewportWindow::render() {
|
||||
|
||||
WGPUTextureView view = wgpuTextureCreateView(surf_tex.texture, nullptr);
|
||||
|
||||
updateFrameUniforms();
|
||||
core_.updateFrameUniforms();
|
||||
|
||||
// Per-frame cull: extract frustum planes from the same VP we just wrote
|
||||
// into the uniform, then run cullModelCpu on every visible model. The
|
||||
@@ -5817,43 +5819,7 @@ static Eigen::Vector3f orbitEye(const float target[3], float dist,
|
||||
// identically everywhere.
|
||||
// buildViewProj moved to ViewportCore (#84-h).
|
||||
|
||||
void ViewportWindow::updateFrameUniforms() {
|
||||
Eigen::Matrix4f view, proj;
|
||||
core_.buildViewProj(view, proj);
|
||||
|
||||
const Eigen::Matrix4f view_proj = proj * view;
|
||||
|
||||
FrameUniforms u = {};
|
||||
std::memcpy(u.view_proj, view_proj.data(), 16 * sizeof(float));
|
||||
|
||||
// Values match the GL viewport's main fragment shader so a side-by-side
|
||||
// diff of the two backends only shows what the wgpu pipeline has yet to
|
||||
// implement (edge silhouette pass, MSAA polish, etc.) — not lighting
|
||||
// model differences. Key + fill are ~unit-length, ~120° apart.
|
||||
Eigen::Vector3f L( 0.3f, 0.5f, 0.8f); L.normalize();
|
||||
Eigen::Vector3f F(-0.3f, -0.5f, 0.8f); F.normalize();
|
||||
u.light_dir[0] = L.x(); u.light_dir[1] = L.y(); u.light_dir[2] = L.z(); u.light_dir[3] = 0;
|
||||
u.fill_dir [0] = F.x(); u.fill_dir [1] = F.y(); u.fill_dir [2] = F.z(); u.fill_dir [3] = 0;
|
||||
u.sky_color [0] = 0.55f; u.sky_color [1] = 0.60f; u.sky_color [2] = 0.70f;
|
||||
u.ground_color[0] = 0.35f; u.ground_color[1] = 0.32f; u.ground_color[2] = 0.28f;
|
||||
|
||||
// Pack active section planes. `is_section_clipped` (WGSL) reads
|
||||
// u.clip_count and u.clip_planes[0..clip_count) and discards
|
||||
// fragments on the positive side.
|
||||
const int n = std::min<int>(int(section_planes_.size()), kMaxSectionPlanes);
|
||||
u.clip_count = n;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
const SectionPlane& p = section_planes_[i];
|
||||
u.clip_planes[i][0] = p.n.x();
|
||||
u.clip_planes[i][1] = p.n.y();
|
||||
u.clip_planes[i][2] = p.n.z();
|
||||
u.clip_planes[i][3] = p.d;
|
||||
}
|
||||
u.xray_alpha_cap = xray_alpha_cap_;
|
||||
u._pad_xray[0] = u._pad_xray[1] = u._pad_xray[2] = 0.0f;
|
||||
|
||||
wgpuQueueWriteBuffer(queue_, frame_uniform_buffer_, 0, &u, sizeof(u));
|
||||
}
|
||||
// updateFrameUniforms moved to ViewportCore (#84-m).
|
||||
|
||||
// computeSceneAabb moved to ViewportCore (#84-h).
|
||||
|
||||
|
||||
@@ -557,7 +557,6 @@ private:
|
||||
// any projection is unreliable. True (cull) when AABB is provably
|
||||
// behind every relevant pyramid cell.
|
||||
bool aabbOccludedByHiz(const float mn[3], const float mx[3]) const;
|
||||
void updateFrameUniforms();
|
||||
void flushPendingSidecarQueue();
|
||||
// computeSceneAabb moved to ViewportCore (#84-h).
|
||||
|
||||
@@ -777,20 +776,22 @@ private:
|
||||
int pick_w_ = 0;
|
||||
int pick_h_ = 0;
|
||||
|
||||
// Section-cutting state. SectionPlane lives in OverlayRenderer.h
|
||||
// because the visualiser reads it; the viewport owns the authoritative
|
||||
// vector that the section tool mutates.
|
||||
std::vector<SectionPlane> section_planes_;
|
||||
// Section-cutting state aliases (storage in core_). The section tool
|
||||
// mutates section_planes_ through addSectionPlaneAtSurface /
|
||||
// removeSectionPlane; the per-frame uniform packs the same vector
|
||||
// for the WGSL fragment-side clip gate.
|
||||
std::vector<SectionPlane>& section_planes_;
|
||||
bool section_tool_active_ = false;
|
||||
|
||||
// X-ray mode. Default 1.0 = no effect (fragment shader clamps
|
||||
// alpha = min(in.color.a, xray_alpha_cap_), which returns in.color.a
|
||||
// when the cap is 1). Alt+X drops it to 0.3 to translucent the whole
|
||||
// scene; pressing again restores 1.0. When < 1.0, the cull
|
||||
// classifier also routes every instance into the transparent pass
|
||||
// so the blend stage actually fires (an opaque-pass fragment with
|
||||
// capped alpha would still overwrite the back buffer).
|
||||
float xray_alpha_cap_ = 1.0f;
|
||||
// X-ray cap alias (storage in core_). Default 1.0 = no effect
|
||||
// (fragment shader clamps alpha = min(in.color.a, xray_alpha_cap_),
|
||||
// which returns in.color.a when the cap is 1). Alt+X drops it to
|
||||
// 0.3 to translucent the whole scene; pressing again restores 1.0.
|
||||
// When < 1.0, the cull classifier (also in core) routes every
|
||||
// instance into the transparent pass so the blend stage actually
|
||||
// fires (an opaque-pass fragment with capped alpha would still
|
||||
// overwrite the back buffer).
|
||||
float& xray_alpha_cap_;
|
||||
|
||||
// Marquee box-select. Armed on LMB press (when no other tool consumes
|
||||
// the click), becomes active after the cursor moves past
|
||||
|
||||
Reference in New Issue
Block a user