Meshroom/meshroom/ui/qml/Viewer/Viewer3D.qml
Yann Lanthony ecc376e3b6 [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
2017-12-12 19:10:18 +01:00

258 lines
8.4 KiB
QML

import QtQuick 2.7
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import QtQuick.Scene3D 2.0
import Qt3D.Core 2.1
import Qt3D.Render 2.1
import Qt3D.Input 2.1 as Qt3DInput // to avoid clash with Controls2 Action
FocusScope {
id: root
property alias status: scene.status
property alias source: modelLoader.source
readonly property alias loading: modelLoader.loading
// functions
function resetCameraCenter() {
mainCamera.viewCenter = Qt.vector3d(0.0, 0.0, 0.0);
mainCamera.upVector = Qt.vector3d(0.0, 1.0, 0.0);
}
function resetCameraPosition() {
mainCamera.position = Qt.vector3d(28.0, 21.0, 28.0);
mainCamera.upVector = Qt.vector3d(0.0, 1.0, 0.0);
mainCamera.viewCenter = Qt.vector3d(0.0, 0.0, 0.0);
}
function findChildrenByProperty(node, propertyName, container)
{
if(!node || !node.childNodes)
return;
for(var i=0; i < node.childNodes.length; ++i)
{
var childNode = node.childNodes[i];
if(!childNode)
continue;
if(childNode[propertyName] !== undefined)
container.push(childNode);
else
findChildrenByProperty(childNode, propertyName, container)
}
}
function unmirrorTextures(rootEntity)
{
var materials = [];
findChildrenByProperty(rootEntity, "diffuse", materials);
var textures = [];
materials.forEach(function(mat){
mat["diffuse"].magnificationFilter = Texture.Linear;
findChildrenByProperty(mat["diffuse"], "mirrored", textures)
})
//console.log(textures)
textures.forEach(function(tex){
//console.log("Unmirroring: " + tex.source)
tex.mirrored = false
})
}
function clear()
{
source = 'no_file'
}
Scene3D {
id: scene3D
anchors.fill: parent
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio // vs. UserAspectRatio
hoverEnabled: false // if true, will trigger positionChanged events in attached MouseHandler
aspects: ["logic", "input"]
focus: true
Keys.onPressed: {
if (event.key == Qt.Key_F) {
resetCameraCenter();
resetCameraPosition();
event.accepted = true;
}
}
Entity {
id: rootEntity
Camera {
id: mainCamera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
nearPlane : 0.1
farPlane : 1000.0
position: Qt.vector3d(28.0, 21.0, 28.0)
upVector: Qt.vector3d(0.0, 1.0, 0.0)
viewCenter: Qt.vector3d(0.0, 0.0, 0.0)
aspectRatio: width/height
Behavior on viewCenter {
Vector3dAnimation { duration: 250 }
}
}
DefaultCameraController {
id: cameraController
camera: mainCamera
onMousePressed: {
scene3D.forceActiveFocus()
if(mouse.button == Qt.LeftButton)
{
if(!doubleClickTimer.running)
doubleClickTimer.restart()
}
else
doubleClickTimer.stop()
}
onMouseReleased: {
if(moving)
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 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)
RenderSurfaceSelector {
CameraSelector {
id: cameraSelector
camera: mainCamera
//FrustumCulling {
ClearBuffers {
buffers : ClearBuffers.ColorDepthBuffer
clearColor: Qt.rgba(0, 0, 0, 0.1)
}
//}
}
}
}
},
Qt3DInput.InputSettings {
eventSource: _window
enabled: true
}
]
Entity {
id: modelLoader
property string source
// SceneLoader status is not reliable when loading a 3D file
property bool loading
onSourceChanged: loading = true
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(pick.button == Qt.LeftButton)
mainCamera.viewCenter = pick.worldIntersection
doubleClickTimer.stop()
}
}
Transform {
id: transform
}
SceneLoader {
id: scene
source: Qt.resolvedUrl(modelLoader.source)
onStatusChanged: {
if(scene.status != SceneLoader.Loading)
modelLoader.loading = false;
if(scene.status == SceneLoader.Ready)
{
unmirrorTextures(parent);
}
}
}
Locator3D { enabled: locatorCheckBox.checked}
}
Grid3D { enabled: gridCheckBox.checked }
}
}
Pane {
background: Rectangle { color: palette.base; opacity: 0.5; radius: 2 }
Column {
spacing: 5
Row {
spacing: 4
Slider { width: 100; from: -180; to: 180; onPositionChanged: transform.rotationX = value}
Label { text: "RX" }
}
Row {
spacing: 4
Slider { width: 100; from: -180; to: 180; onPositionChanged: transform.rotationY = value }
Label { text: "RY" }
}
Row {
spacing: 4
Slider { width: 100; from: -180; to: 180; onPositionChanged: transform.rotationZ = value }
Label { text: "RZ" }
}
Row {
spacing: 4
Slider { width: 100; from: 1; to: 10; onPositionChanged: transform.scale = value }
Label { text: "Scale" }
}
}
}
Pane {
background: Rectangle { color: palette.base; opacity: 0.5; radius: 2 }
anchors.right: parent.right
Column {
CheckBox { id: gridCheckBox; text: "Grid"; checked: true }
CheckBox { id: locatorCheckBox; text: "Locator"; checked: true }
}
}
// Menu
Menu {
id: contextMenu
MenuItem {
text: "View All"
onTriggered: mainCamera.viewAll()
}
MenuItem {
text: "Reset View"
onTriggered: resetCameraPosition()
}
}
}