Skip to content

Commit 2f6b569

Browse files
committed
Animate in cooperation with QgsCameraController
This is cleaner than just updating QCamera's transform. Now we interpolate values ourselves, without Qt3D Animation library.
1 parent ce93338 commit 2f6b569

10 files changed

+114
-73
lines changed

CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,6 @@ IF(WITH_CORE)
315315
FIND_PACKAGE(Qt53DInput REQUIRED)
316316
FIND_PACKAGE(Qt53DLogic REQUIRED)
317317
FIND_PACKAGE(Qt53DExtras REQUIRED)
318-
FIND_PACKAGE(Qt53DAnimation REQUIRED)
319318
SET(HAVE_3D TRUE) # used in qgsconfig.h
320319
ENDIF (WITH_3D)
321320
INCLUDE("cmake/modules/ECMQt4To5Porting.cmake")

src/3d/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ INCLUDE_DIRECTORIES(SYSTEM
135135

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

138-
TARGET_LINK_LIBRARIES(qgis_3d Qt5::3DCore Qt5::3DRender Qt5::3DInput Qt5::3DLogic Qt5::3DExtras Qt5::3DAnimation)
138+
TARGET_LINK_LIBRARIES(qgis_3d Qt5::3DCore Qt5::3DRender Qt5::3DInput Qt5::3DLogic Qt5::3DExtras)
139139

140140
GENERATE_EXPORT_HEADER(
141141
qgis_3d

src/3d/qgscameracontroller.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ void QgsCameraController::setViewFromTop( float worldX, float worldY, float dist
403403

404404
QgsVector3D QgsCameraController::lookingAtPoint() const
405405
{
406-
return QgsVector3D( mCameraData.x, 0, mCameraData.y );
406+
return QgsVector3D( mCameraData.x, mCameraData.elev, mCameraData.y );
407407
}
408408

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

417+
void QgsCameraController::setLookingAtPoint( const QgsVector3D &point, float distance, float pitch, float yaw )
418+
{
419+
setCameraData( point.x(), point.z(), point.y(), distance, pitch, yaw );
420+
emit cameraChanged();
421+
}
422+
417423
QDomElement QgsCameraController::writeXml( QDomDocument &doc ) const
418424
{
419425
QDomElement elemCamera = doc.createElement( "camera" );

src/3d/qgscameracontroller.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ class _3D_EXPORT QgsCameraController : public Qt3DCore::QEntity
7272
//! Sets the point toward which the camera is looking - this is used when world origin changes (e.g. after terrain generator changes)
7373
void setLookingAtPoint( const QgsVector3D &point, float distance = -1 );
7474

75+
void setLookingAtPoint( const QgsVector3D &point, float distance, float pitch, float yaw );
76+
77+
float distance() const { return mCameraData.dist; }
78+
float pitch() const { return mCameraData.pitch; }
79+
float yaw() const { return mCameraData.yaw; }
80+
7581
//! Writes camera configuration to the given DOM element
7682
QDomElement writeXml( QDomDocument &doc ) const;
7783
//! Reads camera configuration from the given DOM element

src/app/3d/qgs3danimationsettings.cpp

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,39 +15,64 @@
1515

1616
#include "qgs3danimationsettings.h"
1717

18-
#include <Qt3DAnimation/QKeyframeAnimation>
19-
#include <Qt3DAnimation/QAnimationGroup>
18+
#include <QEasingCurve>
2019

2120
Qgs3DAnimationSettings::Qgs3DAnimationSettings()
2221
{
23-
2422
}
2523

2624
float Qgs3DAnimationSettings::duration() const
2725
{
2826
return mKeyframes.isEmpty() ? 0 : mKeyframes.constLast().time;
2927
}
3028

31-
Qt3DAnimation::QKeyframeAnimation *Qgs3DAnimationSettings::createAnimation( Qt3DCore::QNode *parent ) const
29+
Qgs3DAnimationSettings::Keyframe Qgs3DAnimationSettings::interpolate( float time ) const
3230
{
33-
Qt3DAnimation::QKeyframeAnimation *animation = new Qt3DAnimation::QKeyframeAnimation;
31+
Q_ASSERT( !mKeyframes.isEmpty() );
3432

35-
QVector<float> framePositions;
36-
QVector<Qt3DCore::QTransform *> transforms;
37-
for ( const Keyframe &keyframe : mKeyframes )
33+
if ( time < 0 )
34+
{
35+
return mKeyframes.first();
36+
}
37+
else if ( time >= duration() )
3838
{
39-
framePositions << keyframe.time;
40-
Qt3DCore::QTransform *t = new Qt3DCore::QTransform( parent );
41-
t->setTranslation( keyframe.position );
42-
t->setRotation( keyframe.rotation );
43-
transforms << t;
39+
return mKeyframes.last();
4440
}
41+
else
42+
{
43+
for ( int i = 0; i < mKeyframes.size() - 1; i++ )
44+
{
45+
const Keyframe &k0 = mKeyframes.at( i );
46+
const Keyframe &k1 = mKeyframes.at( i + 1 );
47+
if ( time >= k0.time && time < k1.time )
48+
{
49+
float ip = ( time - k0.time ) / ( k1.time - k0.time );
50+
float eIp = QEasingCurve( QEasingCurve::InOutQuad ).valueForProgress( ip );
51+
float eIip = 1.0f - eIp;
4552

46-
animation->setKeyframes( transforms );
47-
animation->setFramePositions( framePositions );
53+
Keyframe kf;
54+
kf.time = time;
55+
kf.point.set( k0.point.x() * eIip + k1.point.x() * eIp,
56+
k0.point.y() * eIip + k1.point.y() * eIp,
57+
k0.point.z() * eIip + k1.point.z() * eIp );
58+
kf.dist = k0.dist * eIip + k1.dist * eIp;
59+
kf.pitch = k0.pitch * eIip + k1.pitch * eIp;
4860

49-
//animation->setTarget(cam->transform());
50-
// animation->setEasing(QEasingCurve(QEasingCurve::InOutQuad));
61+
// always use shorter angle
62+
float yaw0 = fmod( k0.yaw, 360 ), yaw1 = fmod( k1.yaw, 360 );
63+
if ( std::abs( yaw0 - yaw1 ) > 180 )
64+
{
65+
if ( yaw0 < yaw1 )
66+
yaw0 += 360;
67+
else
68+
yaw1 += 360;
69+
}
5170

52-
return animation;
71+
kf.yaw = yaw0 * eIip + yaw1 * eIp;
72+
return kf;
73+
}
74+
}
75+
}
76+
Q_ASSERT( false );
77+
return Keyframe();
5378
}

src/app/3d/qgs3danimationsettings.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include <QVector3D>
2020
#include <QQuaternion>
21+
#include "qgsvector3d.h"
2122

2223
namespace Qt3DCore
2324
{
@@ -42,8 +43,11 @@ class Qgs3DAnimationSettings
4243
struct Keyframe
4344
{
4445
float time; //!< Relative time of the keyframe in seconds
45-
QVector3D position; //!< Position of the camera
46-
QQuaternion rotation; //!< Rotation of the camera
46+
47+
QgsVector3D point; //!< Point towards which the camera is looking in 3D world coords
48+
float dist; //!< Distance of the camera from the focal point
49+
float pitch; //!< Tilt of the camera in degrees (0 = looking from the top, 90 = looking from the side, 180 = looking from the bottom)
50+
float yaw; //!< Horizontal rotation around the focal point in degrees
4751
};
4852

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

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

6064
// TODO: read/write routines
6165

6266
private:
6367
Keyframes mKeyframes;
6468
};
6569

70+
Q_DECLARE_METATYPE( Qgs3DAnimationSettings::Keyframe )
71+
6672
#endif // QGS3DANIMATIONSETTINGS_H

src/app/3d/qgs3danimationwidget.cpp

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818
#include "qgs3danimationsettings.h"
1919
#include "qgsapplication.h"
20+
#include "qgscameracontroller.h"
2021

21-
#include <Qt3DAnimation/QAnimationController>
2222
#include <Qt3DRender/QCamera>
2323
#include <QTimer>
2424

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

41-
mAnimationController = new Qt3DAnimation::QAnimationController( this );
42-
4341
btnPlayPause->setCheckable( true );
4442
connect( btnPlayPause, &QToolButton::clicked, this, &Qgs3DAnimationWidget::onPlayPause );
4543

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

51-
void Qgs3DAnimationWidget::setCamera( Qt3DRender::QCamera *camera )
49+
Qgs3DAnimationWidget::~Qgs3DAnimationWidget() = default;
50+
51+
52+
void Qgs3DAnimationWidget::setCameraController( QgsCameraController *cameraController )
5253
{
53-
mCamera = camera;
54-
connect( mCamera, &Qt3DRender::QCamera::viewMatrixChanged, this, &Qgs3DAnimationWidget::onCameraViewMatrixChanged );
54+
mCameraController = cameraController;
55+
connect( mCameraController, &QgsCameraController::cameraChanged, this, &Qgs3DAnimationWidget::onCameraChanged );
5556
}
5657

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

7171
initializeController( animSettings );
7272
}
7373

7474
void Qgs3DAnimationWidget::initializeController( const Qgs3DAnimationSettings &animSettings )
7575
{
76-
// set up animation in the controller
77-
Qt3DAnimation::QAnimationGroup *group = new Qt3DAnimation::QAnimationGroup;
78-
Qt3DAnimation::QKeyframeAnimation *animation = animSettings.createAnimation( nullptr ); // TODO: who deletes transforms?
79-
animation->setParent( group );
80-
animation->setTarget( mCamera->transform() );
81-
group->addAnimation( animation ); // does not delete animations later
82-
83-
QVector<Qt3DAnimation::QAnimationGroup *> groups;
84-
groups << group;
85-
mAnimationController->setAnimationGroups( groups ); // does not delete groups later
76+
mAnimationSettings.reset( new Qgs3DAnimationSettings( animSettings ) );
8677

8778
sliderTime->setMaximum( animSettings.duration() * 100 );
8879
}
@@ -91,15 +82,11 @@ Qgs3DAnimationSettings Qgs3DAnimationWidget::animation() const
9182
{
9283
Qgs3DAnimationSettings animSettings;
9384
Qgs3DAnimationSettings::Keyframes keyframes;
94-
qDebug() << "---";
9585
for ( int i = 1; i < cboKeyframe->count(); ++i )
9686
{
9787
Qgs3DAnimationSettings::Keyframe kf;
98-
kf.time = cboKeyframe->itemData( i, Qt::UserRole + 1 ).toFloat();
99-
kf.position = cboKeyframe->itemData( i, Qt::UserRole + 2 ).value<QVector3D>();
100-
kf.rotation = cboKeyframe->itemData( i, Qt::UserRole + 3 ).value<QQuaternion>();
88+
kf = cboKeyframe->itemData( i, Qt::UserRole + 1 ).value<Qgs3DAnimationSettings::Keyframe>();
10189
keyframes << kf;
102-
qDebug() << "keyframe" << kf.time << kf.position << kf.rotation;
10390
}
10491
animSettings.setKeyframes( keyframes );
10592
return animSettings;
@@ -111,11 +98,17 @@ void Qgs3DAnimationWidget::setDefaultAnimation()
11198
Qgs3DAnimationSettings::Keyframes kf;
11299
Qgs3DAnimationSettings::Keyframe f1, f2;
113100
f1.time = 0;
114-
f1.position = mCamera->transform()->translation();
115-
f1.rotation = mCamera->transform()->rotation();
101+
f1.point = mCameraController->lookingAtPoint();
102+
f1.dist = mCameraController->distance();
103+
f1.pitch = mCameraController->pitch();
104+
f1.yaw = mCameraController->yaw();
105+
116106
f2.time = 5;
117-
f2.position = f1.position + QVector3D( 0, 0, f1.position.z() / 2 );
118-
f2.rotation = f1.rotation;
107+
f2.point = f1.point;
108+
f2.dist = f1.dist * 2;
109+
f2.pitch = f1.pitch;
110+
f2.yaw = f1.yaw;
111+
119112
kf << f1 << f2;
120113
animSettings.setKeyframes( kf );
121114

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

139+
146140
void Qgs3DAnimationWidget::onSliderValueChanged()
147141
{
148-
mAnimationController->setPosition( sliderTime->value() / 100. );
142+
// make sure we do not have an active keyframe
143+
if ( cboKeyframe->currentIndex() != 0 )
144+
cboKeyframe->setCurrentIndex( 0 );
145+
146+
Qgs3DAnimationSettings::Keyframe kf = mAnimationSettings->interpolate( sliderTime->value() / 100. );
147+
mCameraController->setLookingAtPoint( kf.point, kf.dist, kf.pitch, kf.yaw );
149148
}
150149

151-
void Qgs3DAnimationWidget::onCameraViewMatrixChanged()
150+
void Qgs3DAnimationWidget::onCameraChanged()
152151
{
153152
if ( cboKeyframe->currentIndex() <= 0 )
154153
return;
155154

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

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

169172
// jump to the camera view of the keyframe
170-
float time = cboKeyframe->itemData( cboKeyframe->currentIndex(), Qt::UserRole + 1 ).toFloat();
171-
sliderTime->setValue( time * 100 );
173+
Qgs3DAnimationSettings::Keyframe kf = cboKeyframe->itemData( cboKeyframe->currentIndex(), Qt::UserRole + 1 ).value<Qgs3DAnimationSettings::Keyframe>();
174+
175+
whileBlocking( sliderTime )->setValue( kf.time * 100 );
176+
mCameraController->setLookingAtPoint( kf.point, kf.dist, kf.pitch, kf.yaw );
172177
}

src/app/3d/qgs3danimationwidget.h

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,22 @@
1717
#define QGS3DANIMATIONWIDGET_H
1818

1919
#include <QWidget>
20+
#include <memory>
2021

2122
#include "ui_animation3dwidget.h"
2223

23-
namespace Qt3DRender
24-
{
25-
class QCamera;
26-
}
27-
namespace Qt3DAnimation
28-
{
29-
class QAnimationController;
30-
}
3124

3225
class Qgs3DAnimationSettings;
26+
class QgsCameraController;
3327

3428
class Qgs3DAnimationWidget : public QWidget, private Ui::Animation3DWidget
3529
{
3630
Q_OBJECT
3731
public:
3832
explicit Qgs3DAnimationWidget( QWidget *parent = nullptr );
33+
~Qgs3DAnimationWidget();
3934

40-
void setCamera( Qt3DRender::QCamera *camera );
35+
void setCameraController( QgsCameraController *cameraController );
4136

4237
void setAnimation( const Qgs3DAnimationSettings &animation );
4338
Qgs3DAnimationSettings animation() const;
@@ -50,16 +45,16 @@ class Qgs3DAnimationWidget : public QWidget, private Ui::Animation3DWidget
5045
void onPlayPause();
5146
void onAnimationTimer();
5247
void onSliderValueChanged();
53-
void onCameraViewMatrixChanged();
48+
void onCameraChanged();
5449
void onKeyframeChanged();
5550

5651
private:
5752
void initializeController( const Qgs3DAnimationSettings &animSettings );
5853

5954
private:
55+
std::unique_ptr<Qgs3DAnimationSettings> mAnimationSettings;
56+
QgsCameraController *mCameraController;
6057
QTimer *mAnimationTimer = nullptr;
61-
Qt3DAnimation::QAnimationController *mAnimationController = nullptr;
62-
Qt3DRender::QCamera *mCamera = nullptr; //!< Camera (not owned)
6358
};
6459

6560
#endif // QGS3DANIMATIONWIDGET_H

src/app/3d/qgs3dmapcanvasdockwidget.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ void Qgs3DMapCanvasDockWidget::setMapSettings( Qgs3DMapSettings *map )
123123

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

126-
mAnimationWidget->setCamera( mCanvas->scene()->cameraController()->camera() );
126+
mAnimationWidget->setCameraController( mCanvas->scene()->cameraController() );
127127
}
128128

129129
void Qgs3DMapCanvasDockWidget::setMainCanvas( QgsMapCanvas *canvas )

src/app/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,6 @@ ENDIF(ENABLE_MODELTEST)
763763
IF (WITH_3D)
764764
TARGET_LINK_LIBRARIES(qgis_app
765765
qgis_3d
766-
Qt5::3DAnimation
767766
)
768767
ENDIF (WITH_3D)
769768

0 commit comments

Comments
 (0)