mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-10-03 13:28:19 +00:00
ifcviewer: de-Qt QColor/QPoint/QSet/QElapsedTimer in ViewportWindow
Last round of straight-swap Qt value types in ViewportWindow + its
overlay co-pilot.
setBackgroundColor(const QColor&) → (float r, float g, float b, float a)
QColor background_color_ → Eigen::Vector4f (linear, 0..1)
QPoint {nav_,box_select_,fps_, } → Eigen::Vector2i
{section_drag_start_mouse_}
QSet<int> fps_keys_held_ → std::unordered_set<int>
QElapsedTimer fps_last_tick_, → Stopwatch (new header in
fly_render_clock_, IfcViewerCore — std::chrono-
render_thread_local_ backed, exposes the existing
timers in render() QElapsedTimer .start/.restart/
.elapsed/.nsecsElapsed surface)
Also propagates the QPoint → Eigen::Vector2i change through
OverlayRenderer::encodeMarquee since the marquee corner coords flow
through that interface.
API-level helpers:
toV2i(QPoint) — small inline in ViewportWindow.cpp, isolates
the QMouseEvent→Vector2i conversion at the
five mouse-event handlers
Stopwatch.h — new file, IfcViewerCore. Same call shape as
QElapsedTimer; backed by std::chrono::steady_clock.
QSet method swaps:
.isEmpty() → .empty()
.contains(k) → .count(k) (C++17, no std contains() until C++20)
.remove(k) → .erase(k)
Eigen::Vector2i doesn't have .manhattanLength(); the box-select drag
threshold uses std::abs(diff.x()) + std::abs(diff.y()) inline.
Bonsai side: View.cpp's setBackgroundColor wrapper now decomposes the
QColor into floats at the call site (kept locally so the bonsai UI
keeps its QColor-driven theming).
Closes #81 + the QElapsedTimer half of #83. QTimer
(pivot_indicator_hide_timer_) still uses Qt — it needs the host's
scheduleOnce mechanism that lands with #85.
Builds: desktop / bonsai / web all green. Tests 100/100.
This commit is contained in:
@@ -45,11 +45,14 @@ ViewportView::ViewportView(bonsaiviewer::SessionState* session_state,
|
|||||||
, length_measurement_(std::make_unique<LengthMeasurement>())
|
, length_measurement_(std::make_unique<LengthMeasurement>())
|
||||||
{
|
{
|
||||||
auto& settings = bonsaiviewer::ViewerSettings::instance();
|
auto& settings = bonsaiviewer::ViewerSettings::instance();
|
||||||
connect(&settings, &bonsaiviewer::ViewerSettings::themeChanged, this, [this]() {
|
auto setBg = [this](const QString& name) {
|
||||||
viewport_->setBackgroundColor(
|
const QColor c(bonsaiviewer::ViewerSettings::instance().color(name));
|
||||||
QColor(bonsaiviewer::ViewerSettings::instance().color("viewport_background")));
|
viewport_->setBackgroundColor(float(c.redF()), float(c.greenF()),
|
||||||
});
|
float(c.blueF()), float(c.alphaF()));
|
||||||
viewport_->setBackgroundColor(QColor(settings.color("viewport_background")));
|
};
|
||||||
|
connect(&settings, &bonsaiviewer::ViewerSettings::themeChanged, this,
|
||||||
|
[setBg]() { setBg("viewport_background"); });
|
||||||
|
setBg("viewport_background");
|
||||||
|
|
||||||
connect(session_state_, &SessionState::projectReset, this, &ViewportView::refresh);
|
connect(session_state_, &SessionState::projectReset, this, &ViewportView::refresh);
|
||||||
connect(session_state_, &SessionState::projectOpened, this, [this](const QString&) { refresh(); });
|
connect(session_state_, &SessionState::projectOpened, this, [this](const QString&) { refresh(); });
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ set(IFCVIEWER_CORE_HEADERS
|
|||||||
InstancedGeometry.h
|
InstancedGeometry.h
|
||||||
Log.h
|
Log.h
|
||||||
LodBuilder.h
|
LodBuilder.h
|
||||||
|
Stopwatch.h
|
||||||
ModelGpuData.h
|
ModelGpuData.h
|
||||||
SelectionState.h
|
SelectionState.h
|
||||||
SidecarCache.h
|
SidecarCache.h
|
||||||
|
|||||||
@@ -1186,8 +1186,8 @@ bool OverlayRenderer::buildMarquee() {
|
|||||||
void OverlayRenderer::encodeMarquee(WGPUCommandEncoder enc,
|
void OverlayRenderer::encodeMarquee(WGPUCommandEncoder enc,
|
||||||
WGPUTextureView surface_view,
|
WGPUTextureView surface_view,
|
||||||
const OverlayFrame& f,
|
const OverlayFrame& f,
|
||||||
QPoint start_logical_px,
|
Eigen::Vector2i start_logical_px,
|
||||||
QPoint current_logical_px,
|
Eigen::Vector2i current_logical_px,
|
||||||
bool active) {
|
bool active) {
|
||||||
if (!marquee_pipeline_ || !surface_view) return;
|
if (!marquee_pipeline_ || !surface_view) return;
|
||||||
if (!active) return;
|
if (!active) return;
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
#define WGPUOVERLAYRENDERER_H
|
#define WGPUOVERLAYRENDERER_H
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QPoint>
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include <Eigen/Dense>
|
#include <Eigen/Dense>
|
||||||
@@ -189,8 +188,8 @@ public:
|
|||||||
void encodeMarquee(WGPUCommandEncoder enc,
|
void encodeMarquee(WGPUCommandEncoder enc,
|
||||||
WGPUTextureView surface_view,
|
WGPUTextureView surface_view,
|
||||||
const OverlayFrame& f,
|
const OverlayFrame& f,
|
||||||
QPoint start_logical_px,
|
Eigen::Vector2i start_logical_px,
|
||||||
QPoint current_logical_px,
|
Eigen::Vector2i current_logical_px,
|
||||||
bool active);
|
bool active);
|
||||||
|
|
||||||
// Shared with the viewport's main FrameUniforms: same cap so the
|
// Shared with the viewport's main FrameUniforms: same cap so the
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/********************************************************************************
|
||||||
|
* *
|
||||||
|
* 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_STOPWATCH_H
|
||||||
|
#define IFCVIEWER_STOPWATCH_H
|
||||||
|
|
||||||
|
// Qt-free QElapsedTimer replacement: a thin wrapper over
|
||||||
|
// std::chrono::steady_clock that exposes the handful of methods our code
|
||||||
|
// actually uses (start, restart, elapsed-as-ms, nsecsElapsed, isValid).
|
||||||
|
// Same call-site API as QElapsedTimer so the existing diagnostics keep
|
||||||
|
// reading the same way after the type swap.
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class Stopwatch {
|
||||||
|
public:
|
||||||
|
using Clock = std::chrono::steady_clock;
|
||||||
|
using TimePoint = Clock::time_point;
|
||||||
|
|
||||||
|
Stopwatch() = default;
|
||||||
|
|
||||||
|
bool isValid() const { return started_; }
|
||||||
|
void start() { t0_ = Clock::now(); started_ = true; }
|
||||||
|
void restart() { start(); }
|
||||||
|
void invalidate() { started_ = false; }
|
||||||
|
|
||||||
|
// Elapsed milliseconds since start(); matches QElapsedTimer::elapsed().
|
||||||
|
int64_t elapsed() const {
|
||||||
|
if (!started_) return 0;
|
||||||
|
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
Clock::now() - t0_).count();
|
||||||
|
}
|
||||||
|
// Nanoseconds since start; matches QElapsedTimer::nsecsElapsed().
|
||||||
|
int64_t nsecsElapsed() const {
|
||||||
|
if (!started_) return 0;
|
||||||
|
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||||
|
Clock::now() - t0_).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TimePoint t0_{};
|
||||||
|
bool started_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IFCVIEWER_STOPWATCH_H
|
||||||
@@ -32,7 +32,6 @@
|
|||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QResizeEvent>
|
#include <QResizeEvent>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QElapsedTimer>
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
@@ -115,6 +114,14 @@ static double computeMeshLocalVolumeQuantised(
|
|||||||
// AND by pickMeshLocalAt to refine the AABB-coarse surface hit into a
|
// AND by pickMeshLocalAt to refine the AABB-coarse surface hit into a
|
||||||
// real triangle hit — see pickMeshLocalAt's refinement block.
|
// real triangle hit — see pickMeshLocalAt's refinement block.
|
||||||
|
|
||||||
|
// Convert Qt's pixel-coord QPoint (event payload) to the Eigen::Vector2i
|
||||||
|
// we store in member fields. The cast is mechanical but isolating it as
|
||||||
|
// a helper keeps every event-handler site one line shorter.
|
||||||
|
#include <QPoint>
|
||||||
|
static inline Eigen::Vector2i toV2i(const QPoint& p) {
|
||||||
|
return Eigen::Vector2i(p.x(), p.y());
|
||||||
|
}
|
||||||
|
|
||||||
// Slab method ray-AABB. inv_d is precomputed 1/dir per axis.
|
// Slab method ray-AABB. inv_d is precomputed 1/dir per axis.
|
||||||
|
|
||||||
static bool rayAabbSlab(const float ro[3], const float inv_d[3],
|
static bool rayAabbSlab(const float ro[3], const float inv_d[3],
|
||||||
@@ -680,8 +687,8 @@ void ViewportWindow::onToolBackspacePressed() {
|
|||||||
emit toolBackspacePressed();
|
emit toolBackspacePressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewportWindow::setBackgroundColor(const QColor& color) {
|
void ViewportWindow::setBackgroundColor(float r, float g, float b, float a) {
|
||||||
background_color_ = color;
|
background_color_ = {r, g, b, a};
|
||||||
if (isExposed()) requestUpdate();
|
if (isExposed()) requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4625,7 +4632,7 @@ void ViewportWindow::cullModelCpuUpload(ModelGpuData& m) {
|
|||||||
void ViewportWindow::render() {
|
void ViewportWindow::render() {
|
||||||
// Time the whole render() body (cull + encode + present) for the
|
// Time the whole render() body (cull + encode + present) for the
|
||||||
// benchmark stats. Started before any wgpu work so cull is included.
|
// benchmark stats. Started before any wgpu work so cull is included.
|
||||||
QElapsedTimer frame_timer;
|
Stopwatch frame_timer;
|
||||||
frame_timer.start();
|
frame_timer.start();
|
||||||
|
|
||||||
// Advance fly-mode camera by wall-clock dt since the last frame so the
|
// Advance fly-mode camera by wall-clock dt since the last frame so the
|
||||||
@@ -4676,7 +4683,7 @@ void ViewportWindow::render() {
|
|||||||
last_visible_triangles_ = 0;
|
last_visible_triangles_ = 0;
|
||||||
last_sub_draws_ = 0;
|
last_sub_draws_ = 0;
|
||||||
hiz_reject_count_ = 0;
|
hiz_reject_count_ = 0;
|
||||||
QElapsedTimer cull_timer;
|
Stopwatch cull_timer;
|
||||||
cull_timer.start();
|
cull_timer.start();
|
||||||
Eigen::Matrix4f vp_this_frame;
|
Eigen::Matrix4f vp_this_frame;
|
||||||
{
|
{
|
||||||
@@ -4823,7 +4830,7 @@ void ViewportWindow::render() {
|
|||||||
// chunks ≈ 360 wgpu calls/frame). If upload >> compute the parallel
|
// chunks ≈ 360 wgpu calls/frame). If upload >> compute the parallel
|
||||||
// cull is doing its job and the bottleneck is somewhere else.
|
// cull is doing its job and the bottleneck is somewhere else.
|
||||||
const double cull_compute_ms = double(cull_timer.nsecsElapsed()) / 1e6;
|
const double cull_compute_ms = double(cull_timer.nsecsElapsed()) / 1e6;
|
||||||
QElapsedTimer upload_timer;
|
Stopwatch upload_timer;
|
||||||
upload_timer.start();
|
upload_timer.start();
|
||||||
for (auto& [mid, m] : models_gpu_) {
|
for (auto& [mid, m] : models_gpu_) {
|
||||||
if (m.hidden) continue;
|
if (m.hidden) continue;
|
||||||
@@ -4848,7 +4855,7 @@ void ViewportWindow::render() {
|
|||||||
// visible into residency. Runs before draw encoding so newly-loaded
|
// visible into residency. Runs before draw encoding so newly-loaded
|
||||||
// chunks render the same frame. Timed separately because synchronous
|
// chunks render the same frame. Timed separately because synchronous
|
||||||
// disk reads here can dwarf the cull itself on big scenes.
|
// disk reads here can dwarf the cull itself on big scenes.
|
||||||
QElapsedTimer stream_timer;
|
Stopwatch stream_timer;
|
||||||
stream_timer.start();
|
stream_timer.start();
|
||||||
driveStreamingLoads();
|
driveStreamingLoads();
|
||||||
const double stream_ms = double(stream_timer.nsecsElapsed()) / 1e6;
|
const double stream_ms = double(stream_timer.nsecsElapsed()) / 1e6;
|
||||||
@@ -4875,9 +4882,9 @@ void ViewportWindow::render() {
|
|||||||
color.loadOp = WGPULoadOp_Clear;
|
color.loadOp = WGPULoadOp_Clear;
|
||||||
color.storeOp = WGPUStoreOp_Store;
|
color.storeOp = WGPUStoreOp_Store;
|
||||||
color.clearValue = {
|
color.clearValue = {
|
||||||
srgbToLinear(background_color_.redF()),
|
srgbToLinear(background_color_[0]),
|
||||||
srgbToLinear(background_color_.greenF()),
|
srgbToLinear(background_color_[1]),
|
||||||
srgbToLinear(background_color_.blueF()),
|
srgbToLinear(background_color_[2]),
|
||||||
1.0,
|
1.0,
|
||||||
};
|
};
|
||||||
color.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
|
color.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
|
||||||
@@ -5186,7 +5193,7 @@ void ViewportWindow::render() {
|
|||||||
// frame. Drainage happens at the top of the *next* frame via
|
// frame. Drainage happens at the top of the *next* frame via
|
||||||
// drainHizReadbacks(), giving the GPU at least one frame of headroom.
|
// drainHizReadbacks(), giving the GPU at least one frame of headroom.
|
||||||
if (hiz_enabled_ && hiz_submitted_slot >= 0) {
|
if (hiz_enabled_ && hiz_submitted_slot >= 0) {
|
||||||
QElapsedTimer hiz_timer;
|
Stopwatch hiz_timer;
|
||||||
if (bench_total_ > 0) hiz_timer.start();
|
if (bench_total_ > 0) hiz_timer.start();
|
||||||
startHizMap(hiz_submitted_slot, vp_this_frame);
|
startHizMap(hiz_submitted_slot, vp_this_frame);
|
||||||
if (bench_total_ > 0 && bench_count_ >= bench_warmup_) {
|
if (bench_total_ > 0 && bench_count_ >= bench_warmup_) {
|
||||||
@@ -7024,11 +7031,11 @@ void ViewportWindow::enterFpsMode() {
|
|||||||
if (fps_mode_) return;
|
if (fps_mode_) return;
|
||||||
fps_mode_ = true;
|
fps_mode_ = true;
|
||||||
fps_keys_held_.clear();
|
fps_keys_held_.clear();
|
||||||
fps_press_center_ = QPoint(width() / 2, height() / 2);
|
fps_press_center_ = Eigen::Vector2i(width() / 2, height() / 2);
|
||||||
fps_ignore_next_mouse_move_ = true;
|
fps_ignore_next_mouse_move_ = true;
|
||||||
fps_last_tick_.start();
|
fps_last_tick_.start();
|
||||||
setCursor(Qt::BlankCursor);
|
setCursor(Qt::BlankCursor);
|
||||||
QCursor::setPos(mapToGlobal(fps_press_center_));
|
QCursor::setPos(mapToGlobal(QPoint(fps_press_center_.x(), fps_press_center_.y())));
|
||||||
Log::info() << "[wgpu] fly mode active — WASD/QE to move, Shift to boost, Esc to exit";
|
Log::info() << "[wgpu] fly mode active — WASD/QE to move, Shift to boost, Esc to exit";
|
||||||
if (isExposed()) requestUpdate();
|
if (isExposed()) requestUpdate();
|
||||||
}
|
}
|
||||||
@@ -7043,7 +7050,7 @@ void ViewportWindow::exitFpsMode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ViewportWindow::fpsIntegrate() {
|
void ViewportWindow::fpsIntegrate() {
|
||||||
if (!fps_mode_ || fps_keys_held_.isEmpty()) return;
|
if (!fps_mode_ || fps_keys_held_.empty()) return;
|
||||||
|
|
||||||
const qint64 elapsed_ns = fps_last_tick_.nsecsElapsed();
|
const qint64 elapsed_ns = fps_last_tick_.nsecsElapsed();
|
||||||
fps_last_tick_.restart();
|
fps_last_tick_.restart();
|
||||||
@@ -7070,12 +7077,12 @@ void ViewportWindow::fpsIntegrate() {
|
|||||||
right.normalize();
|
right.normalize();
|
||||||
|
|
||||||
Eigen::Vector3f move(0, 0, 0);
|
Eigen::Vector3f move(0, 0, 0);
|
||||||
if (fps_keys_held_.contains(Qt::Key_W)) move += forward;
|
if (fps_keys_held_.count(Qt::Key_W)) move += forward;
|
||||||
if (fps_keys_held_.contains(Qt::Key_S)) move -= forward;
|
if (fps_keys_held_.count(Qt::Key_S)) move -= forward;
|
||||||
if (fps_keys_held_.contains(Qt::Key_D)) move += right;
|
if (fps_keys_held_.count(Qt::Key_D)) move += right;
|
||||||
if (fps_keys_held_.contains(Qt::Key_A)) move -= right;
|
if (fps_keys_held_.count(Qt::Key_A)) move -= right;
|
||||||
if (fps_keys_held_.contains(Qt::Key_E)) move += world_up;
|
if (fps_keys_held_.count(Qt::Key_E)) move += world_up;
|
||||||
if (fps_keys_held_.contains(Qt::Key_Q)) move -= world_up;
|
if (fps_keys_held_.count(Qt::Key_Q)) move -= world_up;
|
||||||
if (move.isZero()) return;
|
if (move.isZero()) return;
|
||||||
move.normalize();
|
move.normalize();
|
||||||
|
|
||||||
@@ -7084,7 +7091,7 @@ void ViewportWindow::fpsIntegrate() {
|
|||||||
// because distance varies frame-to-frame (and worse, wheel zoom kept
|
// because distance varies frame-to-frame (and worse, wheel zoom kept
|
||||||
// changing it underneath fly mode).
|
// changing it underneath fly mode).
|
||||||
const float speed = fps_move_speed_
|
const float speed = fps_move_speed_
|
||||||
* (fps_keys_held_.contains(Qt::Key_Shift) ? 5.0f : 1.0f);
|
* (fps_keys_held_.count(Qt::Key_Shift) ? 5.0f : 1.0f);
|
||||||
const Eigen::Vector3f delta = move * (speed * dt);
|
const Eigen::Vector3f delta = move * (speed * dt);
|
||||||
|
|
||||||
camera_target_[0] += delta.x();
|
camera_target_[0] += delta.x();
|
||||||
@@ -7239,7 +7246,7 @@ void ViewportWindow::mousePressEvent(QMouseEvent* event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nav_active_button_ = event->button();
|
nav_active_button_ = event->button();
|
||||||
nav_last_pos_ = event->position().toPoint();
|
nav_last_pos_ = toV2i(event->position().toPoint());
|
||||||
nav_press_pos_ = nav_last_pos_;
|
nav_press_pos_ = nav_last_pos_;
|
||||||
nav_dragged_ = false;
|
nav_dragged_ = false;
|
||||||
|
|
||||||
@@ -7249,7 +7256,7 @@ void ViewportWindow::mousePressEvent(QMouseEvent* event) {
|
|||||||
if (section_tool_active_
|
if (section_tool_active_
|
||||||
&& event->button() == Qt::LeftButton
|
&& event->button() == Qt::LeftButton
|
||||||
&& event->modifiers() == Qt::NoModifier) {
|
&& event->modifiers() == Qt::NoModifier) {
|
||||||
const QPoint lp = event->position().toPoint();
|
const Eigen::Vector2i lp = toV2i(event->position().toPoint());
|
||||||
const int hit = hitTestSectionGizmo(lp.x(), lp.y());
|
const int hit = hitTestSectionGizmo(lp.x(), lp.y());
|
||||||
if (hit >= 0) {
|
if (hit >= 0) {
|
||||||
section_drag_active_ = true;
|
section_drag_active_ = true;
|
||||||
@@ -7347,7 +7354,7 @@ void ViewportWindow::mouseReleaseEvent(QMouseEvent* event) {
|
|||||||
// route through the selection state. Shift = add, Ctrl = remove,
|
// route through the selection state. Shift = add, Ctrl = remove,
|
||||||
// no modifier = replace. Empty-space click clears.
|
// no modifier = replace. Empty-space click clears.
|
||||||
if (event->button() == Qt::LeftButton && !nav_dragged_) {
|
if (event->button() == Qt::LeftButton && !nav_dragged_) {
|
||||||
const QPoint pos = event->position().toPoint();
|
const Eigen::Vector2i pos = toV2i(event->position().toPoint());
|
||||||
const int px = int(pos.x() * devicePixelRatio());
|
const int px = int(pos.x() * devicePixelRatio());
|
||||||
const int py = int(pos.y() * devicePixelRatio());
|
const int py = int(pos.y() * devicePixelRatio());
|
||||||
|
|
||||||
@@ -7497,7 +7504,7 @@ void ViewportWindow::mouseMoveEvent(QMouseEvent* event) {
|
|||||||
// classification already declined this drag in mousePressEvent, so all
|
// classification already declined this drag in mousePressEvent, so all
|
||||||
// we have to do is slide the plane along its normal.
|
// we have to do is slide the plane along its normal.
|
||||||
if (section_drag_active_) {
|
if (section_drag_active_) {
|
||||||
const QPoint pos = event->position().toPoint();
|
const Eigen::Vector2i pos = toV2i(event->position().toPoint());
|
||||||
updateSectionDrag(pos.x(), pos.y());
|
updateSectionDrag(pos.x(), pos.y());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -7507,10 +7514,11 @@ void ViewportWindow::mouseMoveEvent(QMouseEvent* event) {
|
|||||||
// marquee triggers requestUpdate every frame the cursor moves so the
|
// marquee triggers requestUpdate every frame the cursor moves so the
|
||||||
// rect re-renders.
|
// rect re-renders.
|
||||||
if (box_select_armed_) {
|
if (box_select_armed_) {
|
||||||
const QPoint pos = event->position().toPoint();
|
const Eigen::Vector2i pos = toV2i(event->position().toPoint());
|
||||||
box_select_current_pos_ = pos;
|
box_select_current_pos_ = pos;
|
||||||
if (!box_select_active_) {
|
if (!box_select_active_) {
|
||||||
if ((pos - box_select_start_pos_).manhattanLength()
|
const Eigen::Vector2i diff = pos - box_select_start_pos_;
|
||||||
|
if (std::abs(diff.x()) + std::abs(diff.y())
|
||||||
>= kBoxSelectThresholdPx) {
|
>= kBoxSelectThresholdPx) {
|
||||||
box_select_active_ = true;
|
box_select_active_ = true;
|
||||||
}
|
}
|
||||||
@@ -7531,7 +7539,7 @@ void ViewportWindow::mouseMoveEvent(QMouseEvent* event) {
|
|||||||
fps_ignore_next_mouse_move_ = false;
|
fps_ignore_next_mouse_move_ = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const QPoint pos = event->position().toPoint();
|
const Eigen::Vector2i pos = toV2i(event->position().toPoint());
|
||||||
const int dx = pos.x() - fps_press_center_.x();
|
const int dx = pos.x() - fps_press_center_.x();
|
||||||
const int dy = pos.y() - fps_press_center_.y();
|
const int dy = pos.y() - fps_press_center_.y();
|
||||||
|
|
||||||
@@ -7563,14 +7571,14 @@ void ViewportWindow::mouseMoveEvent(QMouseEvent* event) {
|
|||||||
camera_target_[2] = pinned_eye.z() - camera_distance_ * sp;
|
camera_target_[2] = pinned_eye.z() - camera_distance_ * sp;
|
||||||
|
|
||||||
fps_ignore_next_mouse_move_ = true;
|
fps_ignore_next_mouse_move_ = true;
|
||||||
QCursor::setPos(mapToGlobal(fps_press_center_));
|
QCursor::setPos(mapToGlobal(QPoint(fps_press_center_.x(), fps_press_center_.y())));
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nav_active_button_ == Qt::NoButton) return;
|
if (nav_active_button_ == Qt::NoButton) return;
|
||||||
|
|
||||||
const QPoint pos = event->position().toPoint();
|
const Eigen::Vector2i pos = toV2i(event->position().toPoint());
|
||||||
const int dx = pos.x() - nav_last_pos_.x();
|
const int dx = pos.x() - nav_last_pos_.x();
|
||||||
const int dy = pos.y() - nav_last_pos_.y();
|
const int dy = pos.y() - nav_last_pos_.y();
|
||||||
nav_last_pos_ = pos;
|
nav_last_pos_ = pos;
|
||||||
@@ -7638,7 +7646,7 @@ void ViewportWindow::keyPressEvent(QKeyEvent* event) {
|
|||||||
case Qt::Key_W: case Qt::Key_A: case Qt::Key_S: case Qt::Key_D:
|
case Qt::Key_W: case Qt::Key_A: case Qt::Key_S: case Qt::Key_D:
|
||||||
case Qt::Key_Q: case Qt::Key_E: case Qt::Key_Shift:
|
case Qt::Key_Q: case Qt::Key_E: case Qt::Key_Shift:
|
||||||
if (!event->isAutoRepeat()) {
|
if (!event->isAutoRepeat()) {
|
||||||
const bool was_empty = fps_keys_held_.isEmpty();
|
const bool was_empty = fps_keys_held_.empty();
|
||||||
fps_keys_held_.insert(key);
|
fps_keys_held_.insert(key);
|
||||||
if (was_empty) fps_last_tick_.restart();
|
if (was_empty) fps_last_tick_.restart();
|
||||||
// ALWAYS kick the render loop, not just on first key.
|
// ALWAYS kick the render loop, not just on first key.
|
||||||
@@ -7804,7 +7812,7 @@ void ViewportWindow::keyPressEvent(QKeyEvent* event) {
|
|||||||
|
|
||||||
void ViewportWindow::keyReleaseEvent(QKeyEvent* event) {
|
void ViewportWindow::keyReleaseEvent(QKeyEvent* event) {
|
||||||
if (fps_mode_ && !event->isAutoRepeat()) {
|
if (fps_mode_ && !event->isAutoRepeat()) {
|
||||||
fps_keys_held_.remove(event->key());
|
fps_keys_held_.erase(event->key());
|
||||||
}
|
}
|
||||||
QWindow::keyReleaseEvent(event);
|
QWindow::keyReleaseEvent(event);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,13 @@
|
|||||||
#define WGPUVIEWPORTWINDOW_H
|
#define WGPUVIEWPORTWINDOW_H
|
||||||
|
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
#include <QColor>
|
|
||||||
#include <QElapsedTimer>
|
|
||||||
#include <QPoint>
|
|
||||||
#include <QSet>
|
|
||||||
#include <string>
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include "Stopwatch.h"
|
||||||
|
|
||||||
#include <webgpu/webgpu.h>
|
#include <webgpu/webgpu.h>
|
||||||
|
|
||||||
#include <Eigen/Dense>
|
#include <Eigen/Dense>
|
||||||
@@ -89,7 +89,7 @@ public:
|
|||||||
void onToolModeChanged(int tool_mode) override;
|
void onToolModeChanged(int tool_mode) override;
|
||||||
void onToolBackspacePressed() override;
|
void onToolBackspacePressed() override;
|
||||||
|
|
||||||
void setBackgroundColor(const QColor& color);
|
void setBackgroundColor(float r, float g, float b, float a = 1.0f);
|
||||||
|
|
||||||
// Queue a sidecar path to be loaded after wgpu init completes. Safe to
|
// Queue a sidecar path to be loaded after wgpu init completes. Safe to
|
||||||
// call before the window is exposed. The path is resolved against the
|
// call before the window is exposed. The path is resolved against the
|
||||||
@@ -811,8 +811,8 @@ private:
|
|||||||
// release: plain → replace, Shift → add, Ctrl → remove.
|
// release: plain → replace, Shift → add, Ctrl → remove.
|
||||||
bool box_select_armed_ = false;
|
bool box_select_armed_ = false;
|
||||||
bool box_select_active_ = false;
|
bool box_select_active_ = false;
|
||||||
QPoint box_select_start_pos_; // logical px
|
Eigen::Vector2i box_select_start_pos_; // logical px
|
||||||
QPoint box_select_current_pos_; // logical px
|
Eigen::Vector2i box_select_current_pos_; // logical px
|
||||||
Qt::KeyboardModifiers box_select_press_mods_ = Qt::NoModifier;
|
Qt::KeyboardModifiers box_select_press_mods_ = Qt::NoModifier;
|
||||||
static constexpr int kBoxSelectThresholdPx = 5;
|
static constexpr int kBoxSelectThresholdPx = 5;
|
||||||
// R32UInt staging for the rect-pick. Sized to the largest rect we've
|
// R32UInt staging for the rect-pick. Sized to the largest rect we've
|
||||||
@@ -824,7 +824,7 @@ private:
|
|||||||
// the press fall through to the orbit/pan handlers.
|
// the press fall through to the orbit/pan handlers.
|
||||||
bool section_drag_active_ = false;
|
bool section_drag_active_ = false;
|
||||||
int section_drag_index_ = -1;
|
int section_drag_index_ = -1;
|
||||||
QPoint section_drag_start_mouse_;
|
Eigen::Vector2i section_drag_start_mouse_;
|
||||||
Eigen::Vector3f section_drag_start_origin_;
|
Eigen::Vector3f section_drag_start_origin_;
|
||||||
// Mirrors GL ViewportWindow::hitTestSectionGizmo: returns the index of
|
// Mirrors GL ViewportWindow::hitTestSectionGizmo: returns the index of
|
||||||
// the plane whose arrow gizmo is within grab_px of (x, y), or -1.
|
// the plane whose arrow gizmo is within grab_px of (x, y), or -1.
|
||||||
@@ -858,7 +858,8 @@ private:
|
|||||||
// thread per model.
|
// thread per model.
|
||||||
mutable std::atomic<int> hiz_trace_budget_{0};
|
mutable std::atomic<int> hiz_trace_budget_{0};
|
||||||
|
|
||||||
QColor background_color_ = QColor("#202329");
|
// 0x20 / 0xff ≈ 0.125, 0x23 / 0xff ≈ 0.137, 0x29 / 0xff ≈ 0.161.
|
||||||
|
Eigen::Vector4f background_color_ = {0.125f, 0.137f, 0.161f, 1.0f};
|
||||||
|
|
||||||
// Camera (orbit, right-handed Y-up world → wait, BIM is +Z up).
|
// Camera (orbit, right-handed Y-up world → wait, BIM is +Z up).
|
||||||
// Mirrors the GL viewport's defaults; mouse navigation lands later.
|
// Mirrors the GL viewport's defaults; mouse navigation lands later.
|
||||||
@@ -878,9 +879,9 @@ private:
|
|||||||
// exit via Esc (also any unrelated key click) — recenter the cursor
|
// exit via Esc (also any unrelated key click) — recenter the cursor
|
||||||
// back at fps_press_center_ so the orbit camera resumes cleanly.
|
// back at fps_press_center_ so the orbit camera resumes cleanly.
|
||||||
bool fps_mode_ = false;
|
bool fps_mode_ = false;
|
||||||
QSet<int> fps_keys_held_;
|
std::unordered_set<int> fps_keys_held_;
|
||||||
QElapsedTimer fps_last_tick_;
|
Stopwatch fps_last_tick_;
|
||||||
QPoint fps_press_center_;
|
Eigen::Vector2i fps_press_center_;
|
||||||
bool fps_ignore_next_mouse_move_ = false;
|
bool fps_ignore_next_mouse_move_ = false;
|
||||||
// Fly base speed in m/s at no-modifier (Shift gives a 5× boost). Default
|
// Fly base speed in m/s at no-modifier (Shift gives a 5× boost). Default
|
||||||
// 5.0 matches GL fps_move_speed_. Scrollwheel in fly mode adjusts this
|
// 5.0 matches GL fps_move_speed_. Scrollwheel in fly mode adjusts this
|
||||||
@@ -892,7 +893,7 @@ private:
|
|||||||
// print dt of each fpsIntegrate call and the prior render's elapsed
|
// print dt of each fpsIntegrate call and the prior render's elapsed
|
||||||
// ms. Off by default (env-gated) so the normal log stays clean.
|
// ms. Off by default (env-gated) so the normal log stays clean.
|
||||||
bool fly_debug_ = false;
|
bool fly_debug_ = false;
|
||||||
QElapsedTimer fly_render_clock_;
|
Stopwatch fly_render_clock_;
|
||||||
|
|
||||||
// Click-and-track diagnostic: when a pick lands, stash the chunk
|
// Click-and-track diagnostic: when a pick lands, stash the chunk
|
||||||
// that holds the picked object. driveStreamingLoads watches for that
|
// that holds the picked object. driveStreamingLoads watches for that
|
||||||
@@ -1064,8 +1065,8 @@ private:
|
|||||||
// LMB-click-without-drag picks the object under the cursor. No
|
// LMB-click-without-drag picks the object under the cursor. No
|
||||||
// Blender/Maya preset awareness yet — that arrives with AppSettings.
|
// Blender/Maya preset awareness yet — that arrives with AppSettings.
|
||||||
Qt::MouseButton nav_active_button_ = Qt::NoButton;
|
Qt::MouseButton nav_active_button_ = Qt::NoButton;
|
||||||
QPoint nav_last_pos_;
|
Eigen::Vector2i nav_last_pos_;
|
||||||
QPoint nav_press_pos_;
|
Eigen::Vector2i nav_press_pos_;
|
||||||
bool nav_dragged_ = false;
|
bool nav_dragged_ = false;
|
||||||
|
|
||||||
// Benchmark mode. setBenchmarkFrames(N) arms it; render() integrates the
|
// Benchmark mode. setBenchmarkFrames(N) arms it; render() integrates the
|
||||||
|
|||||||
Reference in New Issue
Block a user