Skip to content
Permalink
Browse files

Initial work on animation support

- new class to store animation configuration
- new class for animation configuration GUI
- animation implementation using Qt3D Animation framework
  • Loading branch information
wonder-sk committed Jul 8, 2018
1 parent 059d0e2 commit ce933380c09e6e048154c29dfb59a46e8d32c74e
@@ -315,6 +315,7 @@ 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)
TARGET_LINK_LIBRARIES(qgis_3d Qt5::3DCore Qt5::3DRender Qt5::3DInput Qt5::3DLogic Qt5::3DExtras Qt5::3DAnimation)

GENERATE_EXPORT_HEADER(
qgis_3d
@@ -0,0 +1,53 @@
/***************************************************************************
qgs3danimationsettings.cpp
--------------------------------------
Date : July 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgs3danimationsettings.h"

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

Qgs3DAnimationSettings::Qgs3DAnimationSettings()
{

}

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

Qt3DAnimation::QKeyframeAnimation *Qgs3DAnimationSettings::createAnimation( Qt3DCore::QNode *parent ) const
{
Qt3DAnimation::QKeyframeAnimation *animation = new Qt3DAnimation::QKeyframeAnimation;

QVector<float> framePositions;
QVector<Qt3DCore::QTransform *> transforms;
for ( const Keyframe &keyframe : mKeyframes )
{
framePositions << keyframe.time;
Qt3DCore::QTransform *t = new Qt3DCore::QTransform( parent );
t->setTranslation( keyframe.position );
t->setRotation( keyframe.rotation );
transforms << t;
}

animation->setKeyframes( transforms );
animation->setFramePositions( framePositions );

//animation->setTarget(cam->transform());
// animation->setEasing(QEasingCurve(QEasingCurve::InOutQuad));

return animation;
}
@@ -0,0 +1,66 @@
/***************************************************************************
qgs3danimationsettings.h
--------------------------------------
Date : July 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGS3DANIMATIONSETTINGS_H
#define QGS3DANIMATIONSETTINGS_H

#include <QVector3D>
#include <QQuaternion>

namespace Qt3DCore
{
class QNode;
}

namespace Qt3DAnimation
{
class QKeyframeAnimation;
}

/**
* Class that holds information about animation in 3D view. The animation is defined
* as a series of keyframes
*/
class Qgs3DAnimationSettings
{
public:
Qgs3DAnimationSettings();

//! keyframe definition
struct Keyframe
{
float time; //!< Relative time of the keyframe in seconds
QVector3D position; //!< Position of the camera
QQuaternion rotation; //!< Rotation of the camera
};

typedef QVector<Keyframe> Keyframes;

void setKeyframes( const Keyframes &keyframes ) { mKeyframes = keyframes; }
Keyframes keyFrames() const { return mKeyframes; }

//! 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;

// TODO: read/write routines

private:
Keyframes mKeyframes;
};

#endif // QGS3DANIMATIONSETTINGS_H
@@ -0,0 +1,172 @@
/***************************************************************************
qgs3danimationwidget.cpp
--------------------------------------
Date : July 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgs3danimationwidget.h"

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

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

Qgs3DAnimationWidget::Qgs3DAnimationWidget( QWidget *parent )
: QWidget( parent )
{
setupUi( this );

btnAddKeyframe->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
btnRemoveKeyframe->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
btnEditKeyframe->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.svg" ) ) );
btnPlayPause->setIcon( QIcon( QgsApplication::iconPath( "mTaskRunning.svg" ) ) );

cboKeyframe->addItem( tr( "<none>" ) );

mAnimationTimer = new QTimer( this );
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 );

connect( sliderTime, &QSlider::valueChanged, this, &Qgs3DAnimationWidget::onSliderValueChanged );

connect( cboKeyframe, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &Qgs3DAnimationWidget::onKeyframeChanged );
}

void Qgs3DAnimationWidget::setCamera( Qt3DRender::QCamera *camera )
{
mCamera = camera;
connect( mCamera, &Qt3DRender::QCamera::viewMatrixChanged, this, &Qgs3DAnimationWidget::onCameraViewMatrixChanged );
}

void Qgs3DAnimationWidget::setAnimation( const Qgs3DAnimationSettings &animSettings )
{
// initialize GUI from the given animation
cboKeyframe->clear();
cboKeyframe->addItem( tr( "<none>" ) );
for ( const Qgs3DAnimationSettings::Keyframe &keyframe : animSettings.keyFrames() )
{
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 );
}

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

sliderTime->setMaximum( animSettings.duration() * 100 );
}

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>();
keyframes << kf;
qDebug() << "keyframe" << kf.time << kf.position << kf.rotation;
}
animSettings.setKeyframes( keyframes );
return animSettings;
}

void Qgs3DAnimationWidget::setDefaultAnimation()
{
Qgs3DAnimationSettings animSettings;
Qgs3DAnimationSettings::Keyframes kf;
Qgs3DAnimationSettings::Keyframe f1, f2;
f1.time = 0;
f1.position = mCamera->transform()->translation();
f1.rotation = mCamera->transform()->rotation();
f2.time = 5;
f2.position = f1.position + QVector3D( 0, 0, f1.position.z() / 2 );
f2.rotation = f1.rotation;
kf << f1 << f2;
animSettings.setKeyframes( kf );

setAnimation( animSettings );
}

void Qgs3DAnimationWidget::onPlayPause()
{
if ( mAnimationTimer->isActive() )
{
mAnimationTimer->stop();
cboKeyframe->setEnabled( true );
}
else
{
cboKeyframe->setCurrentIndex( 0 ); // unset active keyframe
cboKeyframe->setEnabled( false );
mAnimationTimer->start();
}
}

void Qgs3DAnimationWidget::onAnimationTimer()
{
float duration = sliderTime->maximum();
sliderTime->setValue( sliderTime->value() >= duration ? 0 : sliderTime->value() + 1 );
}

void Qgs3DAnimationWidget::onSliderValueChanged()
{
mAnimationController->setPosition( sliderTime->value() / 100. );
}

void Qgs3DAnimationWidget::onCameraViewMatrixChanged()
{
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 );

initializeController( animation() );
}

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

// jump to the camera view of the keyframe
float time = cboKeyframe->itemData( cboKeyframe->currentIndex(), Qt::UserRole + 1 ).toFloat();
sliderTime->setValue( time * 100 );
}
@@ -0,0 +1,65 @@
/***************************************************************************
qgs3danimationwidget.h
--------------------------------------
Date : July 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGS3DANIMATIONWIDGET_H
#define QGS3DANIMATIONWIDGET_H

#include <QWidget>

#include "ui_animation3dwidget.h"

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

class Qgs3DAnimationSettings;

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

void setCamera( Qt3DRender::QCamera *camera );

void setAnimation( const Qgs3DAnimationSettings &animation );
Qgs3DAnimationSettings animation() const;

void setDefaultAnimation();

signals:

private slots:
void onPlayPause();
void onAnimationTimer();
void onSliderValueChanged();
void onCameraViewMatrixChanged();
void onKeyframeChanged();

private:
void initializeController( const Qgs3DAnimationSettings &animSettings );

private:
QTimer *mAnimationTimer = nullptr;
Qt3DAnimation::QAnimationController *mAnimationController = nullptr;
Qt3DRender::QCamera *mCamera = nullptr; //!< Camera (not owned)
};

#endif // QGS3DANIMATIONWIDGET_H

0 comments on commit ce93338

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