mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
wgpu: extract overlays (axis, pivot, section, marquee) into WgpuOverlayRenderer
WgpuViewportWindow.cpp had ~1100 lines of pipeline/shader/buffer plumbing for the axis indicator, pivot gizmo, section visualizer, and marquee drag rect. Mirroring the GL backend's split, that lives in its own class now; the viewport keeps the camera/cull/draw loop and hands the renderer a per-frame WgpuOverlayFrame snapshot for each encode call. No behavioural change — pixel-identical screenshot on basic.ifcview. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,166 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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 WGPUOVERLAYRENDERER_H
|
||||
#define WGPUOVERLAYRENDERER_H
|
||||
|
||||
#include <QMatrix4x4>
|
||||
#include <QPoint>
|
||||
#include <QVector3D>
|
||||
|
||||
#include <webgpu/webgpu.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
// Per-frame snapshot of viewport state that every overlay needs. Built once
|
||||
// at the top of render() and passed by const-ref to each encodeX() call so
|
||||
// the overlay renderer never reaches back into the viewport.
|
||||
struct WgpuOverlayFrame {
|
||||
QMatrix4x4 view_proj;
|
||||
QVector3D camera_target;
|
||||
float camera_distance = 5.0f;
|
||||
float camera_yaw_deg = 0.0f;
|
||||
float camera_pitch_deg = 0.0f;
|
||||
float camera_fov_y_deg = 45.0f;
|
||||
int viewport_w_px = 0;
|
||||
int viewport_h_px = 0;
|
||||
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 WgpuSectionPlane {
|
||||
QVector3D n; // unit normal (camera-facing after auto-flip)
|
||||
float d; // -dot(n, origin)
|
||||
QVector3D origin; // surface point at the moment the plane was added
|
||||
float visual_radius; // unused by the visualizer (kept here so the
|
||||
// section tool's state struct round-trips
|
||||
// through this overlay-facing definition).
|
||||
};
|
||||
|
||||
// All viewport overlays in one place: axis indicator (corner + pivot),
|
||||
// section plane gizmos, and the marquee drag rect. Mirrors GL's
|
||||
// OverlayRenderer split so WgpuViewportWindow.cpp doesn't have to
|
||||
// carry ~1.5k lines of pipeline plumbing.
|
||||
//
|
||||
// Lifecycle: init() once after the device is up, destroy() before the
|
||||
// device dies. Pipelines are immutable after init; only per-frame
|
||||
// uniforms get re-written.
|
||||
//
|
||||
// Threading: all calls are main-thread only — they touch the wgpu queue.
|
||||
class WgpuOverlayRenderer {
|
||||
public:
|
||||
WgpuOverlayRenderer() = default;
|
||||
~WgpuOverlayRenderer();
|
||||
|
||||
WgpuOverlayRenderer(const WgpuOverlayRenderer&) = delete;
|
||||
WgpuOverlayRenderer& operator=(const WgpuOverlayRenderer&) = delete;
|
||||
|
||||
bool init(WGPUInstance instance, WGPUDevice device, WGPUQueue queue,
|
||||
WGPUTextureFormat surface_format, int sample_count);
|
||||
void destroy();
|
||||
|
||||
// ---- Inside the main MSAA pass, after geometry ----
|
||||
// Both share depth with the scene so they're correctly occluded.
|
||||
|
||||
// Orbit pivot indicator. `visible` is the viewport's UI gate (orbit
|
||||
// drag / wheel-zoom afterglow). When false this is a cheap no-op.
|
||||
void encodePivot(WGPURenderPassEncoder pass,
|
||||
const WgpuOverlayFrame& f,
|
||||
bool visible);
|
||||
|
||||
// Per-plane wireframe gizmo (2 × 2 m quad outline + arrow shaft +
|
||||
// arrow head). Drawn at each plane's origin in its local basis;
|
||||
// colour comes from Bonsai's decorator_color_error.
|
||||
void encodeSectionGizmos(WGPURenderPassEncoder pass,
|
||||
const WgpuOverlayFrame& f,
|
||||
const std::vector<WgpuSectionPlane>& planes);
|
||||
|
||||
// ---- After the edge silhouette pass, on the resolved surface ----
|
||||
|
||||
// Corner axis gizmo (bottom-left, 110×110 px). Independent ortho
|
||||
// projection — only the camera direction matters.
|
||||
void encodeCornerAxis(WGPUCommandEncoder enc,
|
||||
WGPUTextureView surface_view,
|
||||
const WgpuOverlayFrame& f);
|
||||
|
||||
// Marquee box-select drag rect (translucent fill + thick outline).
|
||||
// No-op when `active` is false.
|
||||
void encodeMarquee(WGPUCommandEncoder enc,
|
||||
WGPUTextureView surface_view,
|
||||
const WgpuOverlayFrame& f,
|
||||
QPoint start_logical_px,
|
||||
QPoint current_logical_px,
|
||||
bool active);
|
||||
|
||||
// Shared with the viewport's main FrameUniforms: same cap so the
|
||||
// viewport's clip-plane array and the gizmo visualiser agree on
|
||||
// how many planes can ever be active.
|
||||
static constexpr int kMaxSectionPlanes = 6;
|
||||
|
||||
private:
|
||||
bool buildAxisIndicator();
|
||||
bool buildSectionVisualizer();
|
||||
bool buildMarquee();
|
||||
|
||||
WGPUInstance instance_ = nullptr;
|
||||
WGPUDevice device_ = nullptr;
|
||||
WGPUQueue queue_ = nullptr;
|
||||
WGPUTextureFormat surface_format_ = WGPUTextureFormat_Undefined;
|
||||
int sample_count_ = 1;
|
||||
|
||||
// ---- Axis indicator (shared shape, three pipelines) ----
|
||||
// Slot 0 = corner gizmo. Slots 1/2 = pivot visible/x-ray.
|
||||
WGPUShaderModule axis_shader_module_ = nullptr;
|
||||
WGPUBindGroupLayout axis_bgl_ = nullptr;
|
||||
WGPUPipelineLayout axis_pipeline_layout_ = nullptr;
|
||||
WGPURenderPipeline axis_pivot_pipeline_ = nullptr;
|
||||
WGPURenderPipeline axis_pivot_xray_pipeline_ = nullptr;
|
||||
WGPURenderPipeline axis_corner_pipeline_ = nullptr;
|
||||
WGPUBuffer axis_vertex_buffer_ = nullptr;
|
||||
WGPUBuffer axis_uniform_buffer_ = nullptr;
|
||||
WGPUBindGroup axis_bind_group_ = nullptr;
|
||||
static constexpr uint32_t kAxisUniformSlotSize = 256;
|
||||
|
||||
// ---- Section plane gizmos (1 pipeline, dynamic offset per plane) ----
|
||||
WGPUShaderModule section_shader_module_ = nullptr;
|
||||
WGPUBindGroupLayout section_bgl_ = nullptr;
|
||||
WGPUPipelineLayout section_pipeline_layout_ = nullptr;
|
||||
WGPURenderPipeline section_pipeline_ = nullptr;
|
||||
WGPUBuffer section_vertex_buffer_ = nullptr;
|
||||
WGPUBuffer section_uniform_buffer_ = nullptr;
|
||||
WGPUBindGroup section_bind_group_ = nullptr;
|
||||
static constexpr uint32_t kSectionUniformSlotSize = 256;
|
||||
|
||||
// ---- Marquee (fill + outline pipelines, one uniform buffer) ----
|
||||
WGPUShaderModule marquee_shader_module_ = nullptr;
|
||||
WGPUBindGroupLayout marquee_bgl_ = nullptr;
|
||||
WGPUPipelineLayout marquee_pipeline_layout_ = nullptr;
|
||||
WGPURenderPipeline marquee_pipeline_ = nullptr;
|
||||
WGPURenderPipeline marquee_fill_pipeline_ = nullptr;
|
||||
WGPUBuffer marquee_vertex_buffer_ = nullptr;
|
||||
WGPUBuffer marquee_fill_vertex_buffer_ = nullptr;
|
||||
WGPUBuffer marquee_uniform_buffer_ = nullptr;
|
||||
WGPUBindGroup marquee_bind_group_ = nullptr;
|
||||
};
|
||||
|
||||
#endif // WGPUOVERLAYRENDERER_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,6 +40,7 @@
|
||||
#include "SidecarCache.h"
|
||||
#include "WgpuBufferPool.h"
|
||||
#include "WgpuModelGpuData.h"
|
||||
#include "WgpuOverlayRenderer.h"
|
||||
#include "WgpuSelectionState.h"
|
||||
#include "WgpuStreamingThread.h"
|
||||
#include "WgpuVisibilityState.h"
|
||||
@@ -220,18 +221,11 @@ private:
|
||||
bool buildHizPipeline();
|
||||
bool buildEdgePipeline();
|
||||
void encodeEdgePass(WGPUCommandEncoder enc, WGPUTextureView surface_view);
|
||||
bool buildAxisIndicator();
|
||||
// Encode the pivot indicator inside the main MSAA render pass — runs
|
||||
// after geometry so depth interaction is correct.
|
||||
void encodePivotIndicator(WGPURenderPassEncoder pass,
|
||||
const QMatrix4x4& view_proj);
|
||||
// Encode the corner gizmo on the resolved single-sample surface,
|
||||
// after the edge silhouette pass. Uses setViewport for the corner box.
|
||||
void encodeCornerAxisGizmo(WGPUCommandEncoder enc,
|
||||
WGPUTextureView surface_view);
|
||||
// Show/hide the pivot indicator. hide_after_ms > 0 starts the
|
||||
// single-shot auto-hide timer used by the wheel-zoom afterglow;
|
||||
// drag callers pass 0 and toggle manually on press/release.
|
||||
// drag callers pass 0 and toggle manually on press/release. The
|
||||
// actual gizmo rendering lives in WgpuOverlayRenderer — this just
|
||||
// manages the UI-side visibility timer.
|
||||
void setPivotIndicatorVisible(bool visible, int hide_after_ms = 0);
|
||||
void releaseEdgeResources();
|
||||
|
||||
@@ -450,46 +444,17 @@ private:
|
||||
WGPUBindGroup edge_bind_group_ = nullptr;
|
||||
bool edges_enabled_ = true;
|
||||
|
||||
// Axis indicator overlay — drives both the corner gizmo and the
|
||||
// orbit pivot indicator from the SAME 6-vertex unit cross + shader.
|
||||
// One uniform buffer with three 256-byte-aligned slots
|
||||
// (slot 0 = corner, slot 1 = pivot visible, slot 2 = pivot x-ray),
|
||||
// one bind group with a dynamic offset, three pipelines that differ
|
||||
// only in render-target / depth setup:
|
||||
// axis_pivot_pipeline_: MSAA + depth LessEqual (visible α=1)
|
||||
// axis_pivot_xray_pipeline_: MSAA + depth GreaterEqual (occluded α≈0.3)
|
||||
// axis_corner_pipeline_: resolved, no depth, sampleCount=1
|
||||
// The pivot's x-ray pass renders first so the visible pass overdraws
|
||||
// it where geometry isn't in the way. Matches GL's renderPivotIndicator.
|
||||
WGPUShaderModule axis_shader_module_ = nullptr;
|
||||
WGPUBindGroupLayout axis_bgl_ = nullptr;
|
||||
WGPUPipelineLayout axis_pipeline_layout_ = nullptr;
|
||||
WGPURenderPipeline axis_pivot_pipeline_ = nullptr;
|
||||
WGPURenderPipeline axis_pivot_xray_pipeline_ = nullptr;
|
||||
WGPURenderPipeline axis_corner_pipeline_ = nullptr;
|
||||
// Marquee overlay (drag-rect for box-select). Renders on the resolved
|
||||
// surface after the corner gizmo. Uses a 4-segment unit-quad VBO; the
|
||||
// shader maps unit coords to NDC via per-frame uniform (rect min/max
|
||||
// in NDC + colour + viewport + line width).
|
||||
WGPUShaderModule marquee_shader_module_ = nullptr;
|
||||
WGPUBindGroupLayout marquee_bgl_ = nullptr;
|
||||
WGPUPipelineLayout marquee_pipeline_layout_ = nullptr;
|
||||
WGPURenderPipeline marquee_pipeline_ = nullptr; // outline (thick line + AA)
|
||||
WGPURenderPipeline marquee_fill_pipeline_ = nullptr; // translucent quad fill
|
||||
WGPUBuffer marquee_vertex_buffer_ = nullptr; // outline geometry
|
||||
WGPUBuffer marquee_fill_vertex_buffer_ = nullptr; // 6-vert quad
|
||||
WGPUBuffer marquee_uniform_buffer_ = nullptr;
|
||||
WGPUBindGroup marquee_bind_group_ = nullptr;
|
||||
bool buildMarquee();
|
||||
void encodeMarquee(WGPUCommandEncoder enc, WGPUTextureView surface_view);
|
||||
void releaseMarquee();
|
||||
WGPUBuffer axis_vertex_buffer_ = nullptr; // 6 × 24 B
|
||||
WGPUBuffer axis_uniform_buffer_ = nullptr; // 3 × 256 B slots
|
||||
WGPUBindGroup axis_bind_group_ = nullptr; // dynamic-offset
|
||||
static constexpr uint32_t kAxisUniformSlotSize = 256;
|
||||
// Pivot visibility state — the gizmo itself lives in overlays_.
|
||||
// The timer auto-hides the pivot after a wheel-zoom afterglow.
|
||||
bool pivot_indicator_visible_ = false;
|
||||
QTimer* pivot_indicator_hide_timer_ = nullptr;
|
||||
|
||||
// All viewport overlays (axis indicator, section gizmos, marquee
|
||||
// rect) — pipelines + shaders + buffers + encoders. The viewport
|
||||
// builds a WgpuOverlayFrame each frame and asks the renderer to
|
||||
// encode each overlay; see WgpuOverlayRenderer.h.
|
||||
WgpuOverlayRenderer overlays_;
|
||||
|
||||
// Pick pass (stage 4). Single-sample R32UInt target + depth, vertex-
|
||||
// pulled from the same visible_draws / instances buffers as the main
|
||||
// pass — pick fragment outputs the instance's object_id. The pick
|
||||
@@ -510,18 +475,11 @@ private:
|
||||
int pick_w_ = 0;
|
||||
int pick_h_ = 0;
|
||||
|
||||
// Section-cutting state. Each plane is (n, d) with normal n in world
|
||||
// space and signed distance d = -dot(n, point_on_plane); a point P is
|
||||
// on the kept side iff dot(n, P) + d <= 0. The vector mirrors GL's
|
||||
// ViewportWindow::section_planes_.
|
||||
struct SectionPlane {
|
||||
QVector3D n; // unit normal
|
||||
float d; // -dot(n, origin)
|
||||
QVector3D origin; // surface point at the moment the plane was added
|
||||
float visual_radius; // half-extent for the gizmo quad (set from the picked instance's AABB diagonal)
|
||||
};
|
||||
std::vector<SectionPlane> section_planes_;
|
||||
bool section_tool_active_ = false;
|
||||
// Section-cutting state. WgpuSectionPlane lives in WgpuOverlayRenderer.h
|
||||
// because the visualiser reads it; the viewport owns the authoritative
|
||||
// vector that the section tool mutates.
|
||||
std::vector<WgpuSectionPlane> section_planes_;
|
||||
bool section_tool_active_ = false;
|
||||
|
||||
// Marquee box-select. Armed on LMB press (when no other tool consumes
|
||||
// the click), becomes active after the cursor moves past
|
||||
@@ -553,25 +511,6 @@ private:
|
||||
// along that direction.
|
||||
void updateSectionDrag(int x, int y);
|
||||
|
||||
// Section plane visualisation. Renders one translucent quad per active
|
||||
// plane, sized to the scene AABB so the cut is visible at any zoom.
|
||||
// Built once (unit quad in plane-local space), oriented per-plane in
|
||||
// the vertex shader from u_origin + tangent/bitangent (derived from
|
||||
// the plane normal). Two passes: a back-facing fill (alpha 0.18) for
|
||||
// the "behind geometry" hint plus a front-facing fill (alpha 0.35).
|
||||
WGPUShaderModule section_shader_module_ = nullptr;
|
||||
WGPUBindGroupLayout section_bgl_ = nullptr;
|
||||
WGPUPipelineLayout section_pipeline_layout_ = nullptr;
|
||||
WGPURenderPipeline section_pipeline_ = nullptr;
|
||||
WGPUBuffer section_vertex_buffer_ = nullptr;
|
||||
WGPUBuffer section_uniform_buffer_ = nullptr;
|
||||
WGPUBindGroup section_bind_group_ = nullptr;
|
||||
static constexpr uint32_t kSectionUniformSlotSize = 256;
|
||||
bool buildSectionVisualizer();
|
||||
void encodeSectionPlanes(WGPURenderPassEncoder pass,
|
||||
const QMatrix4x4& view_proj);
|
||||
void releaseSectionVisualizer();
|
||||
|
||||
enum class HizSlotState : uint8_t { Idle, Mapping, Mapped };
|
||||
static constexpr int HIZ_SLOTS = 2;
|
||||
WGPUBuffer hiz_staging_buffers_[HIZ_SLOTS] = { nullptr, nullptr };
|
||||
|
||||
Reference in New Issue
Block a user