From ae28a5477eafe9eb230c6c544e4719926ae31243 Mon Sep 17 00:00:00 2001 From: Yann Lanthony Date: Fri, 17 Nov 2017 19:02:24 +0100 Subject: [PATCH] [ui] add 3D viewer components --- meshroom/ui/qml/Viewer/Grid3D.qml | 58 +++++ meshroom/ui/qml/Viewer/Locator3D.qml | 56 +++++ .../ui/qml/Viewer/MayaCameraController.qml | 128 +++++++++++ meshroom/ui/qml/Viewer/Viewer3D.qml | 199 ++++++++++++++++++ meshroom/ui/qml/Viewer/qmldir | 5 + 5 files changed, 446 insertions(+) create mode 100644 meshroom/ui/qml/Viewer/Grid3D.qml create mode 100644 meshroom/ui/qml/Viewer/Locator3D.qml create mode 100644 meshroom/ui/qml/Viewer/MayaCameraController.qml create mode 100644 meshroom/ui/qml/Viewer/Viewer3D.qml diff --git a/meshroom/ui/qml/Viewer/Grid3D.qml b/meshroom/ui/qml/Viewer/Grid3D.qml new file mode 100644 index 00000000..4108765f --- /dev/null +++ b/meshroom/ui/qml/Viewer/Grid3D.qml @@ -0,0 +1,58 @@ +import QtQuick 2.7 +import Qt3D.Core 2.0 +import Qt3D.Render 2.0 +import Qt3D.Extras 2.0 + +// Grid +Entity { + id: gridEntity + components: [ + GeometryRenderer { + primitiveType: GeometryRenderer.Lines + geometry: Geometry { + Attribute { + id: gridPosition + attributeType: Attribute.VertexAttribute + vertexBaseType: Attribute.Float + vertexSize: 3 + count: 0 + name: defaultPositionAttributeName + buffer: Buffer { + type: Buffer.VertexBuffer + data: { + function buildGrid(first, last, offset, attribute) { + var vertexCount = (((last-first)/offset)+1)*4; + var f32 = new Float32Array(vertexCount*3); + for(var id = 0, i = first; i <= last; i += offset, id++) + { + f32[12*id] = i; + f32[12*id+1] = 0.0; + f32[12*id+2] = first; + + f32[12*id+3] = i; + f32[12*id+4] = 0.0; + f32[12*id+5] = last; + + f32[12*id+6] = first; + f32[12*id+7] = 0.0; + f32[12*id+8] = i; + + f32[12*id+9] = last; + f32[12*id+10] = 0.0; + f32[12*id+11] = i; + } + attribute.count = vertexCount; + return f32; + } + return buildGrid(-12, 12, 1, gridPosition); + } + } + } + boundingVolumePositionAttribute: gridPosition + } + }, + PhongMaterial { + ambient: Qt.rgba(0.4, 0.4, 0.4, 1) + } + ] +} diff --git a/meshroom/ui/qml/Viewer/Locator3D.qml b/meshroom/ui/qml/Viewer/Locator3D.qml new file mode 100644 index 00000000..32b31446 --- /dev/null +++ b/meshroom/ui/qml/Viewer/Locator3D.qml @@ -0,0 +1,56 @@ +import QtQuick 2.7 +import Qt3D.Core 2.0 +import Qt3D.Render 2.0 +import Qt3D.Extras 2.0 + +// Locator +Entity { + id: locatorEntity + components: [ + GeometryRenderer { + primitiveType: GeometryRenderer.Lines + geometry: Geometry { + Attribute { + id: locatorPosition + attributeType: Attribute.VertexAttribute + vertexBaseType: Attribute.Float + vertexSize: 3 + count: 6 + name: defaultPositionAttributeName + buffer: Buffer { + type: Buffer.VertexBuffer + data: Float32Array([ + 0.0, 0.001, 0.0, + 1.0, 0.001, 0.0, + 0.0, 0.001, 0.0, + 0.0, 1.001, 0.0, + 0.0, 0.001, 0.0, + 0.0, 0.001, 1.0 + ]) + } + } + Attribute { + attributeType: Attribute.VertexAttribute + vertexBaseType: Attribute.Float + vertexSize: 3 + count: 6 + name: defaultColorAttributeName + buffer: Buffer { + type: Buffer.VertexBuffer + data: Float32Array([ + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0 + ]) + } + } + boundingVolumePositionAttribute: locatorPosition + } + }, + PerVertexColorMaterial {}, + Transform { id: locatorTransform } + ] +} diff --git a/meshroom/ui/qml/Viewer/MayaCameraController.qml b/meshroom/ui/qml/Viewer/MayaCameraController.qml new file mode 100644 index 00000000..b5a5ea39 --- /dev/null +++ b/meshroom/ui/qml/Viewer/MayaCameraController.qml @@ -0,0 +1,128 @@ +import QtQuick 2.7 +import Qt3D.Core 2.1 +import Qt3D.Render 2.1 +import Qt3D.Input 2.1 +//import Qt3D.Extras 2.0 +import Qt3D.Logic 2.0 +import QtQml 2.2 + +Entity { + + id: root + property Camera camera + property real translateSpeed: 100.0 + property real tiltSpeed: 500.0 + property real panSpeed: 500.0 + property bool moving: false + + signal mousePressed(var mouse) + signal mouseReleased(var mouse) + signal mouseWheeled(var wheel) + + KeyboardDevice { id: keyboardSourceDevice } + MouseDevice { id: mouseSourceDevice; sensitivity: 0.1 } + + MouseHandler { + + sourceDevice: mouseSourceDevice + onPressed: mousePressed(mouse) + onReleased: mouseReleased(mouse) + onWheel: { + var d = (root.camera.viewCenter.minus(root.camera.position)).length() * 0.05; + 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: actionAlt + onActiveChanged: root.moving = active + 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(!actionAlt.active) + return; + 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(actionRMB.active) { // zoom + var d = (root.camera.viewCenter.minus(root.camera.position)).length() * 0.05; + var tz = axisMX.value * root.translateSpeed * d; + root.camera.translate(Qt.vector3d(0, 0, tz).times(dt), Camera.DontTranslateViewCenter) + return; + } + } + } + ] +} diff --git a/meshroom/ui/qml/Viewer/Viewer3D.qml b/meshroom/ui/qml/Viewer/Viewer3D.qml new file mode 100644 index 00000000..663207b2 --- /dev/null +++ b/meshroom/ui/qml/Viewer/Viewer3D.qml @@ -0,0 +1,199 @@ +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 + + +FocusScope { + + id: root + + // 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 loadModel(url) + { + modelLoader.source = Qt.resolvedUrl(url) + } + + 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 } + } + } + + MayaCameraController { + id: cameraController + camera: mainCamera + onMousePressed: scene3D.forceActiveFocus() + 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; + } + } + } + + components: [ + RenderSettings { + // To avoid performance drop, only pick triangles when not moving the camera + pickingSettings.pickMethod: cameraController.moving ? PickingSettings.BoundingVolumePicking : PickingSettings.TrianglePicking + 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) + } + //} + } + } + } + }, + InputSettings { + eventSource: _window + enabled: true + } + ] + + Entity { + id: modelLoader + property alias source: scene.source + components: [scene, transform, picker] + ObjectPicker { + id: picker + hoverEnabled: false + onPressed: { + if(Qt.LeftButton & pick.buttons) + { + if(!doubleClickTimer.running) + doubleClickTimer.start() + else + mainCamera.viewCenter = pick.worldIntersection + } + } + + Timer { + id: doubleClickTimer + running: false + interval: 400 + } + + } + + Transform { + id: transform + + } + SceneLoader { + id: scene + onStatusChanged: { + if(scene.status == SceneLoader.Ready) + unmirrorTextures(parent); + } + } + Locator3D {} + } + + Grid3D { } + Locator3D {} + + } + } + + // menus + Menu { + id: contextMenu + MenuItem { + text: "Reset camera position [F]" + onTriggered: { + resetCameraCenter(); + resetCameraPosition(); + } + } + } +} diff --git a/meshroom/ui/qml/Viewer/qmldir b/meshroom/ui/qml/Viewer/qmldir index 692dd4ae..d0091d75 100644 --- a/meshroom/ui/qml/Viewer/qmldir +++ b/meshroom/ui/qml/Viewer/qmldir @@ -1,3 +1,8 @@ module Viewer Viewer2D 1.0 Viewer2D.qml +Viewer3D 1.0 Viewer3D.qml +MayaCameraController 1.0 MayaCameraController.qml +Locator3D 1.0 Locator3D.qml +Grid3D 1.0 Grid3D.qml +