diff --git a/qrenderdoc/Code/Interface/PersistantConfig.cpp b/qrenderdoc/Code/Interface/PersistantConfig.cpp index 75043648a..56b5ee5b1 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.cpp +++ b/qrenderdoc/Code/Interface/PersistantConfig.cpp @@ -99,6 +99,12 @@ rdcstr convertFromVariant(const QVariant &val) return val.toString(); } +template <> +uint32_t convertFromVariant(const QVariant &val) +{ + return val.toUInt(); +} + template listType convertFromVariant(const QVariantList &val) { diff --git a/qrenderdoc/Code/Interface/PersistantConfig.h b/qrenderdoc/Code/Interface/PersistantConfig.h index 488b15888..6fe35ba8d 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.h +++ b/qrenderdoc/Code/Interface/PersistantConfig.h @@ -324,6 +324,24 @@ DECLARE_REFLECTION_STRUCT(BugReport); ":type: bool"); \ CONFIG_SETTING_VAL(public, bool, bool, TextureViewer_PerTexYFlip, false) \ \ + DOCUMENT( \ + "List of Qt keycodes for mesh viewer key bindings. Can be empty if no keys are configured " \ + "which will revert to default behaviour of physical WASD (independent of local keyboard " \ + "layout)." \ + "\n" \ + "Defaults to an empty list." \ + "" \ + ":type: List[int]"); \ + CONFIG_SETTING(public, QVariantList, rdcarray, MeshViewer_KeySettings) \ + \ + DOCUMENT( \ + "The Qt modifier code for the mesh viewer 'speed' modifier." \ + "\n" \ + "Defaults to ``-1`` which means Shift." \ + "" \ + ":type: int"); \ + CONFIG_SETTING_VAL(public, int, int, MeshViewer_SpeedModifier, -1) \ + \ DOCUMENT( \ "List of the directories containing custom shader files for the Texture Viewer.\n" \ "\n:" \ diff --git a/qrenderdoc/Widgets/CustomPaintWidget.cpp b/qrenderdoc/Widgets/CustomPaintWidget.cpp index cc5f58e0d..402d975e7 100644 --- a/qrenderdoc/Widgets/CustomPaintWidget.cpp +++ b/qrenderdoc/Widgets/CustomPaintWidget.cpp @@ -200,6 +200,11 @@ void CustomPaintWidgetInternal::mousePressEvent(QMouseEvent *e) emit m_Custom.clicked(e); } +void CustomPaintWidgetInternal::mouseReleaseEvent(QMouseEvent *e) +{ + emit m_Custom.unclicked(e); +} + void CustomPaintWidgetInternal::mouseDoubleClickEvent(QMouseEvent *event) { emit m_Custom.doubleClicked(event); diff --git a/qrenderdoc/Widgets/CustomPaintWidget.h b/qrenderdoc/Widgets/CustomPaintWidget.h index 8ede69d3a..bc633e4f5 100644 --- a/qrenderdoc/Widgets/CustomPaintWidget.h +++ b/qrenderdoc/Widgets/CustomPaintWidget.h @@ -45,6 +45,7 @@ public: bool IsRendering() const { return m_Rendering; } protected: void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; void mouseDoubleClickEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *e) override; void wheelEvent(QWheelEvent *e) override; @@ -82,6 +83,7 @@ public: void SetBackCol(QColor col) { m_BackCol = col; } signals: void clicked(QMouseEvent *e); + void unclicked(QMouseEvent *e); void doubleClicked(QMouseEvent *e); void mouseMove(QMouseEvent *e); void resize(QResizeEvent *e); diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index e81206da8..e98540b6b 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -135,13 +135,11 @@ enum class CameraWrapper { public: + CameraWrapper(ICaptureContext &ctx) : m_Ctx(ctx) {} virtual ~CameraWrapper() {} virtual bool Update(QRect winSize) = 0; virtual ICamera *camera() = 0; - virtual void MouseWheel(QWheelEvent *e) = 0; - - virtual void MouseClick(QMouseEvent *e) { m_DragStartPos = e->pos(); } virtual void MouseMove(QMouseEvent *e) { if(e->buttons() & Qt::LeftButton) @@ -154,58 +152,110 @@ public: } } - enum class KeyPressDirection - { - None, - Left, - Right, - Forward, - Back, - Up, - Down, - }; - KeyPressDirection GetDirection(QKeyEvent *e) { - // if we have a native scancode, we expect to be able to match it. If we don't then don't get - // any false positives by checking the virtual key - if(e->nativeScanCode() > 1) + // if no settings are bound, use default bindings. This should be empty, but we also do this + // path if it's invalid and not the size we expect + if(m_Ctx.Config().MeshViewer_KeySettings.size() < (size_t)KeyPressDirection::NumSettings) { - switch(e->nativeScanCode()) + // if we have a native scancode, we expect to be able to match it. If we don't then don't get + // any false positives by checking the virtual key + if(e->nativeScanCode() > 1) { - case NativeScanCode::Key_A: return KeyPressDirection::Left; - case NativeScanCode::Key_D: return KeyPressDirection::Right; - case NativeScanCode::Key_W: return KeyPressDirection::Forward; - case NativeScanCode::Key_S: return KeyPressDirection::Back; - case NativeScanCode::Key_R: return KeyPressDirection::Up; - case NativeScanCode::Key_F: return KeyPressDirection::Down; + switch(e->nativeScanCode()) + { + case NativeScanCode::Key_A: return KeyPressDirection::Left; + case NativeScanCode::Key_D: return KeyPressDirection::Right; + case NativeScanCode::Key_W: return KeyPressDirection::Forward; + case NativeScanCode::Key_S: return KeyPressDirection::Back; + case NativeScanCode::Key_R: return KeyPressDirection::Up; + case NativeScanCode::Key_F: return KeyPressDirection::Down; + default: break; + } + } + else + { + switch(e->nativeVirtualKey()) + { + case NativeVirtualKey::Key_A: return KeyPressDirection::Left; + case NativeVirtualKey::Key_D: return KeyPressDirection::Right; + case NativeVirtualKey::Key_W: return KeyPressDirection::Forward; + case NativeVirtualKey::Key_S: return KeyPressDirection::Back; + case NativeVirtualKey::Key_R: return KeyPressDirection::Up; + case NativeVirtualKey::Key_F: return KeyPressDirection::Down; + default: break; + } + } + + // handle arrow keys, we can do this safely with Qt::Key + switch(e->key()) + { + case Qt::Key_Left: return KeyPressDirection::Left; + case Qt::Key_Right: return KeyPressDirection::Right; + case Qt::Key_Up: return KeyPressDirection::Forward; + case Qt::Key_Down: return KeyPressDirection::Back; + case Qt::Key_PageUp: return KeyPressDirection::Up; + case Qt::Key_PageDown: return KeyPressDirection::Down; default: break; } } else { - switch(e->nativeVirtualKey()) + for(int i = 0; i < (int)KeyPressDirection::Count; i++) { - case NativeVirtualKey::Key_A: return KeyPressDirection::Left; - case NativeVirtualKey::Key_D: return KeyPressDirection::Right; - case NativeVirtualKey::Key_W: return KeyPressDirection::Forward; - case NativeVirtualKey::Key_S: return KeyPressDirection::Back; - case NativeVirtualKey::Key_R: return KeyPressDirection::Up; - case NativeVirtualKey::Key_F: return KeyPressDirection::Down; - default: break; + KeyPressDirection dir = KeyPressDirection(i); + Qt::Key primary = + getKeySetting(m_Ctx.Config().MeshViewer_KeySettings[keySettingIdx(dir, true)]); + Qt::Key secondary = + getKeySetting(m_Ctx.Config().MeshViewer_KeySettings[keySettingIdx(dir, false)]); + + if(e->key() == primary || e->key() == secondary) + return dir; } } - // handle arrow keys, we can do this safely with Qt::Key - switch(e->key()) + return KeyPressDirection::None; + } + + KeyPressDirection GetDirection(QMouseEvent *e) + { + if(m_Ctx.Config().MeshViewer_KeySettings.size() >= (size_t)KeyPressDirection::NumSettings) { - case Qt::Key_Left: return KeyPressDirection::Left; - case Qt::Key_Right: return KeyPressDirection::Right; - case Qt::Key_Up: return KeyPressDirection::Forward; - case Qt::Key_Down: return KeyPressDirection::Back; - case Qt::Key_PageUp: return KeyPressDirection::Up; - case Qt::Key_PageDown: return KeyPressDirection::Down; - default: break; + for(int i = 0; i < (int)KeyPressDirection::Count; i++) + { + KeyPressDirection dir = KeyPressDirection(i); + Qt::MouseButton primary = + getMouseButtonSetting(m_Ctx.Config().MeshViewer_KeySettings[keySettingIdx(dir, true)]); + Qt::MouseButton secondary = + getMouseButtonSetting(m_Ctx.Config().MeshViewer_KeySettings[keySettingIdx(dir, false)]); + + if(e->button() == primary || e->button() == secondary) + return dir; + } + } + + return KeyPressDirection::None; + } + + KeyPressDirection GetDirection(QWheelEvent *e) + { + if(m_Ctx.Config().MeshViewer_KeySettings.size() >= (size_t)KeyPressDirection::NumSettings) + { + QPoint angleDelta = e->angleDelta(); + angleDelta.setX(qMin(1, qMax(-1, angleDelta.x()))); + angleDelta.setY(qMin(1, qMax(-1, angleDelta.y()))); + + for(int i = 0; i < (int)KeyPressDirection::Count; i++) + { + KeyPressDirection dir = KeyPressDirection(i); + QPoint primary = + getMouseWheelSetting(m_Ctx.Config().MeshViewer_KeySettings[keySettingIdx(dir, true)]); + QPoint secondary = + getMouseWheelSetting(m_Ctx.Config().MeshViewer_KeySettings[keySettingIdx(dir, false)]); + + if(angleDelta == primary || angleDelta == secondary) + return dir; + } } return KeyPressDirection::None; @@ -217,12 +267,16 @@ public: if(dir == KeyPressDirection::Left || dir == KeyPressDirection::Right) setMove(Direction::Horiz, 0); - if(dir == KeyPressDirection::Forward || dir == KeyPressDirection::Back) + else if(dir == KeyPressDirection::Forward || dir == KeyPressDirection::Back) setMove(Direction::Fwd, 0); - if(dir == KeyPressDirection::Up || dir == KeyPressDirection::Down) + else if(dir == KeyPressDirection::Up || dir == KeyPressDirection::Down) setMove(Direction::Vert, 0); - if(e->modifiers() & Qt::ShiftModifier) + Qt::KeyboardModifier speedMod = Qt::ShiftModifier; + if(m_Ctx.Config().MeshViewer_SpeedModifier > 0) + speedMod = Qt::KeyboardModifier(m_Ctx.Config().MeshViewer_SpeedModifier); + + if(speedMod != Qt::NoModifier && (e->modifiers() & speedMod)) m_CurrentSpeed = 3.0f; else m_CurrentSpeed = 1.0f; @@ -234,7 +288,7 @@ public: switch(dir) { - case KeyPressDirection::None: break; + default: break; case KeyPressDirection::Left: setMove(Direction::Horiz, -1); break; case KeyPressDirection::Right: setMove(Direction::Horiz, 1); break; case KeyPressDirection::Forward: setMove(Direction::Fwd, 1); break; @@ -243,12 +297,47 @@ public: case KeyPressDirection::Down: setMove(Direction::Vert, -1); break; } - if(e->modifiers() & Qt::ShiftModifier) + Qt::KeyboardModifier speedMod = Qt::ShiftModifier; + if(m_Ctx.Config().MeshViewer_SpeedModifier > 0) + speedMod = Qt::KeyboardModifier(m_Ctx.Config().MeshViewer_SpeedModifier); + + if(speedMod != Qt::NoModifier && (e->modifiers() & speedMod)) m_CurrentSpeed = 3.0f; else m_CurrentSpeed = 1.0f; } + virtual void MouseClick(QMouseEvent *e) + { + m_DragStartPos = e->pos(); + KeyPressDirection dir = GetDirection(e); + + switch(dir) + { + default: break; + case KeyPressDirection::Left: setMove(Direction::Horiz, -1); break; + case KeyPressDirection::Right: setMove(Direction::Horiz, 1); break; + case KeyPressDirection::Forward: setMove(Direction::Fwd, 1); break; + case KeyPressDirection::Back: setMove(Direction::Fwd, -1); break; + case KeyPressDirection::Up: setMove(Direction::Vert, 1); break; + case KeyPressDirection::Down: setMove(Direction::Vert, -1); break; + } + } + + virtual void MouseUnclick(QMouseEvent *e) + { + KeyPressDirection dir = GetDirection(e); + + if(dir == KeyPressDirection::Left || dir == KeyPressDirection::Right) + setMove(Direction::Horiz, 0); + else if(dir == KeyPressDirection::Forward || dir == KeyPressDirection::Back) + setMove(Direction::Fwd, 0); + else if(dir == KeyPressDirection::Up || dir == KeyPressDirection::Down) + setMove(Direction::Vert, 0); + } + + virtual void MouseWheel(QWheelEvent *e) {} + float SpeedMultiplier = 0.05f; protected: @@ -263,6 +352,8 @@ protected: int move(Direction dir) { return m_CurrentMove[(int)dir]; } float currentSpeed() { return m_CurrentSpeed * SpeedMultiplier; } QPoint dragStartPos() { return m_DragStartPos; } + + ICaptureContext &m_Ctx; private: float m_CurrentSpeed = 1.0f; int m_CurrentMove[(int)Direction::Num] = {0, 0, 0}; @@ -274,7 +365,10 @@ private: class ArcballWrapper : public CameraWrapper { public: - ArcballWrapper() { m_Cam = RENDERDOC_InitCamera(CameraType::Arcball); } + ArcballWrapper(ICaptureContext &ctx) : CameraWrapper(ctx) + { + m_Cam = RENDERDOC_InitCamera(CameraType::Arcball); + } virtual ~ArcballWrapper() { m_Cam->Shutdown(); } ICamera *camera() override { return m_Cam; } void Reset(FloatVector pos, float dist) @@ -299,6 +393,8 @@ public: void MouseWheel(QWheelEvent *e) override { + CameraWrapper::MouseWheel(e); + float mod = (1.0f - e->delta() / 2500.0f); SetDistance(qMax(1e-6f, m_Distance * mod)); @@ -376,7 +472,10 @@ private: class FlycamWrapper : public CameraWrapper { public: - FlycamWrapper() { m_Cam = RENDERDOC_InitCamera(CameraType::FPSLook); } + FlycamWrapper(ICaptureContext &ctx) : CameraWrapper(ctx) + { + m_Cam = RENDERDOC_InitCamera(CameraType::FPSLook); + } virtual ~FlycamWrapper() { m_Cam->Shutdown(); } ICamera *camera() override { return m_Cam; } void Reset(FloatVector pos) @@ -431,7 +530,49 @@ public: return false; } - void MouseWheel(QWheelEvent *e) override {} + virtual void MouseWheel(QWheelEvent *e) override + { + CameraWrapper::MouseWheel(e); + + KeyPressDirection dir = GetDirection(e); + + FloatVector fwd = m_Cam->GetForward(); + FloatVector right = m_Cam->GetRight(); + + float speed = currentSpeed(); + + if(dir == KeyPressDirection::Left || dir == KeyPressDirection::Right) + { + int horizMove = dir == KeyPressDirection::Left ? -1 : 1; + m_Position.x += right.x * speed * (float)horizMove; + m_Position.y += right.y * speed * (float)horizMove; + m_Position.z += right.z * speed * (float)horizMove; + } + else if(dir == KeyPressDirection::Up || dir == KeyPressDirection::Down) + { + // this makes less intuitive sense, instead go 'absolute' up + // m_Position.x += up.x * speed * (float)vertMove; + // m_Position.y += up.y * speed * (float)vertMove; + // m_Position.z += up.z * speed * (float)vertMove; + + int vertMove = dir == KeyPressDirection::Up ? -1 : 1; + m_Position.y += speed * (float)vertMove; + } + else if(dir == KeyPressDirection::Forward || dir == KeyPressDirection::Back) + { + int fwdMove = dir == KeyPressDirection::Back ? -1 : 1; + m_Position.x += fwd.x * speed * (float)fwdMove; + m_Position.y += fwd.y * speed * (float)fwdMove; + m_Position.z += fwd.z * speed * (float)fwdMove; + } + else + { + return; + } + + m_Cam->SetPosition(m_Position.x, m_Position.y, m_Position.z); + } + void MouseMove(QMouseEvent *e) override { if(dragStartPos().x() > 0 && e->buttons() == Qt::LeftButton) @@ -2410,8 +2551,8 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) ui->formatSpecifier->setContext(&m_Ctx); - m_Flycam = new FlycamWrapper(); - m_Arcball = new ArcballWrapper(); + m_Flycam = new FlycamWrapper(m_Ctx); + m_Arcball = new ArcballWrapper(m_Ctx); m_CurrentCamera = m_Arcball; m_Output = NULL; @@ -2636,6 +2777,7 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) QObject::connect(ui->render, &CustomPaintWidget::mouseMove, this, &BufferViewer::render_mouseMove); QObject::connect(ui->render, &CustomPaintWidget::clicked, this, &BufferViewer::render_clicked); + QObject::connect(ui->render, &CustomPaintWidget::unclicked, this, &BufferViewer::render_unclicked); QObject::connect(ui->render, &CustomPaintWidget::keyPress, this, &BufferViewer::render_keyPress); QObject::connect(ui->render, &CustomPaintWidget::keyRelease, this, &BufferViewer::render_keyRelease); @@ -5021,6 +5163,17 @@ void BufferViewer::render_clicked(QMouseEvent *e) INVOKE_MEMFN(RT_UpdateAndDisplay); } +void BufferViewer::render_unclicked(QMouseEvent *e) +{ + if(!m_Ctx.IsCaptureLoaded()) + return; + + if(m_CurrentCamera) + m_CurrentCamera->MouseUnclick(e); + + INVOKE_MEMFN(RT_UpdateAndDisplay); +} + void BufferViewer::ScrollToRow(RDTableView *view, int row) { int hs = view->horizontalScrollBar()->value(); diff --git a/qrenderdoc/Windows/BufferViewer.h b/qrenderdoc/Windows/BufferViewer.h index 2bc91998a..b6a75e050 100644 --- a/qrenderdoc/Windows/BufferViewer.h +++ b/qrenderdoc/Windows/BufferViewer.h @@ -34,6 +34,70 @@ namespace Ui class BufferViewer; } +enum class KeyPressDirection +{ + Forward, + Back, + Left, + Right, + Up, + Down, + Count, + None = Count, + NumSettings = Count * 2, +}; + +constexpr int keySettingIdx(KeyPressDirection dir, bool primary) +{ + return int(dir) * 2 + (primary ? 0 : 1); +} + +constexpr Qt::Key getKeySetting(uint32_t k) +{ + return k != 0 && k < 0x10000000U ? Qt::Key(k) : Qt::Key_unknown; +} + +constexpr uint32_t makeKeySetting(Qt::Key k) +{ + return k; +} + +constexpr Qt::MouseButton getMouseButtonSetting(uint32_t b) +{ + return b & 0x10000000U ? Qt::MouseButton(b & ~0x10000000U) : Qt::MaxMouseButton; +} + +constexpr uint32_t makeMouseButtonSetting(Qt::MouseButton b) +{ + return uint32_t(b) | 0x10000000U; +} + +inline QPoint getMouseWheelSetting(uint32_t w) +{ + switch(w) + { + case 0x20000000U + 0: return QPoint(0, 1); + case 0x20000000U + 1: return QPoint(0, -1); + case 0x20000000U + 2: return QPoint(1, 0); + case 0x20000000U + 3: return QPoint(-1, 0); + default: break; + } + return QPoint(); +} + +inline uint32_t makeMouseWheelSetting(QPoint angleDelta) +{ + if(angleDelta.y() > 0) + return 0x20000000U + 0; + else if(angleDelta.y() < 0) + return 0x20000000U + 1; + else if(angleDelta.x() < 0) + return 0x20000000U + 2; + else if(angleDelta.x() > 0) + return 0x20000000U + 3; + return 0; +} + class ComputeDebugSelector; class RDSpinBox64; class QItemSelection;