mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-30 00:23:14 +00:00
wgpu: corner axis gizmo + orbit pivot indicator (shared geometry)
Both overlays draw the same three positive-axis rays (origin → +X / +Y
/ +Z) — one screen-space-thick-line shader, one vertex buffer, one
bind group layout. The vertex stage transforms each vertex as
`mvp * (origin + position * arm)` so the same primitive serves both
modes:
corner: viewport set to a 110×110 px box in the bottom-left,
camera-orientation ortho MVP, origin=0, arm=1.
pivot : full viewport, main view-proj, origin=camera_target,
arm = 30 logical px in world units.
Pipelines
axis_pivot_pipeline_ — MSAA + depth LessEqual (α=1)
axis_pivot_xray_pipeline_ — MSAA + depth GreaterEqual (α=0.30)
axis_corner_pipeline_ — resolved surface, no depth, sampleCount=1
Pivot renders inside the main MSAA pass after geometry (depth
interaction); corner renders on the resolved surface after the edge
silhouette pass so the laplacian can't darken its lines. The pivot's
two passes — x-ray first then visible — give an occluded-side hint
matching GL's renderPivotIndicator.
Screen-space thick lines
WebGPU has no lineWidth, so each axis is a 2-triangle quad expanded
by `line_width / 2` pixels along the screen-space perpendicular in
the vertex shader. Every vertex carries BOTH endpoints (start, end)
plus `t ∈ {0,1}` and `side ∈ {-1,+1}` so the direction is computed
consistently as `s_end - s_start` regardless of which end the vertex
sits at — an earlier "this vertex vs the other end" formulation
flipped sign at the end vertex and produced a bowtie.
Analytical AA
|side_t| ∈ [0,1] is the perpendicular distance from the line centre.
`smoothstep(1-fwidth, 1, |side_t|)` gives a 1-pixel coverage falloff
at the long edges — gizmos read cleanly even on the resolved-surface
corner pass which has no MSAA.
Pivot visibility
- orbit / pan drag press → on, release → off
- wheel zoom → on with 600 ms afterglow via QTimer
Pole fallback for the corner gizmo's lookAt mirrors buildViewProj's
identical fix (swap Y-up when |pitch| ≥ 89°), so top/bottom standard
views don't degenerate.
Tracked under task #59.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
#include <QPoint>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
|
||||
#include <webgpu/webgpu.h>
|
||||
|
||||
@@ -218,6 +219,19 @@ 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.
|
||||
void setPivotIndicatorVisible(bool visible, int hide_after_ms = 0);
|
||||
void releaseEdgeResources();
|
||||
|
||||
bool buildPickPipeline();
|
||||
@@ -399,6 +413,30 @@ 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;
|
||||
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;
|
||||
bool pivot_indicator_visible_ = false;
|
||||
QTimer* pivot_indicator_hide_timer_ = nullptr;
|
||||
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user