mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-07-25 20:47:39 +02:00
[ui] Viewer3D: move TrackballController code to Python side
Port JS trackball camera controller code to Python for improved performance and stability.
This commit is contained in:
parent
bc9abe3b07
commit
b2e1743a6f
3 changed files with 69 additions and 46 deletions
|
@ -3,8 +3,9 @@ def registerTypes():
|
||||||
from PySide2.QtQml import qmlRegisterType
|
from PySide2.QtQml import qmlRegisterType
|
||||||
from meshroom.ui.components.edge import EdgeMouseArea
|
from meshroom.ui.components.edge import EdgeMouseArea
|
||||||
from meshroom.ui.components.filepath import FilepathHelper
|
from meshroom.ui.components.filepath import FilepathHelper
|
||||||
from meshroom.ui.components.scene3D import Scene3DHelper
|
from meshroom.ui.components.scene3D import Scene3DHelper, TrackballController
|
||||||
|
|
||||||
qmlRegisterType(EdgeMouseArea, "GraphEditor", 1, 0, "EdgeMouseArea")
|
qmlRegisterType(EdgeMouseArea, "GraphEditor", 1, 0, "EdgeMouseArea")
|
||||||
qmlRegisterType(FilepathHelper, "Meshroom.Helpers", 1, 0, "FilepathHelper") # TODO: uncreatable
|
qmlRegisterType(FilepathHelper, "Meshroom.Helpers", 1, 0, "FilepathHelper") # TODO: uncreatable
|
||||||
qmlRegisterType(Scene3DHelper, "Meshroom.Helpers", 1, 0, "Scene3DHelper") # TODO: uncreatable
|
qmlRegisterType(Scene3DHelper, "Meshroom.Helpers", 1, 0, "Scene3DHelper") # TODO: uncreatable
|
||||||
|
qmlRegisterType(TrackballController, "Meshroom.Helpers", 1, 0, "TrackballController")
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
from PySide2.QtCore import QObject, Slot
|
from math import acos, pi, sqrt
|
||||||
|
|
||||||
|
from PySide2.QtCore import QObject, Slot, QSize, Signal, QPointF
|
||||||
from PySide2.Qt3DCore import Qt3DCore
|
from PySide2.Qt3DCore import Qt3DCore
|
||||||
from PySide2.Qt3DRender import Qt3DRender
|
from PySide2.Qt3DRender import Qt3DRender
|
||||||
from PySide2.QtGui import QVector3D, QQuaternion
|
from PySide2.QtGui import QVector3D, QQuaternion, QVector2D
|
||||||
|
|
||||||
|
from meshroom.ui.utils import makeProperty
|
||||||
|
|
||||||
class Scene3DHelper(QObject):
|
class Scene3DHelper(QObject):
|
||||||
|
|
||||||
|
@ -42,7 +45,54 @@ class Scene3DHelper(QObject):
|
||||||
count += sum([attr.count() for attr in geo.attributes() if attr.name() == "vertexPosition"])
|
count += sum([attr.count() for attr in geo.attributes() if attr.name() == "vertexPosition"])
|
||||||
return count / 3
|
return count / 3
|
||||||
|
|
||||||
@Slot(QQuaternion, QVector3D, result=QVector3D)
|
|
||||||
def rotatedVector(self, quaternion, vector):
|
class TrackballController(QObject):
|
||||||
""" Returns the rotation of 'vector' with 'quaternion'. """
|
"""
|
||||||
return quaternion.rotatedVector(vector)
|
Trackball-like camera controller.
|
||||||
|
Based on the C++ version from https://github.com/cjmdaixi/Qt3DTrackball
|
||||||
|
"""
|
||||||
|
|
||||||
|
_windowSize = QSize()
|
||||||
|
_camera = None
|
||||||
|
_trackballSize = 1.0
|
||||||
|
_rotationSpeed = 5.0
|
||||||
|
|
||||||
|
def projectToTrackball(self, screenCoords):
|
||||||
|
sx = screenCoords.x()
|
||||||
|
sy = self._windowSize.height() - screenCoords.y()
|
||||||
|
p2d = QVector2D(sx / self._windowSize.width() - 0.5, sy / self._windowSize.height() - 0.5)
|
||||||
|
z = 0.0
|
||||||
|
r2 = pow(self._trackballSize, 2)
|
||||||
|
lengthSquared = p2d.lengthSquared()
|
||||||
|
if lengthSquared <= r2 * 0.5:
|
||||||
|
z = sqrt(r2 - lengthSquared)
|
||||||
|
else:
|
||||||
|
z = r2 * 0.5 / p2d.length()
|
||||||
|
return QVector3D(p2d.x(), p2d.y(), z)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def clamp(x):
|
||||||
|
return max(-1, min(x, 1))
|
||||||
|
|
||||||
|
def createRotation(self, firstPoint, nextPoint):
|
||||||
|
lastPos3D = self.projectToTrackball(firstPoint).normalized()
|
||||||
|
currentPos3D = self.projectToTrackball(nextPoint).normalized()
|
||||||
|
angle = acos(self.clamp(QVector3D.dotProduct(currentPos3D, lastPos3D)))
|
||||||
|
direction = QVector3D.crossProduct(currentPos3D, lastPos3D)
|
||||||
|
return angle, direction
|
||||||
|
|
||||||
|
@Slot(QPointF, QPointF, float)
|
||||||
|
def rotate(self, lastPosition, currentPosition, dt):
|
||||||
|
angle, direction = self.createRotation(lastPosition, currentPosition)
|
||||||
|
rotatedAxis = self._camera.transform().rotation().rotatedVector(direction)
|
||||||
|
angle *= self._rotationSpeed * dt
|
||||||
|
self._camera.rotateAboutViewCenter(QQuaternion.fromAxisAndAngle(rotatedAxis, angle * pi * 180))
|
||||||
|
|
||||||
|
windowSizeChanged = Signal()
|
||||||
|
windowSize = makeProperty(QSize, '_windowSize', windowSizeChanged)
|
||||||
|
cameraChanged = Signal()
|
||||||
|
camera = makeProperty(Qt3DRender.QCamera, '_camera', cameraChanged)
|
||||||
|
trackballSizeChanged = Signal()
|
||||||
|
trackballSize = makeProperty(float, '_trackballSize', trackballSizeChanged)
|
||||||
|
rotationSpeedChanged = Signal()
|
||||||
|
rotationSpeed = makeProperty(float, '_rotationSpeed', rotationSpeedChanged)
|
||||||
|
|
|
@ -5,6 +5,8 @@ import Qt3D.Input 2.1
|
||||||
import Qt3D.Logic 2.0
|
import Qt3D.Logic 2.0
|
||||||
import QtQml 2.2
|
import QtQml 2.2
|
||||||
|
|
||||||
|
import Meshroom.Helpers 1.0
|
||||||
|
|
||||||
Entity {
|
Entity {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
@ -15,9 +17,9 @@ Entity {
|
||||||
property bool moving: pressed || (actionAlt.active && keyboardHandler._pressed)
|
property bool moving: pressed || (actionAlt.active && keyboardHandler._pressed)
|
||||||
property alias focus: keyboardHandler.focus
|
property alias focus: keyboardHandler.focus
|
||||||
readonly property bool pickingActive: actionControl.active && keyboardHandler._pressed
|
readonly property bool pickingActive: actionControl.active && keyboardHandler._pressed
|
||||||
property real rotationSpeed: 2.0
|
property alias rotationSpeed: trackball.rotationSpeed
|
||||||
property size windowSize
|
property alias windowSize: trackball.windowSize
|
||||||
property real trackballSize: 1.0
|
property alias trackballSize: trackball.trackballSize
|
||||||
|
|
||||||
readonly property alias pressed: mouseHandler._pressed
|
readonly property alias pressed: mouseHandler._pressed
|
||||||
signal mousePressed(var mouse)
|
signal mousePressed(var mouse)
|
||||||
|
@ -29,6 +31,11 @@ Entity {
|
||||||
KeyboardDevice { id: keyboardSourceDevice }
|
KeyboardDevice { id: keyboardSourceDevice }
|
||||||
MouseDevice { id: mouseSourceDevice }
|
MouseDevice { id: mouseSourceDevice }
|
||||||
|
|
||||||
|
TrackballController {
|
||||||
|
id: trackball
|
||||||
|
camera: root.camera
|
||||||
|
}
|
||||||
|
|
||||||
MouseHandler {
|
MouseHandler {
|
||||||
id: mouseHandler
|
id: mouseHandler
|
||||||
property bool _pressed
|
property bool _pressed
|
||||||
|
@ -148,36 +155,6 @@ Entity {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on the C++ version from https://github.com/cjmdaixi/Qt3DTrackball
|
|
||||||
function projectToTrackball(screenCoords)
|
|
||||||
{
|
|
||||||
var sx = screenCoords.x, sy = windowSize.height - screenCoords.y;
|
|
||||||
var p2d = Qt.vector2d(sx / windowSize.width - 0.5, sy / windowSize.height - 0.5);
|
|
||||||
var z = 0.0;
|
|
||||||
var r2 = trackballSize * trackballSize;
|
|
||||||
var lengthSquared = p2d.length() * p2d.length();
|
|
||||||
if(lengthSquared <= r2 * 0.5)
|
|
||||||
z = Math.sqrt(r2 - lengthSquared);
|
|
||||||
else
|
|
||||||
z = r2 * 0.5 / p2d.length();
|
|
||||||
return Qt.vector3d(p2d.x, p2d.y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clamp(x)
|
|
||||||
{
|
|
||||||
return Math.max(-1, Math.min(x, 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
function createRotation(firstPoint, nextPoint)
|
|
||||||
{
|
|
||||||
var lastPos3D = projectToTrackball(firstPoint).normalized();
|
|
||||||
var currentPos3D = projectToTrackball(nextPoint).normalized();
|
|
||||||
var obj = {};
|
|
||||||
obj.angle = Math.acos(clamp(currentPos3D.dotProduct(lastPos3D)));
|
|
||||||
obj.dir = currentPos3D.crossProduct(lastPos3D);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
components: [
|
components: [
|
||||||
FrameAction {
|
FrameAction {
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
@ -189,12 +166,7 @@ Entity {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(actionLMB.active){ // trackball rotation
|
if(actionLMB.active){ // trackball rotation
|
||||||
var res = createRotation(mouseHandler.lastPosition, mouseHandler.currentPosition);
|
trackball.rotate(mouseHandler.lastPosition, mouseHandler.currentPosition, dt);
|
||||||
var transform = root.camera.components[1]; // transform is camera first component
|
|
||||||
var currentRotation = transform.rotation;
|
|
||||||
var rotatedAxis = Scene3DHelper.rotatedVector(transform.rotation, res.dir);
|
|
||||||
res.angle *= rotationSpeed * dt;
|
|
||||||
root.camera.rotateAboutViewCenter(transform.fromAxisAndAngle(rotatedAxis, res.angle * Math.PI * 180));
|
|
||||||
mouseHandler.lastPosition = mouseHandler.currentPosition;
|
mouseHandler.lastPosition = mouseHandler.currentPosition;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue