[ui] Viewer3D: DefaultCameraController + better double click handling

* add DefaultCameraController: 
   * mouse-only navigation
   * keyboard modifiers fallbacks for view re-centering (Ctrl+LeftClick) and fast zooming (Alt+RightClick)
* improved double click handling for 3D Object Picking
  * moved logic/Timer to the CameraController
  * always use TrianglePicking but only activate the ObjectPicker when necessary
This commit is contained in:
Yann Lanthony 2017-12-12 19:10:18 +01:00
parent 886ded7f19
commit ecc376e3b6
3 changed files with 172 additions and 27 deletions

View file

@ -0,0 +1,140 @@
import QtQuick 2.7
import Qt3D.Core 2.1
import Qt3D.Render 2.1
import Qt3D.Input 2.1
import Qt3D.Logic 2.0
import QtQml 2.2
Entity {
id: root
property Camera camera
property real translateSpeed: 75.0
property real tiltSpeed: 500.0
property real panSpeed: 500.0
property bool moving: pressed || actionAlt.active
readonly property alias controlPressed: actionControl.active
readonly property alias pressed: mouseHandler._pressed
signal mousePressed(var mouse)
signal mouseReleased(var mouse)
signal mouseClicked(var mouse)
signal mouseWheeled(var wheel)
signal mouseDoubleClicked(var mouse)
KeyboardDevice { id: keyboardSourceDevice }
MouseDevice { id: mouseSourceDevice }
MouseHandler {
id: mouseHandler
property bool _pressed
sourceDevice: mouseSourceDevice
onPressed: { _pressed = true; mousePressed(mouse) }
onReleased: { _pressed = false; mouseReleased(mouse) }
onClicked: mouseClicked(mouse)
onDoubleClicked: mouseDoubleClicked(mouse)
onWheel: {
var d = (root.camera.viewCenter.minus(root.camera.position)).length() * 0.2;
var tz = (wheel.angleDelta.y / 120) * d;
root.camera.translate(Qt.vector3d(0, 0, tz), Camera.DontTranslateViewCenter)
}
}
LogicalDevice {
id: cameraControlDevice
actions: [
Action {
id: actionLMB
inputs: [
ActionInput {
sourceDevice: mouseSourceDevice
buttons: [MouseEvent.LeftButton]
}
]
},
Action {
id: actionRMB
inputs: [
ActionInput {
sourceDevice: mouseSourceDevice
buttons: [MouseEvent.RightButton]
}
]
},
Action {
id: actionMMB
inputs: [
ActionInput {
sourceDevice: mouseSourceDevice
buttons: [MouseEvent.MiddleButton]
}
]
},
Action {
id: actionControl
inputs: [
ActionInput {
sourceDevice: keyboardSourceDevice
buttons: [Qt.Key_Control]
}
]
},
Action {
id: actionAlt
inputs: [
ActionInput {
sourceDevice: keyboardSourceDevice
buttons: [Qt.Key_Alt]
}
]
}
]
axes: [
Axis {
id: axisMX
inputs: [
AnalogAxisInput {
sourceDevice: mouseSourceDevice
axis: MouseDevice.X
}
]
},
Axis {
id: axisMY
inputs: [
AnalogAxisInput {
sourceDevice: mouseSourceDevice
axis: MouseDevice.Y
}
]
}
]
}
components: [
FrameAction {
onTriggered: {
if(actionLMB.active) { // rotate
var rx = -axisMX.value;
var ry = -axisMY.value;
root.camera.panAboutViewCenter(root.panSpeed * rx * dt, Qt.vector3d(0,1,0))
root.camera.tiltAboutViewCenter(root.tiltSpeed * ry * dt)
return;
}
if(actionMMB.active) { // translate
var d = (root.camera.viewCenter.minus(root.camera.position)).length() * 0.03;
var tx = axisMX.value * root.translateSpeed * d;
var ty = axisMY.value * root.translateSpeed * d;
root.camera.translate(Qt.vector3d(-tx, -ty, 0).times(dt))
return;
}
if(actionAlt.active && actionRMB.active) { // zoom with alt + RMD
var d = (root.camera.viewCenter.minus(root.camera.position)).length() * 0.1;
var tz = axisMX.value * root.translateSpeed * d;
root.camera.translate(Qt.vector3d(0, 0, tz).times(dt), Camera.DontTranslateViewCenter)
return;
}
}
}
]
}

View file

@ -98,32 +98,42 @@ FocusScope {
}
}
MayaCameraController {
DefaultCameraController {
id: cameraController
camera: mainCamera
onMousePressed: {
mouse.accepted = false
scene3D.forceActiveFocus()
if(mouse.button == Qt.LeftButton)
{
if(!doubleClickTimer.running)
doubleClickTimer.restart()
}
else
doubleClickTimer.stop()
}
onMouseReleased: {
if(moving)
return;
switch(mouse.button) {
case Qt.LeftButton:
break;
case Qt.RightButton:
contextMenu.x = mouse.x;
contextMenu.y = mouse.y;
contextMenu.open();
break;
return
if(mouse.button == Qt.RightButton)
{
contextMenu.popup()
}
}
// Manually handle double click to activate object picking
// for camera re-centering only during a short amount of time
Timer {
id: doubleClickTimer
running: false
interval: 300
}
}
components: [
RenderSettings {
// To avoid performance drop, only pick triangles when not moving the camera
pickingSettings.pickMethod: cameraController.moving ? PickingSettings.BoundingVolumePicking : PickingSettings.TrianglePicking
// To avoid performance drops, picking is only enabled under certain circumstances (see ObjectPicker below)
pickingSettings.pickMethod: PickingSettings.TrianglePicking
pickingSettings.pickResultMode: PickingSettings.NearestPick
renderPolicy: RenderSettings.Always
activeFrameGraph: Viewport {
normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
@ -156,23 +166,17 @@ FocusScope {
components: [scene, transform, picker]
// ObjectPicker used for view re-centering
ObjectPicker {
id: picker
// Triangle picking is expensive
// Only activate it when a double click may happen or when the 'Control' key is pressed
enabled: cameraController.controlPressed || doubleClickTimer.running
hoverEnabled: false
onPressed: {
if(Qt.LeftButton & pick.buttons)
{
if(!doubleClickTimer.running)
doubleClickTimer.start()
else
if(pick.button == Qt.LeftButton)
mainCamera.viewCenter = pick.worldIntersection
}
}
Timer {
id: doubleClickTimer
running: false
interval: 400
doubleClickTimer.stop()
}
}
@ -239,7 +243,7 @@ FocusScope {
}
}
// menus
// Menu
Menu {
id: contextMenu
MenuItem {

View file

@ -2,6 +2,7 @@ module Viewer
Viewer2D 1.0 Viewer2D.qml
Viewer3D 1.0 Viewer3D.qml
DefaultCameraController 1.0 DefaultCameraController.qml
MayaCameraController 1.0 MayaCameraController.qml
Locator3D 1.0 Locator3D.qml
Grid3D 1.0 Grid3D.qml