Gui: Refactor navigation animations

- Animations are played through an Animator
- Standard animations have a fixed duration and inherit from QVariantAnimation
- The animation duration can be changed in the preferences
- Fix animations when using the two arrows above the NaviCube
- Start an animation or start and wait for an animation
- Replaces standard camera animations (e.g. by selecting standard views or NaviCube) with FixedTimeAnimation
- Replace View3DInventorViewer's CameraAnimation with FixedTimeAnimation
- Replace OpenInventor navigation style infinite spin animation with SpinningAnimation
- Stops an active animation when a new animation is started
- Stops an active animation when the user starts dragging, panning or zooming
- Refactor reorientCamera so it can be used in animations
- Enable animations by default
This commit is contained in:
Rexbas
2023-04-30 19:26:27 +02:00
parent 6da62b83fa
commit dc216cde2a
15 changed files with 677 additions and 355 deletions

View File

@@ -45,40 +45,13 @@
#include "Application.h"
#include "MenuManager.h"
#include "MouseSelection.h"
#include "NavigationAnimator.h"
#include "NavigationAnimation.h"
#include "SoMouseWheelEvent.h"
#include "View3DInventorViewer.h"
using namespace Gui;
namespace Gui {
struct NavigationStyleP {
int animationsteps;
int animationdelta;
SbVec3f focal1, focal2;
SbVec3f rotationCenter;
SbBool rotationCenterFound;
NavigationStyle::RotationCenterModes rotationCenterMode;
SbRotation endRotation;
SoTimerSensor * animsensor;
float sensitivity;
SbBool resetcursorpos;
NavigationStyleP()
{
this->animationsteps = 0;
this->animationdelta = 0;
this->animsensor = nullptr;
this->sensitivity = 2.0f;
this->resetcursorpos = false;
this->rotationCenterFound = false;
this->rotationCenterMode = NavigationStyle::RotationCenterMode::ScenePointAtCursor |
NavigationStyle::RotationCenterMode::FocalPointAtCursor;
}
static void viewAnimationCB(void * data, SoSensor * sensor);
};
}
class FCSphereSheetProjector : public SbSphereSheetProjector {
using inherited = SbSphereSheetProjector;
@@ -184,25 +157,19 @@ const Base::Type& NavigationStyleEvent::style() const
return t;
}
#define PRIVATE(ptr) (ptr->pimpl)
#define PUBLIC(ptr) (ptr->pub)
TYPESYSTEM_SOURCE_ABSTRACT(Gui::NavigationStyle,Base::BaseClass)
NavigationStyle::NavigationStyle() : viewer(nullptr), mouseSelection(nullptr)
{
PRIVATE(this) = new NavigationStyleP();
PRIVATE(this)->animsensor = new SoTimerSensor(NavigationStyleP::viewAnimationCB, this);
this->rotationCenterMode = NavigationStyle::RotationCenterMode::ScenePointAtCursor
| NavigationStyle::RotationCenterMode::FocalPointAtCursor;
initialize();
}
NavigationStyle::~NavigationStyle()
{
finalize();
if (PRIVATE(this)->animsensor->isScheduled())
PRIVATE(this)->animsensor->unschedule();
delete PRIVATE(this)->animsensor;
delete PRIVATE(this);
delete this->animator;
}
NavigationStyle& NavigationStyle::operator = (const NavigationStyle& ns)
@@ -222,12 +189,15 @@ void NavigationStyle::setViewer(View3DInventorViewer* view)
void NavigationStyle::initialize()
{
this->animator = new NavigationAnimator();
this->sensitivity = 2.0f;
this->resetcursorpos = false;
this->currentmode = NavigationStyle::IDLE;
this->prevRedrawTime = SbTime::getTimeOfDay();
this->spinanimatingallowed = true;
this->spinsamplecounter = 0;
this->spinincrement = SbRotation::identity();
this->spinRotation.setValue(SbVec3f(0, 0, -1), 0);
this->rotationCenterFound = false;
// FIXME: use a smaller sphere than the default one to have a larger
// area close to the borders that gives us "z-axis rotation"?
@@ -353,171 +323,71 @@ SbBool NavigationStyle::lookAtPoint(const SbVec2s screenpos)
SbVec3f hitpoint;
hitpoint = picked->getPoint();
lookAtPoint(hitpoint);
this->rotationCenterFound = false;
translateCamera(hitpoint - getFocalPoint());
return true;
}
void NavigationStyle::lookAtPoint(const SbVec3f& pos)
SoCamera* NavigationStyle::getCamera() const
{
SoCamera* cam = viewer->getSoRenderManager()->getCamera();
if (!cam)
return;
PRIVATE(this)->rotationCenterFound = false;
// Find global coordinates of focal point.
SbVec3f direction;
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
PRIVATE(this)->focal1 = cam->position.getValue() +
cam->focalDistance.getValue() * direction;
PRIVATE(this)->focal2 = pos;
// avoid to interfere with spinning (fixes #3101462)
if (this->isAnimating())
this->stopAnimating();
if (PRIVATE(this)->animsensor->isScheduled()) {
PRIVATE(this)->animsensor->unschedule();
this->interactiveCountDec();
}
if (isAnimationEnabled()) {
SbRotation cam_rot = cam->orientation.getValue();
// get the amount of movement
SbVec3f dir1 = direction, dir2;
dir2 = pos - cam->position.getValue();
dir2.normalize();
SbRotation rot(dir1, dir2);
float val = 0.5f*(1.0f + dir1.dot(dir2)); // value in range [0,1]
int div = (int)(val * 20.0f);
int steps = 20-div; // do it with max. 20 steps
// check whether a movement is required
if (steps > 0) {
PRIVATE(this)->endRotation = cam_rot;
this->spinRotation = cam_rot;
PRIVATE(this)->animationsteps = 5;
PRIVATE(this)->animationdelta = std::max<int>(100/steps, 5);
PRIVATE(this)->animsensor->setBaseTime(SbTime::getTimeOfDay());
PRIVATE(this)->animsensor->schedule();
this->interactiveCountInc();
}
else {
// set to the given position
SbVec3f direction;
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
cam->position = pos - cam->focalDistance.getValue() * direction;
}
}
else {
// set to the given position
SbVec3f direction;
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
cam->position = pos - cam->focalDistance.getValue() * direction;
}
return this->viewer->getCamera();
}
void NavigationStyle::setCameraOrientation(const SbRotation& rot, SbBool moveToCenter)
void NavigationStyle::setCameraOrientation(const SbRotation& orientation, SbBool moveToCenter)
{
SoCamera* cam = viewer->getSoRenderManager()->getCamera();
if (!cam)
SoCamera* camera = getCamera();
if (!camera)
return;
// Find global coordinates of focal point.
SbVec3f direction;
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
PRIVATE(this)->focal1 = cam->position.getValue() +
cam->focalDistance.getValue() * direction;
PRIVATE(this)->focal2 = PRIVATE(this)->focal1;
animator->stop();
SbVec3f focalPoint = getFocalPoint();
SbVec3f translation(0, 0, 0);
if (moveToCenter) {
SoGetBoundingBoxAction action(viewer->getSoRenderManager()->getViewportRegion());
action.apply(viewer->getSceneGraph());
SbBox3f box = action.getBoundingBox();
if (!box.isEmpty()) {
rot.multVec(SbVec3f(0, 0, -1), direction);
//float s = (this->focal1 - box.getCenter()).dot(direction);
//this->focal2 = box.getCenter() + s * direction;
// setting the center of the overall bounding box as the future focal point
// seems to be a satisfactory solution
PRIVATE(this)->focal2 = box.getCenter();
translation = box.getCenter() - focalPoint;
}
}
// avoid to interfere with spinning (fixes #3101462)
if (this->isAnimating())
this->stopAnimating();
if (PRIVATE(this)->animsensor->isScheduled()) {
PRIVATE(this)->animsensor->unschedule();
this->interactiveCountDec();
}
// Start an animation or set the pose directly
if (isAnimationEnabled()) {
// get the amount of movement
SbVec3f dir1, dir2;
SbRotation cam_rot = cam->orientation.getValue();
cam_rot.multVec(SbVec3f(0, 0, -1), dir1);
rot.multVec(SbVec3f(0, 0, -1), dir2);
float val = 0.5f*(1.0f + dir1.dot(dir2)); // value in range [0,1]
int div = (int)(val * 20.0f);
int steps = 20-div; // do it with max. 20 steps
// check whether a movement is required
if (steps > 0) {
PRIVATE(this)->endRotation = rot; // this is the final camera orientation
this->spinRotation = cam_rot;
PRIVATE(this)->animationsteps = 5;
PRIVATE(this)->animationdelta = std::max<int>(100/steps, 5);
PRIVATE(this)->animsensor->setBaseTime(SbTime::getTimeOfDay());
PRIVATE(this)->animsensor->schedule();
this->interactiveCountInc();
}
else {
// due to possible round-off errors make sure that the
// exact orientation is set
cam->orientation.setValue(rot);
cam->position = PRIVATE(this)->focal2 - cam->focalDistance.getValue() * direction;
}
viewer->startAnimation(orientation, focalPoint, translation);
}
else {
// set to the given rotation
cam->orientation.setValue(rot);
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
cam->position = PRIVATE(this)->focal2 - cam->focalDistance.getValue() * direction;
// Distance from rotation center to camera position in camera coordinate system
SbVec3f rotationCenterDistanceCam = camera->focalDistance.getValue() * SbVec3f(0, 0, 1);
// Set to the given orientation
camera->orientation = orientation;
// Distance from rotation center to new camera position in global coordinate system
SbVec3f newRotationCenterDistance;
camera->orientation.getValue().multVec(rotationCenterDistanceCam, newRotationCenterDistance);
// Reposition camera so the rotation center stays in the same place
// Optionally add translation to move to center
camera->position = focalPoint + newRotationCenterDistance + translation;
}
}
void NavigationStyleP::viewAnimationCB(void * data, SoSensor * sensor)
void NavigationStyle::translateCamera(const SbVec3f& translation)
{
Q_UNUSED(sensor);
auto that = static_cast<NavigationStyle*>(data);
if (PRIVATE(that)->animationsteps > 0) {
// here the camera rotates from the current rotation to a given
// rotation (e.g. the standard views). To get this movement animated
// we calculate an interpolated rotation and update the view after
// each step
float step = std::min<float>((float)PRIVATE(that)->animationsteps/100.0f, 1.0f);
SbRotation slerp = SbRotation::slerp(that->spinRotation, PRIVATE(that)->endRotation, step);
SbVec3f focalpoint = (1.0f-step)*PRIVATE(that)->focal1 + step*PRIVATE(that)->focal2;
SoCamera* cam = that->viewer->getSoRenderManager()->getCamera();
if (!cam) // no camera
return;
SoCamera* camera = getCamera();
if (!camera)
return;
SbVec3f direction;
cam->orientation.setValue(slerp);
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
cam->position = focalpoint - cam->focalDistance.getValue() * direction;
animator->stop();
PRIVATE(that)->animationsteps += PRIVATE(that)->animationdelta;
if (PRIVATE(that)->animationsteps > 100) {
// now we have reached the end of the movement
PRIVATE(that)->animationsteps=0;
PRIVATE(that)->animsensor->unschedule();
that->interactiveCountDec();
// set to the actual given rotation
cam->orientation.setValue(PRIVATE(that)->endRotation);
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
cam->position = PRIVATE(that)->focal2 - cam->focalDistance.getValue() * direction;
}
// Start an animation or set the pose directly
if (isAnimationEnabled()) {
viewer->startAnimation(camera->orientation.getValue(), SbVec3f(0, 0, 0), translation);
}
else {
camera->position = camera->position.getValue() + translation;
}
}
@@ -604,29 +474,41 @@ void NavigationStyle::viewAll()
}
}
/** Rotate the camera by the given amount, then reposition it so we're
* still pointing at the same focal point.
/** Rotate the camera by the given amount, then reposition it so we're still pointing at the same
* focal point
*/
void NavigationStyle::reorientCamera(SoCamera * cam, const SbRotation & rot)
void NavigationStyle::reorientCamera(SoCamera* camera, const SbRotation& rotation)
{
if (!cam)
reorientCamera(camera, rotation, getFocalPoint());
}
/** Rotate the camera by the given amount, then reposition it so the rotation center stays in the
* same place
*/
void NavigationStyle::reorientCamera(SoCamera* camera, const SbRotation& rotation, const SbVec3f& rotationCenter)
{
if (!camera) {
return;
// Find global coordinates of focal point.
SbVec3f direction;
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
SbVec3f focalpoint = cam->position.getValue() +
cam->focalDistance.getValue() * direction;
// Set new orientation value by accumulating the new rotation.
cam->orientation = rot * cam->orientation.getValue();
// Fix issue with near clipping in orthogonal view
if (cam->getTypeId().isDerivedFrom(SoOrthographicCamera::getClassTypeId())) {
cam->focalDistance = static_cast<SoOrthographicCamera*>(cam)->height;
}
// Reposition camera so we are still pointing at the same old focal point.
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
cam->position = focalpoint - cam->focalDistance.getValue() * direction;
// Distance from rotation center to camera position in camera coordinate system
SbVec3f rotationCenterDistanceCam;
camera->orientation.getValue().inverse().multVec(camera->position.getValue() - rotationCenter, rotationCenterDistanceCam);
// Set new orientation value by accumulating the new rotation
camera->orientation = rotation * camera->orientation.getValue();
// Fix issue with near clipping in orthogonal view
if (camera->getTypeId().isDerivedFrom(SoOrthographicCamera::getClassTypeId())) {
camera->focalDistance = static_cast<SoOrthographicCamera*>(camera)->height;
}
// Distance from rotation center to new camera position in global coordinate system
SbVec3f newRotationCenterDistance;
camera->orientation.getValue().multVec(rotationCenterDistanceCam, newRotationCenterDistance);
// Reposition camera so the rotation center stays in the same place
camera->position = rotationCenter + newRotationCenterDistance;
}
void NavigationStyle::panCamera(SoCamera * cam, float aspectratio, const SbPlane & panplane,
@@ -684,7 +566,7 @@ void NavigationStyle::panToCenter(const SbPlane & pplane, const SbVec2f & currpo
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
float ratio = vp.getViewportAspectRatio();
panCamera(viewer->getSoRenderManager()->getCamera(), ratio, pplane, SbVec2f(0.5,0.5), currpos);
PRIVATE(this)->rotationCenterFound = false;
this->rotationCenterFound = false;
}
/** Dependent on the camera type this will either shrink or expand the
@@ -695,6 +577,9 @@ void NavigationStyle::zoom(SoCamera * cam, float diffvalue)
{
if (!cam) // can happen for empty scenegraph
return;
animator->stop();
SoType t = cam->getTypeId();
SbName tname = t.getName();
@@ -862,14 +747,14 @@ void NavigationStyle::doRotate(SoCamera * camera, float angle, const SbVec2f& po
SbVec3f NavigationStyle::getRotationCenter(SbBool& found) const
{
found = PRIVATE(this)->rotationCenterFound;
return PRIVATE(this)->rotationCenter;
found = this->rotationCenterFound;
return this->rotationCenter;
}
void NavigationStyle::setRotationCenter(const SbVec3f& cnt)
{
PRIVATE(this)->rotationCenter = cnt;
PRIVATE(this)->rotationCenterFound = true;
this->rotationCenter = cnt;
this->rotationCenterFound = true;
}
SbVec3f NavigationStyle::getFocalPoint() const
@@ -901,8 +786,8 @@ void NavigationStyle::spin(const SbVec2f & pointerpos)
lastpos[0] = float(this->log.position[1][0]) / float(std::max((int)(glsize[0]-1), 1));
lastpos[1] = float(this->log.position[1][1]) / float(std::max((int)(glsize[1]-1), 1));
if (PRIVATE(this)->rotationCenterMode && PRIVATE(this)->rotationCenterFound) {
SbVec3f hitpoint = PRIVATE(this)->rotationCenter;
if (this->rotationCenterMode && this->rotationCenterFound) {
SbVec3f hitpoint = this->rotationCenter;
// set to the given position
SbVec3f direction;
@@ -929,7 +814,7 @@ void NavigationStyle::spin(const SbVec2f & pointerpos)
r.invert();
this->reorientCamera(viewer->getSoRenderManager()->getCamera(), r);
if (PRIVATE(this)->rotationCenterMode && PRIVATE(this)->rotationCenterFound) {
if (this->rotationCenterMode && this->rotationCenterFound) {
float ratio = vp.getViewportAspectRatio();
SbViewVolume vv = viewer->getSoRenderManager()->getCamera()->getViewVolume(vp.getViewportAspectRatio());
SbPlane panplane = vv.getPlane(viewer->getSoRenderManager()->getCamera()->focalDistance.getValue());
@@ -1015,7 +900,7 @@ SbBool NavigationStyle::doSpin()
float radians;
rot.getValue(axis, radians);
if ((radians > 0.01f) && (deltatime < 0.300)) {
this->spinRotation = rot;
viewer->startSpinningAnimation(axis, radians * 5);
return true;
}
}
@@ -1030,14 +915,14 @@ void NavigationStyle::saveCursorPosition(const SoEvent * const ev)
this->localPos = ev->getPosition();
// mode is WindowCenter
if (!PRIVATE(this)->rotationCenterMode) {
if (!this->rotationCenterMode) {
setRotationCenter(getFocalPoint());
}
//Option to get point on model (slow) or always on focal plane (fast)
//
// mode is ScenePointAtCursor to get exact point if possible
if (PRIVATE(this)->rotationCenterMode & NavigationStyle::RotationCenterMode::ScenePointAtCursor) {
if (this->rotationCenterMode & NavigationStyle::RotationCenterMode::ScenePointAtCursor) {
SoRayPickAction rpaction(viewer->getSoRenderManager()->getViewportRegion());
rpaction.setPoint(this->localPos);
rpaction.setRadius(viewer->getPickRadius());
@@ -1051,7 +936,7 @@ void NavigationStyle::saveCursorPosition(const SoEvent * const ev)
}
// mode is FocalPointAtCursor or a ScenePointAtCursor failed
if (PRIVATE(this)->rotationCenterMode & NavigationStyle::RotationCenterMode::FocalPointAtCursor) {
if (this->rotationCenterMode & NavigationStyle::RotationCenterMode::FocalPointAtCursor) {
// get the intersection point of the ray and the focal plane
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
float ratio = vp.getViewportAspectRatio();
@@ -1072,7 +957,7 @@ void NavigationStyle::saveCursorPosition(const SoEvent * const ev)
}
// mode is BoundingBoxCenter or a ScenePointAtCursor failed
if (PRIVATE(this)->rotationCenterMode & NavigationStyle::RotationCenterMode::BoundingBoxCenter) {
if (this->rotationCenterMode & NavigationStyle::RotationCenterMode::BoundingBoxCenter) {
const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
float ratio = vp.getViewportAspectRatio();
@@ -1128,20 +1013,6 @@ void NavigationStyle::moveCursorPosition()
}
}
void NavigationStyle::updateAnimation()
{
SbTime now = SbTime::getTimeOfDay();
double secs = now.getValue() - prevRedrawTime.getValue();
this->prevRedrawTime = now;
if (this->isAnimating()) {
// here the camera rotates around a fix axis
SbRotation deltaRotation = this->spinRotation;
deltaRotation.scaleAngle(secs * 5.0);
this->reorientCamera(viewer->getSoRenderManager()->getCamera(), deltaRotation);
}
}
void NavigationStyle::redraw()
{
if (mouseSelection)
@@ -1168,7 +1039,7 @@ void
NavigationStyle::setAnimationEnabled(const SbBool enable)
{
this->spinanimatingallowed = enable;
if (!enable && this->isAnimating()) { this->stopAnimating(); }
if (!enable && this->isAnimating()) { animator->stop(); }
}
/*!
@@ -1191,52 +1062,29 @@ SbBool NavigationStyle::isAnimating() const
return this->currentmode == NavigationStyle::SPINNING;
}
/*!
* Starts programmatically the viewer in animation mode. The given axis direction
* is always in screen coordinates, not in world coordinates.
*/
void NavigationStyle::startAnimating(const SbVec3f& axis, float velocity)
NavigationAnimator* NavigationStyle::getAnimator() const
{
if (!isAnimationEnabled())
return;
this->prevRedrawTime = SbTime::getTimeOfDay();
this->spinincrement = SbRotation::identity();
SbRotation rot;
rot.setValue(axis, velocity);
this->setViewing(true);
this->setViewingMode(NavigationStyle::SPINNING);
this->spinRotation = rot;
}
void NavigationStyle::stopAnimating()
{
if (this->currentmode != NavigationStyle::SPINNING) {
return;
}
this->setViewingMode(this->isViewing() ?
NavigationStyle::IDLE : NavigationStyle::INTERACT);
return this->animator;
}
void NavigationStyle::setSensitivity(float val)
{
PRIVATE(this)->sensitivity = val;
this->sensitivity = val;
}
float NavigationStyle::getSensitivity() const
{
return PRIVATE(this)->sensitivity;
return this->sensitivity;
}
void NavigationStyle::setResetCursorPosition(SbBool on)
{
PRIVATE(this)->resetcursorpos = on;
this->resetcursorpos = on;
}
SbBool NavigationStyle::isResetCursorPosition() const
{
return PRIVATE(this)->resetcursorpos;
return this->resetcursorpos;
}
void NavigationStyle::setZoomInverted(SbBool on)
@@ -1266,12 +1114,12 @@ SbBool NavigationStyle::isZoomAtCursor() const
void NavigationStyle::setRotationCenterMode(NavigationStyle::RotationCenterModes mode)
{
PRIVATE(this)->rotationCenterMode = mode;
this->rotationCenterMode = mode;
}
NavigationStyle::RotationCenterModes NavigationStyle::getRotationCenterMode() const
{
return PRIVATE(this)->rotationCenterMode;
return this->rotationCenterMode;
}
void NavigationStyle::startSelection(AbstractMouseSelection* mouse)
@@ -1415,6 +1263,7 @@ void NavigationStyle::setViewingMode(const ViewerMode newmode)
case DRAGGING:
// Set up initial projection point for the projector object when
// first starting a drag operation.
animator->stop();
viewer->showRotationCenter(true);
this->spinprojector->project(this->lastmouseposition);
this->interactiveCountInc();
@@ -1428,15 +1277,18 @@ void NavigationStyle::setViewingMode(const ViewerMode newmode)
break;
case PANNING:
animator->stop();
pan(viewer->getSoRenderManager()->getCamera());
this->interactiveCountInc();
break;
case ZOOMING:
animator->stop();
this->interactiveCountInc();
break;
case BOXZOOM:
animator->stop();
this->interactiveCountInc();
break;