#ifndef ARCBALL_H #define ARCBALL_H #include #include #include #include #include using namespace Magnum; Quaternion ndcToArcBall(const Vector2 &p); /* Implementation of Ken Shoemake's arcball camera with smooth navigation feature: https://www.talisman.org/~erlkonig/misc/shoemake92-arcball.pdf */ class ArcBall { public: ArcBall(const Vector3 &cameraPosition, const Vector3 &viewCenter, const Vector3 &upDir, Deg fov, const Vector2i &windowSize); /* Set the camera view parameters: eye position, view center, up direction */ void setViewParameters(const Vector3 &eye, const Vector3 &viewCenter, const Vector3 &upDir); /* Reset the camera to its initial position, view center, and up dir */ void reset(); /* Update screen size after the window has been resized */ void reshape(const Vector2i &windowSize) { m_windowSize = windowSize; } /* Update any unfinished transformation due to lagging, return true if the camera matrices have changed */ bool updateTransformation(); /* Get/set the amount of lagging such that the camera will (slowly) smoothly navigate. Lagging must be in [0, 1) */ Float lagging() const { return m_lagging; } void setLagging(Float lagging); /* Initialize the first (screen) mouse position for camera transformation. This should be called in mouse pressed event. */ void initTransformation(const Vector2i &mousePos); /* Rotate the camera from the previous (screen) mouse position to the current (screen) position */ void rotate(const Vector2i &mousePos); /* Translate the camera from the previous (screen) mouse position to the current (screen) mouse position */ void translate(const Vector2i &mousePos); /* Translate the camera by the delta amount of (NDC) mouse position. Note that NDC position must be in [-1, -1] to [1, 1]. */ void translateDelta(const Vector2 &translationNDC); /* Zoom the camera (positive delta = zoom in, negative = zoom out) */ void zoom(Float delta); /* Get the camera's view transformation as a qual quaternion */ const DualQuaternion &view() const { return m_view; } /* Get the camera's view transformation as a matrix */ Matrix4 viewMatrix() const { return m_view.toMatrix(); } /* Get the camera's inverse view matrix (which also produces transformation of the camera) */ Matrix4 inverseViewMatrix() const { return m_inverseView.toMatrix(); } /* Get the camera's transformation as a dual quaternion */ const DualQuaternion &transformation() const { return m_inverseView; } /* Get the camera's transformation matrix */ Matrix4 transformationMatrix() const { return m_inverseView.toMatrix(); } /* Return the distance from the camera position to the center view */ Float viewDistance() const { return Math::abs(m_targetZooming); } protected: /* Update the camera transformations */ void updateInternalTransformations(); /* Transform from screen coordinate to NDC - normalized device coordinate. The top-left of the screen corresponds to [-1, 1] NDC, and the bottom right is [1, -1] NDC. */ Vector2 screenCoordToNDC(const Vector2i &mousePos) const; Deg m_fov; Vector2i m_windowSize; Vector2 m_prevMousePosNDC; Float m_lagging{}; Vector3 m_targetPosition, m_currentPosition, m_positionT0; Quaternion m_targetQRotation, m_currentQRotation, m_qRotationT0; Float m_targetZooming, m_currentZooming, m_zoomingT0; DualQuaternion m_view, m_inverseView; }; #endif