From f614e63a990ced5f0396b23295f7e5f88b2cc29b Mon Sep 17 00:00:00 2001 From: Julien-Haudegond <44610840+Julien-Haudegond@users.noreply.github.com> Date: Fri, 7 Aug 2020 17:55:13 +0200 Subject: [PATCH] [doc] add a lot of developer's information --- meshroom/ui/components/scene3D.py | 51 ++++++- meshroom/ui/qml/Viewer3D/EntityWithGizmo.qml | 9 +- .../ui/qml/Viewer3D/MeshingBoundingBox.qml | 11 +- .../ui/qml/Viewer3D/SfMTransformGizmo.qml | 11 +- meshroom/ui/qml/Viewer3D/TransformGizmo.qml | 141 +++++++++++++++--- 5 files changed, 188 insertions(+), 35 deletions(-) diff --git a/meshroom/ui/components/scene3D.py b/meshroom/ui/components/scene3D.py index 9db47691d0..b4569c19f0 100644 --- a/meshroom/ui/components/scene3D.py +++ b/meshroom/ui/components/scene3D.py @@ -111,7 +111,14 @@ class Transformations3DHelper(QObject): @Slot(QVector4D, Qt3DRender.QCamera, QSize, result=QVector2D) def pointFromWorldToScreen(self, point, camera, windowSize): - """ Compute the Screen point corresponding to a World Point. """ + """ Compute the Screen point corresponding to a World Point. + Args: + point (QVector4D): point in world coordinates + camera (QCamera): camera viewing the scene + windowSize (QSize): size of the Scene3D window + Returns: + QVector2D: point in screen coordinates + """ # Transform the point from World Coord to Normalized Device Coord viewMatrix = camera.transform().matrix().inverted() projectedPoint = (camera.projectionMatrix() * viewMatrix[0]).map(point) @@ -130,7 +137,14 @@ def pointFromWorldToScreen(self, point, camera, windowSize): @Slot(Qt3DCore.QTransform, QMatrix4x4, QMatrix4x4, QMatrix4x4, QVector3D) def relativeLocalTranslate(self, transformQtInstance, initialPosMat, initialRotMat, initialScaleMat, translateVec): - """ Translate the QTransform in its local space relatively to an initial state. """ + """ Translate the QTransform in its local space relatively to an initial state. + Args: + transformQtInstance (QTransform): reference to the Transform to modify + initialPosMat (QMatrix4x4): initial position matrix + initialRotMat (QMatrix4x4): initial rotation matrix + initialScaleMat (QMatrix4x4): initial scale matrix + translateVec (QVector3D): vector used for the local translation + """ # Compute the translation transformation matrix translationMat = QMatrix4x4() translationMat.translate(translateVec) @@ -141,7 +155,15 @@ def relativeLocalTranslate(self, transformQtInstance, initialPosMat, initialRotM @Slot(Qt3DCore.QTransform, QMatrix4x4, QQuaternion, QMatrix4x4, QVector3D, int) def relativeLocalRotate(self, transformQtInstance, initialPosMat, initialRotQuat, initialScaleMat, axis, degree): - """ Rotate the QTransform in its local space relatively to an initial state. """ + """ Rotate the QTransform in its local space relatively to an initial state. + Args: + transformQtInstance (QTransform): reference to the Transform to modify + initialPosMat (QMatrix4x4): initial position matrix + initialRotQuat (QQuaternion): initial rotation quaternion + initialScaleMat (QMatrix4x4): initial scale matrix + axis (QVector3D): axis to rotate around + degree (int): angle of rotation in degree + """ # Compute the transformation quaternion from axis and angle in degrees transformQuat = QQuaternion.fromAxisAndAngle(axis, degree) @@ -155,7 +177,14 @@ def relativeLocalRotate(self, transformQtInstance, initialPosMat, initialRotQuat @Slot(Qt3DCore.QTransform, QMatrix4x4, QMatrix4x4, QMatrix4x4, QVector3D) def relativeLocalScale(self, transformQtInstance, initialPosMat, initialRotMat, initialScaleMat, scaleVec): - """ Scale the QTransform in its local space relatively to an initial state. """ + """ Scale the QTransform in its local space relatively to an initial state. + Args: + transformQtInstance (QTransform): reference to the Transform to modify + initialPosMat (QMatrix4x4): initial position matrix + initialRotMat (QMatrix4x4): initial rotation matrix + initialScaleMat (QMatrix4x4): initial scale matrix + scaleVec (QVector3D): vector used for the relative scale + """ # Make a copy of the scale matrix (otherwise, it is a reference and it does not work as expected) scaleMat = self.copyMatrix4x4(initialScaleMat) @@ -175,7 +204,12 @@ def relativeLocalScale(self, transformQtInstance, initialPosMat, initialRotMat, @Slot(QMatrix4x4, result="QVariant") def modelMatrixToMatrices(self, modelMat): - """ Decompose a model matrix into individual matrices. """ + """ Decompose a model matrix into individual matrices. + Args: + modelMat (QMatrix4x4): model matrix to decompose + Returns: + QVariant: object containing position, rotation and scale matrices + rotation quaternion + """ decomposition = self.decomposeModelMatrix(modelMat) posMat = QMatrix4x4() @@ -251,7 +285,12 @@ def copyMatrix4x4(self, mat): return newMat def decomposeModelMatrix(self, modelMat): - """ Decompose a model matrix into individual component. """ + """ Decompose a model matrix into individual component. + Args: + modelMat (QMatrix4x4): model matrix to decompose + Returns: + QVariant: object containing translation and scale vectors + rotation quaternion + """ translation = modelMat.column(3).toVector3D() quaternion = QQuaternion.fromDirection(modelMat.column(2).toVector3D(), modelMat.column(1).toVector3D()) scale = QVector3D(modelMat.column(0).length(), modelMat.column(1).length(), modelMat.column(2).length()) diff --git a/meshroom/ui/qml/Viewer3D/EntityWithGizmo.qml b/meshroom/ui/qml/Viewer3D/EntityWithGizmo.qml index 72ab7c3c73..b2cf95e58f 100644 --- a/meshroom/ui/qml/Viewer3D/EntityWithGizmo.qml +++ b/meshroom/ui/qml/Viewer3D/EntityWithGizmo.qml @@ -5,12 +5,19 @@ import Qt3D.Extras 2.10 import QtQuick 2.9 import Qt3D.Logic 2.0 +/** + * Wrapper for TransformGizmo. + * Must be instantiated to control an other entity. + * The goal is to instantiate the other entity inside this wrapper to gather the object and the gizmo. + * objectTranform is the component the other entity should use as a Transform. + */ + Entity { id: root property DefaultCameraController sceneCameraController property Layer frontLayerComponent property var window - property alias uniformScale: transformGizmo.uniformScale // by default, if not set, the value is: false + property alias uniformScale: transformGizmo.uniformScale // By default, if not set, the value is: false property TransformGizmo transformGizmo: TransformGizmo { id: transformGizmo camera: root.camera diff --git a/meshroom/ui/qml/Viewer3D/MeshingBoundingBox.qml b/meshroom/ui/qml/Viewer3D/MeshingBoundingBox.qml index b2e3e47cbc..e068f3f1f4 100644 --- a/meshroom/ui/qml/Viewer3D/MeshingBoundingBox.qml +++ b/meshroom/ui/qml/Viewer3D/MeshingBoundingBox.qml @@ -4,6 +4,10 @@ import Qt3D.Input 2.0 import Qt3D.Extras 2.10 import QtQuick 2.9 +/** + * BoundingBox entity for Meshing node. Used to define the area to reconstruct. + * Simple box controlled by a gizmo to give easy and visual representation. + */ Entity { id: root property DefaultCameraController sceneCameraController @@ -56,22 +60,25 @@ Entity { } } - // Automatically evaluate the Transform: value is taken from the node OR from the actual modification if the gizmo is moved by mouse. - // When the gizmo has changed (with mouse), the new values are set to the node, the priority is given back to the node and the Transform is re-evaluated once with those values. + // Translation values from node (vector3d because this is the type of QTransform.translation) property var nodeTranslation : Qt.vector3d( root.currentMeshingNode.attribute("boundingBox.bboxTranslation.x").value, root.currentMeshingNode.attribute("boundingBox.bboxTranslation.y").value, root.currentMeshingNode.attribute("boundingBox.bboxTranslation.z").value ) + // Rotation values from node (3 separated values because QTransform stores Euler angles like this) property var nodeRotationX: root.currentMeshingNode.attribute("boundingBox.bboxRotation.x").value property var nodeRotationY: root.currentMeshingNode.attribute("boundingBox.bboxRotation.y").value property var nodeRotationZ: root.currentMeshingNode.attribute("boundingBox.bboxRotation.z").value + // Scale values from node (vector3d because this is the type of QTransform.scale3D) property var nodeScale: Qt.vector3d( root.currentMeshingNode.attribute("boundingBox.bboxScale.x").value, root.currentMeshingNode.attribute("boundingBox.bboxScale.y").value, root.currentMeshingNode.attribute("boundingBox.bboxScale.z").value ) + // Automatically evaluate the Transform: value is taken from the node OR from the actual modification if the gizmo is moved by mouse. + // When the gizmo has changed (with mouse), the new values are set to the node, the priority is given back to the node and the Transform is re-evaluated once with those values. transformGizmo.gizmoDisplayTransform.translation: transformGizmo.focusGizmoPriority ? transformGizmo.gizmoDisplayTransform.translation : nodeTranslation transformGizmo.gizmoDisplayTransform.rotationX: transformGizmo.focusGizmoPriority ? transformGizmo.gizmoDisplayTransform.rotationX : nodeRotationX transformGizmo.gizmoDisplayTransform.rotationY: transformGizmo.focusGizmoPriority ? transformGizmo.gizmoDisplayTransform.rotationY : nodeRotationY diff --git a/meshroom/ui/qml/Viewer3D/SfMTransformGizmo.qml b/meshroom/ui/qml/Viewer3D/SfMTransformGizmo.qml index c312d4bfce..4844a32979 100644 --- a/meshroom/ui/qml/Viewer3D/SfMTransformGizmo.qml +++ b/meshroom/ui/qml/Viewer3D/SfMTransformGizmo.qml @@ -4,6 +4,10 @@ import Qt3D.Input 2.0 import Qt3D.Extras 2.10 import QtQuick 2.9 +/** + * Gizmo for SfMTransform node. + * Uses EntityWithGizmo wrapper because we should not instantiate TransformGizmo alone. + */ Entity { id: root property DefaultCameraController sceneCameraController @@ -60,18 +64,21 @@ Entity { } } - // Automatically evaluate the Transform: value is taken from the node OR from the actual modification if the gizmo is moved by mouse. - // When the gizmo has changed (with mouse), the new values are set to the node, the priority is given back to the node and the Transform is re-evaluated once with those values. + // Translation values from node (vector3d because this is the type of QTransform.translation) property var nodeTranslation : Qt.vector3d( root.currentSfMTransformNode.attribute("manualTransform.manualTranslation.x").value, root.currentSfMTransformNode.attribute("manualTransform.manualTranslation.y").value, root.currentSfMTransformNode.attribute("manualTransform.manualTranslation.z").value ) + // Rotation values from node (3 separated values because QTransform stores Euler angles like this) property var nodeRotationX: root.currentSfMTransformNode.attribute("manualTransform.manualRotation.x").value property var nodeRotationY: root.currentSfMTransformNode.attribute("manualTransform.manualRotation.y").value property var nodeRotationZ: root.currentSfMTransformNode.attribute("manualTransform.manualRotation.z").value + // Scale value from node (simple number because we use uniform scale) property var nodeScale: root.currentSfMTransformNode.attribute("manualTransform.manualScale").value + // Automatically evaluate the Transform: value is taken from the node OR from the actual modification if the gizmo is moved by mouse. + // When the gizmo has changed (with mouse), the new values are set to the node, the priority is given back to the node and the Transform is re-evaluated once with those values. transformGizmo.gizmoDisplayTransform.translation: transformGizmo.focusGizmoPriority ? transformGizmo.gizmoDisplayTransform.translation : nodeTranslation transformGizmo.gizmoDisplayTransform.rotationX: transformGizmo.focusGizmoPriority ? transformGizmo.gizmoDisplayTransform.rotationX : nodeRotationX transformGizmo.gizmoDisplayTransform.rotationY: transformGizmo.focusGizmoPriority ? transformGizmo.gizmoDisplayTransform.rotationY : nodeRotationY diff --git a/meshroom/ui/qml/Viewer3D/TransformGizmo.qml b/meshroom/ui/qml/Viewer3D/TransformGizmo.qml index 46aa91050c..05ada07948 100644 --- a/meshroom/ui/qml/Viewer3D/TransformGizmo.qml +++ b/meshroom/ui/qml/Viewer3D/TransformGizmo.qml @@ -7,19 +7,27 @@ import Qt3D.Logic 2.0 import QtQuick.Controls 2.3 import Utils 1.0 + +/** + * Simple transformation gizmo entirely made with Qt3D entities. + * Uses Python Transformations3DHelper to compute matrices. + * This TransformGizmo entity should only be instantiated in EntityWithGizmo entity which is its wrapper. + * It means, to use it for a specified application, make sure to instantiate EntityWithGizmo. + */ Entity { id: root - readonly property alias gizmoScale: gizmoScaleLookSlider.value property Camera camera property var windowSize - property var frontLayerComponent + property Layer frontLayerComponent // Used to draw gizmo on top of everything property var window - property bool uniformScale: false // by default, the scale is not uniform + readonly property alias gizmoScale: gizmoScaleLookSlider.value + property bool uniformScale: false // By default, the scale is not uniform property bool focusGizmoPriority: false // If true, it is used to give the priority to the current transformation (and not to a upper-level binding) property Transform gizmoDisplayTransform: Transform { id: gizmoDisplayTransform - scale: root.gizmoScale * (camera.position.minus(gizmoDisplayTransform.translation)).length() + scale: root.gizmoScale * (camera.position.minus(gizmoDisplayTransform.translation)).length() // The gizmo needs a constant apparent size } + // Component the object controlled by the gizmo must use property Transform objectTransform : Transform { translation: gizmoDisplayTransform.translation rotation: gizmoDisplayTransform.rotation @@ -75,21 +83,78 @@ Entity { /***** TRANSFORMATIONS (using local vars) *****/ + /** + * @brief Translate locally the gizmo and the object. + * + * @remarks + * To make local translation, we need to recompute a new matrix. + * Update gizmoDisplayTransform's matrix and all its properties while avoiding the override of translation property. + * Update objectTransform in the same time thanks to binding on translation property. + * + * @param initialModelMatrix object containing position, rotation and scale matrices + rotation quaternion + * @param translateVec vector3d used to make the local translation + */ function doRelativeTranslation(initialModelMatrix, translateVec) { - Transformations3DHelper.relativeLocalTranslate(gizmoDisplayTransform, initialModelMatrix.position, initialModelMatrix.rotation, initialModelMatrix.scale, translateVec) // Update gizmo matrix and object matrix with binding + Transformations3DHelper.relativeLocalTranslate( + gizmoDisplayTransform, + initialModelMatrix.position, + initialModelMatrix.rotation, + initialModelMatrix.scale, + translateVec + ) } + /** + * @brief Rotate the gizmo and the object around a specific axis. + * + * @remarks + * To make local rotation around an axis, we need to recompute a new matrix from a quaternion. + * Update gizmoDisplayTransform's matrix and all its properties while avoiding the override of rotation, rotationX, rotationY and rotationZ properties. + * Update objectTransform in the same time thanks to binding on rotation property. + * + * @param initialModelMatrix object containing position, rotation and scale matrices + rotation quaternion + * @param axis vector3d describing the axis to rotate around + * @param degree angle of rotation in degrees + */ function doRelativeRotation(initialModelMatrix, axis, degree) { - Transformations3DHelper.relativeLocalRotate(gizmoDisplayTransform, initialModelMatrix.position, initialModelMatrix.quaternion, initialModelMatrix.scale, axis, degree) // Update gizmo matrix and object matrix with binding + Transformations3DHelper.relativeLocalRotate( + gizmoDisplayTransform, + initialModelMatrix.position, + initialModelMatrix.quaternion, + initialModelMatrix.scale, + axis, + degree + ) } + /** + * @brief Scale the object relatively to its current scale. + * + * @remarks + * To change scale of the object, we need to recompute a new matrix to avoid overriding bindings. + * Update objectTransform properties only (gizmoDisplayTransform is not affected). + * + * @param initialModelMatrix object containing position, rotation and scale matrices + rotation quaternion + * @param scaleVec vector3d used to make the relative scale + */ function doRelativeScale(initialModelMatrix, scaleVec) { - Transformations3DHelper.relativeLocalScale(objectTransform, initialModelMatrix.position, initialModelMatrix.rotation, initialModelMatrix.scale, scaleVec) // Update only object matrix + Transformations3DHelper.relativeLocalScale( + objectTransform, + initialModelMatrix.position, + initialModelMatrix.rotation, + initialModelMatrix.scale, + scaleVec + ) } + /** + * @brief Reset the translation of the gizmo and the object. + * + * @remarks + * Update gizmoDisplayTransform's matrix and all its properties while avoiding the override of translation property. + * Update objectTransform in the same time thanks to binding on translation property. + */ function resetTranslation() { - // We have to reset the translation inside the matrix because we cannot override gizmoDisplayTransform.translation (because it can be used in upper-level binding) - // The object matrix will also be updated because of the binding with the translation property const mat = gizmoDisplayTransform.matrix const newMat = Qt.matrix4x4( mat.m11, mat.m12, mat.m13, 0, @@ -100,13 +165,30 @@ Entity { gizmoDisplayTransform.setMatrix(newMat) } + /** + * @brief Reset the rotation of the gizmo and the object. + * + * @remarks + * Update gizmoDisplayTransform's quaternion while avoiding the override of rotationX, rotationY and rotationZ properties. + * Update objectTransform in the same time thanks to binding on rotation property. + * Here, we can change the rotation property (but not rotationX, rotationY and rotationZ because they can be used in upper-level bindings). + * + * @note + * We could implement a way of changing the matrix instead of overriding rotation (quaternion) property. + */ function resetRotation() { - // Here, we can change the rotation property (but not rotationX, rotationY and rotationZ because they can be used in upper-level bindings) - gizmoDisplayTransform.rotation = Qt.quaternion(1,0,0,0) // Reset gizmo matrix and object matrix with binding + gizmoDisplayTransform.rotation = Qt.quaternion(1,0,0,0) } + /** + * @brief Reset the scale of the object. + * + * @remarks + * To reset the scale, we make the difference of the current one to 1 and recompute the matrix. + * Like this, we kind of apply an inverse scale transformation. + * It prevents overriding scale3D property (because it can be used in upper-level binding). + */ function resetScale() { - // We have to make the difference scale to 1 because we cannot override objectTransform.scale3D (because it can be used in upper-level binding) const modelMat = Transformations3DHelper.modelMatrixToMatrices(objectTransform.matrix) const scaleDiff = Qt.vector3d( -(objectTransform.scale3D.x - 1), @@ -133,9 +215,9 @@ Entity { // Get the selected axis const pickedAxis = convertAxisEnum(objectPicker.gizmoAxis) - // If it is a TRANSLATION or a SCALE + // TRANSLATION or SCALE transformation if(objectPicker.gizmoType === TransformGizmo.Type.TRANSLATION || objectPicker.gizmoType === TransformGizmo.Type.SCALE) { - // Compute the current vector PickedPoint -> CurrentMousePoint + // Compute the vector PickedPosition -> CurrentMousePoint const pickedPosition = objectPicker.screenPoint const mouseVector = Qt.vector2d(mouse.x - pickedPosition.x, -(mouse.y - pickedPosition.y)) @@ -147,6 +229,7 @@ Entity { const screenAxisVector = Qt.vector2d(screenPoint2D.x - screenCenter2D.x, -(screenPoint2D.y - screenCenter2D.y)) // Get the cosinus of the angle from the screenAxisVector to the mouseVector + // It will be used as a intensity factor const cosAngle = screenAxisVector.dotProduct(mouseVector) / (screenAxisVector.length() * mouseVector.length()) const offset = cosAngle * mouseVector.length() / objectPicker.scaleUnit @@ -163,15 +246,16 @@ Entity { return } + // ROTATION transformation else if(objectPicker.gizmoType === TransformGizmo.Type.ROTATION) { // Get Screen Coordinates of the gizmo center const gizmoCenterPoint = gizmoDisplayTransform.matrix.times(Qt.vector4d(0, 0, 0, 1)) const screenCenter2D = Transformations3DHelper.pointFromWorldToScreen(gizmoCenterPoint, camera, root.windowSize) - // Get the vector screenCenter2D -> PickedPoint + // Get the vector screenCenter2D -> PickedPosition const originalVector = Qt.vector2d(objectPicker.screenPoint.x - screenCenter2D.x, -(objectPicker.screenPoint.y - screenCenter2D.y)) - // Compute the current vector screenCenter2D -> CurrentMousePoint + // Compute the vector screenCenter2D -> CurrentMousePoint const mouseVector = Qt.vector2d(mouse.x - screenCenter2D.x, -(mouse.y - screenCenter2D.y)) // Get the angle from the originalVector to the mouseVector @@ -385,9 +469,12 @@ Entity { gizmoType: TransformGizmo.Type.SCALE onPickedChanged: { - this.modelMatrix = Transformations3DHelper.modelMatrixToMatrices(objectTransform.matrix) // Save the current transformations of the OBJECT - this.scaleUnit = Transformations3DHelper.computeScaleUnitFromModelMatrix(convertAxisEnum(gizmoAxis), gizmoDisplayTransform.matrix, camera, root.windowSize) // Compute a scale unit at picking time - root.pickedChanged(picker.isPressed) // Used to prevent camera transformations + // Save the current transformations of the OBJECT + this.modelMatrix = Transformations3DHelper.modelMatrixToMatrices(objectTransform.matrix) + // Compute a scale unit at picking time + this.scaleUnit = Transformations3DHelper.computeScaleUnitFromModelMatrix(convertAxisEnum(gizmoAxis), gizmoDisplayTransform.matrix, camera, root.windowSize) + // Prevent camera transformations + root.pickedChanged(picker.isPressed) } } } @@ -445,9 +532,12 @@ Entity { gizmoType: TransformGizmo.Type.TRANSLATION onPickedChanged: { - this.modelMatrix = Transformations3DHelper.modelMatrixToMatrices(objectTransform.matrix) // Save the current transformations of the OBJECT - this.scaleUnit = Transformations3DHelper.computeScaleUnitFromModelMatrix(convertAxisEnum(gizmoAxis), gizmoDisplayTransform.matrix, camera, root.windowSize) // Compute a scale unit at picking time - root.pickedChanged(picker.isPressed) // Used to prevent camera transformations + // Save the current transformations of the OBJECT + this.modelMatrix = Transformations3DHelper.modelMatrixToMatrices(objectTransform.matrix) + // Compute a scale unit at picking time + this.scaleUnit = Transformations3DHelper.computeScaleUnitFromModelMatrix(convertAxisEnum(gizmoAxis), gizmoDisplayTransform.matrix, camera, root.windowSize) + // Prevent camera transformations + root.pickedChanged(picker.isPressed) } } } @@ -491,8 +581,11 @@ Entity { gizmoType: TransformGizmo.Type.ROTATION onPickedChanged: { - this.modelMatrix = Transformations3DHelper.modelMatrixToMatrices(objectTransform.matrix) // Save the current transformations of the OBJECT - root.pickedChanged(picker.isPressed) // Used to prevent camera transformations + // Save the current transformations of the OBJECT + this.modelMatrix = Transformations3DHelper.modelMatrixToMatrices(objectTransform.matrix) + // No need to compute a scale unit for rotation + // Prevent camera transformations + root.pickedChanged(picker.isPressed) } } }