Skip to content
Permalink
Browse files

Animate in cooperation with QgsCameraController

This is cleaner than just updating QCamera's transform.
Now we interpolate values ourselves, without Qt3D Animation library.
  • Loading branch information
wonder-sk committed Jul 4, 2018
1 parent ce93338 commit 2f6b569c31e98e23e069f4594668514e43d0d332
@@ -315,7 +315,6 @@ IF(WITH_CORE)
FIND_PACKAGE(Qt53DInput REQUIRED)
FIND_PACKAGE(Qt53DLogic REQUIRED)
FIND_PACKAGE(Qt53DExtras REQUIRED)
FIND_PACKAGE(Qt53DAnimation REQUIRED)
SET(HAVE_3D TRUE) # used in qgsconfig.h
ENDIF (WITH_3D)
INCLUDE("cmake/modules/ECMQt4To5Porting.cmake")
@@ -135,7 +135,7 @@ INCLUDE_DIRECTORIES(SYSTEM

ADD_LIBRARY(qgis_3d SHARED ${QGIS_3D_SRCS} ${QGIS_3D_MOC_SRCS} ${QGIS_3D_HDRS} ${QGIS_3D_RCC_SRCS})

TARGET_LINK_LIBRARIES(qgis_3d Qt5::3DCore Qt5::3DRender Qt5::3DInput Qt5::3DLogic Qt5::3DExtras Qt5::3DAnimation)
TARGET_LINK_LIBRARIES(qgis_3d Qt5::3DCore Qt5::3DRender Qt5::3DInput Qt5::3DLogic Qt5::3DExtras)

GENERATE_EXPORT_HEADER(
qgis_3d
@@ -403,7 +403,7 @@ void QgsCameraController::setViewFromTop( float worldX, float worldY, float dist

QgsVector3D QgsCameraController::lookingAtPoint() const
{
return QgsVector3D( mCameraData.x, 0, mCameraData.y );
return QgsVector3D( mCameraData.x, mCameraData.elev, mCameraData.y );
}

void QgsCameraController::setLookingAtPoint( const QgsVector3D &point, float dist )
@@ -414,6 +414,12 @@ void QgsCameraController::setLookingAtPoint( const QgsVector3D &point, float dis
emit cameraChanged();
}

void QgsCameraController::setLookingAtPoint( const QgsVector3D &point, float distance, float pitch, float yaw )
{
setCameraData( point.x(), point.z(), point.y(), distance, pitch, yaw );
emit cameraChanged();
}

QDomElement QgsCameraController::writeXml( QDomDocument &doc ) const
{
QDomElement elemCamera = doc.createElement( "camera" );
@@ -72,6 +72,12 @@ class _3D_EXPORT QgsCameraController : public Qt3DCore::QEntity
//! Sets the point toward which the camera is looking - this is used when world origin changes (e.g. after terrain generator changes)
void setLookingAtPoint( const QgsVector3D &point, float distance = -1 );

void setLookingAtPoint( const QgsVector3D &point, float distance, float pitch, float yaw );

float distance() const { return mCameraData.dist; }
float pitch() const { return mCameraData.pitch; }
float yaw() const { return mCameraData.yaw; }

//! Writes camera configuration to the given DOM element
QDomElement writeXml( QDomDocument &doc ) const;
//! Reads camera configuration from the given DOM element
@@ -15,39 +15,64 @@

#include "qgs3danimationsettings.h"

#include <Qt3DAnimation/QKeyframeAnimation>
#include <Qt3DAnimation/QAnimationGroup>
#include <QEasingCurve>

Qgs3DAnimationSettings::Qgs3DAnimationSettings()
{

}

float Qgs3DAnimationSettings::duration() const
{
return mKeyframes.isEmpty() ? 0 : mKeyframes.constLast().time;
}

Qt3DAnimation::QKeyframeAnimation *Qgs3DAnimationSettings::createAnimation( Qt3DCore::QNode *parent ) const
Qgs3DAnimationSettings::Keyframe Qgs3DAnimationSettings::interpolate( float time ) const
{
Qt3DAnimation::QKeyframeAnimation *animation = new Qt3DAnimation::QKeyframeAnimation;
Q_ASSERT( !mKeyframes.isEmpty() );

QVector<float> framePositions;
QVector<Qt3DCore::QTransform *> transforms;
for ( const Keyframe &keyframe : mKeyframes )
if ( time < 0 )
{
return mKeyframes.first();
}
else if ( time >= duration() )
{
framePositions << keyframe.time;
Qt3DCore::QTransform *t = new Qt3DCore::QTransform( parent );
t->setTranslation( keyframe.position );
t->setRotation( keyframe.rotation );
transforms << t;
return mKeyframes.last();
}
else
{
for ( int i = 0; i < mKeyframes.size() - 1; i++ )
{
const Keyframe &k0 = mKeyframes.at( i );
const Keyframe &k1 = mKeyframes.at( i + 1 );
if ( time >= k0.time && time < k1.time )
{
float ip = ( time - k0.time ) / ( k1.time - k0.time );
float eIp = QEasingCurve( QEasingCurve::InOutQuad ).valueForProgress( ip );
float eIip = 1.0f - eIp;

animation->setKeyframes( transforms );
animation->setFramePositions( framePositions );
Keyframe kf;
kf.time = time;
kf.point.set( k0.point.x() * eIip + k1.point.x() * eIp,
k0.point.y() * eIip + k1.point.y() * eIp,
k0.point.z() * eIip + k1.point.z() * eIp );
kf.dist = k0.dist * eIip + k1.dist * eIp;
kf.pitch = k0.pitch * eIip + k1.pitch * eIp;

//animation->setTarget(cam->transform());
// animation->setEasing(QEasingCurve(QEasingCurve::InOutQuad));
// always use shorter angle
float yaw0 = fmod( k0.yaw, 360 ), yaw1 = fmod( k1.yaw, 360 );
if ( std::abs( yaw0 - yaw1 ) > 180 )
{
if ( yaw0 < yaw1 )
yaw0 += 360;
else
yaw1 += 360;
}

return animation;
kf.yaw = yaw0 * eIip + yaw1 * eIp;
return kf;
}
}
}
Q_ASSERT( false );
return Keyframe();
}
@@ -18,6 +18,7 @@

#include <QVector3D>
#include <QQuaternion>
#include "qgsvector3d.h"

namespace Qt3DCore
{
@@ -42,8 +43,11 @@ class Qgs3DAnimationSettings
struct Keyframe
{
float time; //!< Relative time of the keyframe in seconds
QVector3D position; //!< Position of the camera
QQuaternion rotation; //!< Rotation of the camera

QgsVector3D point; //!< Point towards which the camera is looking in 3D world coords
float dist; //!< Distance of the camera from the focal point
float pitch; //!< Tilt of the camera in degrees (0 = looking from the top, 90 = looking from the side, 180 = looking from the bottom)
float yaw; //!< Horizontal rotation around the focal point in degrees
};

typedef QVector<Keyframe> Keyframes;
@@ -54,13 +58,15 @@ class Qgs3DAnimationSettings
//! Returns duration of the whole animation in seconds
float duration() const;

//! Returns a new object that contains Qt3D animation according to the keyframes
Qt3DAnimation::QKeyframeAnimation *createAnimation( Qt3DCore::QNode *parent ) const;
//! Interpolates camera position and rotation at the given point in time
Keyframe interpolate( float time ) const;

// TODO: read/write routines

private:
Keyframes mKeyframes;
};

Q_DECLARE_METATYPE( Qgs3DAnimationSettings::Keyframe )

#endif // QGS3DANIMATIONSETTINGS_H
@@ -17,8 +17,8 @@

#include "qgs3danimationsettings.h"
#include "qgsapplication.h"
#include "qgscameracontroller.h"

#include <Qt3DAnimation/QAnimationController>
#include <Qt3DRender/QCamera>
#include <QTimer>

@@ -38,8 +38,6 @@ Qgs3DAnimationWidget::Qgs3DAnimationWidget( QWidget *parent )
mAnimationTimer->setInterval( 10 );
connect( mAnimationTimer, &QTimer::timeout, this, &Qgs3DAnimationWidget::onAnimationTimer );

mAnimationController = new Qt3DAnimation::QAnimationController( this );

btnPlayPause->setCheckable( true );
connect( btnPlayPause, &QToolButton::clicked, this, &Qgs3DAnimationWidget::onPlayPause );

@@ -48,12 +46,16 @@ Qgs3DAnimationWidget::Qgs3DAnimationWidget( QWidget *parent )
connect( cboKeyframe, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &Qgs3DAnimationWidget::onKeyframeChanged );
}

void Qgs3DAnimationWidget::setCamera( Qt3DRender::QCamera *camera )
Qgs3DAnimationWidget::~Qgs3DAnimationWidget() = default;


void Qgs3DAnimationWidget::setCameraController( QgsCameraController *cameraController )
{
mCamera = camera;
connect( mCamera, &Qt3DRender::QCamera::viewMatrixChanged, this, &Qgs3DAnimationWidget::onCameraViewMatrixChanged );
mCameraController = cameraController;
connect( mCameraController, &QgsCameraController::cameraChanged, this, &Qgs3DAnimationWidget::onCameraChanged );
}


void Qgs3DAnimationWidget::setAnimation( const Qgs3DAnimationSettings &animSettings )
{
// initialize GUI from the given animation
@@ -63,26 +65,15 @@ void Qgs3DAnimationWidget::setAnimation( const Qgs3DAnimationSettings &animSetti
{
cboKeyframe->addItem( QString( "%1 s" ).arg( keyframe.time ) );
int lastIndex = cboKeyframe->count() - 1;
cboKeyframe->setItemData( lastIndex, keyframe.time, Qt::UserRole + 1 );
cboKeyframe->setItemData( lastIndex, keyframe.position, Qt::UserRole + 2 );
cboKeyframe->setItemData( lastIndex, keyframe.rotation, Qt::UserRole + 3 );
cboKeyframe->setItemData( lastIndex, QVariant::fromValue<Qgs3DAnimationSettings::Keyframe>( keyframe ), Qt::UserRole + 1 );
}

initializeController( animSettings );
}

void Qgs3DAnimationWidget::initializeController( const Qgs3DAnimationSettings &animSettings )
{
// set up animation in the controller
Qt3DAnimation::QAnimationGroup *group = new Qt3DAnimation::QAnimationGroup;
Qt3DAnimation::QKeyframeAnimation *animation = animSettings.createAnimation( nullptr ); // TODO: who deletes transforms?
animation->setParent( group );
animation->setTarget( mCamera->transform() );
group->addAnimation( animation ); // does not delete animations later

QVector<Qt3DAnimation::QAnimationGroup *> groups;
groups << group;
mAnimationController->setAnimationGroups( groups ); // does not delete groups later
mAnimationSettings.reset( new Qgs3DAnimationSettings( animSettings ) );

sliderTime->setMaximum( animSettings.duration() * 100 );
}
@@ -91,15 +82,11 @@ Qgs3DAnimationSettings Qgs3DAnimationWidget::animation() const
{
Qgs3DAnimationSettings animSettings;
Qgs3DAnimationSettings::Keyframes keyframes;
qDebug() << "---";
for ( int i = 1; i < cboKeyframe->count(); ++i )
{
Qgs3DAnimationSettings::Keyframe kf;
kf.time = cboKeyframe->itemData( i, Qt::UserRole + 1 ).toFloat();
kf.position = cboKeyframe->itemData( i, Qt::UserRole + 2 ).value<QVector3D>();
kf.rotation = cboKeyframe->itemData( i, Qt::UserRole + 3 ).value<QQuaternion>();
kf = cboKeyframe->itemData( i, Qt::UserRole + 1 ).value<Qgs3DAnimationSettings::Keyframe>();
keyframes << kf;
qDebug() << "keyframe" << kf.time << kf.position << kf.rotation;
}
animSettings.setKeyframes( keyframes );
return animSettings;
@@ -111,11 +98,17 @@ void Qgs3DAnimationWidget::setDefaultAnimation()
Qgs3DAnimationSettings::Keyframes kf;
Qgs3DAnimationSettings::Keyframe f1, f2;
f1.time = 0;
f1.position = mCamera->transform()->translation();
f1.rotation = mCamera->transform()->rotation();
f1.point = mCameraController->lookingAtPoint();
f1.dist = mCameraController->distance();
f1.pitch = mCameraController->pitch();
f1.yaw = mCameraController->yaw();

f2.time = 5;
f2.position = f1.position + QVector3D( 0, 0, f1.position.z() / 2 );
f2.rotation = f1.rotation;
f2.point = f1.point;
f2.dist = f1.dist * 2;
f2.pitch = f1.pitch;
f2.yaw = f1.yaw;

kf << f1 << f2;
animSettings.setKeyframes( kf );

@@ -143,20 +136,30 @@ void Qgs3DAnimationWidget::onAnimationTimer()
sliderTime->setValue( sliderTime->value() >= duration ? 0 : sliderTime->value() + 1 );
}


void Qgs3DAnimationWidget::onSliderValueChanged()
{
mAnimationController->setPosition( sliderTime->value() / 100. );
// make sure we do not have an active keyframe
if ( cboKeyframe->currentIndex() != 0 )
cboKeyframe->setCurrentIndex( 0 );

Qgs3DAnimationSettings::Keyframe kf = mAnimationSettings->interpolate( sliderTime->value() / 100. );
mCameraController->setLookingAtPoint( kf.point, kf.dist, kf.pitch, kf.yaw );
}

void Qgs3DAnimationWidget::onCameraViewMatrixChanged()
void Qgs3DAnimationWidget::onCameraChanged()
{
if ( cboKeyframe->currentIndex() <= 0 )
return;

// update keyframe's camera position/rotation
int i = cboKeyframe->currentIndex();
cboKeyframe->setItemData( i, mCamera->transform()->translation(), Qt::UserRole + 2 );
cboKeyframe->setItemData( i, mCamera->transform()->rotation(), Qt::UserRole + 3 );
Qgs3DAnimationSettings::Keyframe kf = cboKeyframe->itemData( i, Qt::UserRole + 1 ).value<Qgs3DAnimationSettings::Keyframe>();
kf.point = mCameraController->lookingAtPoint();
kf.dist = mCameraController->distance();
kf.pitch = mCameraController->pitch();
kf.yaw = mCameraController->yaw();
cboKeyframe->setItemData( i, QVariant::fromValue<Qgs3DAnimationSettings::Keyframe>( kf ), Qt::UserRole + 1 );

initializeController( animation() );
}
@@ -167,6 +170,8 @@ void Qgs3DAnimationWidget::onKeyframeChanged()
return;

// jump to the camera view of the keyframe
float time = cboKeyframe->itemData( cboKeyframe->currentIndex(), Qt::UserRole + 1 ).toFloat();
sliderTime->setValue( time * 100 );
Qgs3DAnimationSettings::Keyframe kf = cboKeyframe->itemData( cboKeyframe->currentIndex(), Qt::UserRole + 1 ).value<Qgs3DAnimationSettings::Keyframe>();

whileBlocking( sliderTime )->setValue( kf.time * 100 );
mCameraController->setLookingAtPoint( kf.point, kf.dist, kf.pitch, kf.yaw );
}
@@ -17,27 +17,22 @@
#define QGS3DANIMATIONWIDGET_H

#include <QWidget>
#include <memory>

#include "ui_animation3dwidget.h"

namespace Qt3DRender
{
class QCamera;
}
namespace Qt3DAnimation
{
class QAnimationController;
}

class Qgs3DAnimationSettings;
class QgsCameraController;

class Qgs3DAnimationWidget : public QWidget, private Ui::Animation3DWidget
{
Q_OBJECT
public:
explicit Qgs3DAnimationWidget( QWidget *parent = nullptr );
~Qgs3DAnimationWidget();

void setCamera( Qt3DRender::QCamera *camera );
void setCameraController( QgsCameraController *cameraController );

void setAnimation( const Qgs3DAnimationSettings &animation );
Qgs3DAnimationSettings animation() const;
@@ -50,16 +45,16 @@ class Qgs3DAnimationWidget : public QWidget, private Ui::Animation3DWidget
void onPlayPause();
void onAnimationTimer();
void onSliderValueChanged();
void onCameraViewMatrixChanged();
void onCameraChanged();
void onKeyframeChanged();

private:
void initializeController( const Qgs3DAnimationSettings &animSettings );

private:
std::unique_ptr<Qgs3DAnimationSettings> mAnimationSettings;
QgsCameraController *mCameraController;
QTimer *mAnimationTimer = nullptr;
Qt3DAnimation::QAnimationController *mAnimationController = nullptr;
Qt3DRender::QCamera *mCamera = nullptr; //!< Camera (not owned)
};

#endif // QGS3DANIMATIONWIDGET_H
@@ -123,7 +123,7 @@ void Qgs3DMapCanvasDockWidget::setMapSettings( Qgs3DMapSettings *map )

connect( mCanvas->scene(), &Qgs3DMapScene::terrainPendingJobsCountChanged, this, &Qgs3DMapCanvasDockWidget::onTerrainPendingJobsCountChanged );

mAnimationWidget->setCamera( mCanvas->scene()->cameraController()->camera() );
mAnimationWidget->setCameraController( mCanvas->scene()->cameraController() );
}

void Qgs3DMapCanvasDockWidget::setMainCanvas( QgsMapCanvas *canvas )
@@ -763,7 +763,6 @@ ENDIF(ENABLE_MODELTEST)
IF (WITH_3D)
TARGET_LINK_LIBRARIES(qgis_app
qgis_3d
Qt5::3DAnimation
)
ENDIF (WITH_3D)

0 comments on commit 2f6b569

Please sign in to comment.
You can’t perform that action at this time.