Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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 8, 2018
1 parent ce93338 commit 2f6b569
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 73 deletions.
1 change: 0 additions & 1 deletion CMakeLists.txt
Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion src/3d/CMakeLists.txt
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion src/3d/qgscameracontroller.cpp
Expand Up @@ -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 )
Expand All @@ -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" );
Expand Down
6 changes: 6 additions & 0 deletions src/3d/qgscameracontroller.h
Expand Up @@ -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
Expand Down
61 changes: 43 additions & 18 deletions src/app/3d/qgs3danimationsettings.cpp
Expand Up @@ -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();
}
14 changes: 10 additions & 4 deletions src/app/3d/qgs3danimationsettings.h
Expand Up @@ -18,6 +18,7 @@

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

namespace Qt3DCore
{
Expand All @@ -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;
Expand All @@ -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
73 changes: 39 additions & 34 deletions src/app/3d/qgs3danimationwidget.cpp
Expand Up @@ -17,8 +17,8 @@

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

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

Expand All @@ -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 );

Expand All @@ -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
Expand All @@ -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 );
}
Expand All @@ -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;
Expand All @@ -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 );

Expand Down Expand Up @@ -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() );
}
Expand All @@ -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 );
}
19 changes: 7 additions & 12 deletions src/app/3d/qgs3danimationwidget.h
Expand Up @@ -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;
Expand All @@ -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
2 changes: 1 addition & 1 deletion src/app/3d/qgs3dmapcanvasdockwidget.cpp
Expand Up @@ -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 )
Expand Down
1 change: 0 additions & 1 deletion src/app/CMakeLists.txt
Expand Up @@ -763,7 +763,6 @@ ENDIF(ENABLE_MODELTEST)
IF (WITH_3D)
TARGET_LINK_LIBRARIES(qgis_app
qgis_3d
Qt5::3DAnimation
)
ENDIF (WITH_3D)

Expand Down

0 comments on commit 2f6b569

Please sign in to comment.