[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:
Yann Lanthony 2018-12-11 16:38:37 +01:00
parent bc9abe3b07
commit b2e1743a6f
3 changed files with 69 additions and 46 deletions

View file

@ -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.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):
@ -42,7 +45,54 @@ class Scene3DHelper(QObject):
count += sum([attr.count() for attr in geo.attributes() if attr.name() == "vertexPosition"])
return count / 3
@Slot(QQuaternion, QVector3D, result=QVector3D)
def rotatedVector(self, quaternion, vector):
""" Returns the rotation of 'vector' with 'quaternion'. """
return quaternion.rotatedVector(vector)
class TrackballController(QObject):
"""
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)