summaryrefslogtreecommitdiffstats
path: root/src/threed/scene_ai/qaianimation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/threed/scene_ai/qaianimation.cpp')
-rw-r--r--src/threed/scene_ai/qaianimation.cpp431
1 files changed, 431 insertions, 0 deletions
diff --git a/src/threed/scene_ai/qaianimation.cpp b/src/threed/scene_ai/qaianimation.cpp
new file mode 100644
index 000000000..2950fb6e0
--- /dev/null
+++ b/src/threed/scene_ai/qaianimation.cpp
@@ -0,0 +1,431 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qaianimation.h"
+#include "qaianimation_p.h"
+
+
+//#define Q_DEBUG_ANIMATIONS 1
+
+
+QT_BEGIN_NAMESPACE
+
+template <typename T>
+T Lerp(const T& t1, const T& t2, qreal t)
+{
+ Q_ASSERT(t>=0.0 && t<=1.0);
+ return t1*(1.0-t) + t2*t;
+}
+
+template <>
+QQuaternion Lerp<QQuaternion>(const QQuaternion& t1, const QQuaternion& t2, qreal t)
+{
+ Q_ASSERT(t>=0.0 && t<=1.0);
+ return QQuaternion::slerp(t1,t2,t).normalized();
+}
+
+//-----------------------------------------------------------------------------------------------------------------------
+
+QAiAnimationPrivate::QAiAnimationPrivate(QObject* pParent) :
+ QObject(pParent)
+ ,m_bValid(false)
+ ,m_bActive(false)
+ ,m_bPaused(false)
+ ,m_Duration(0)
+ ,m_bLoop(true)
+ ,m_Position(0)
+ ,m_bPositionDirty(true)
+ ,m_pPropertyAnim(0)
+{}
+
+QAiAnimationPrivate::~QAiAnimationPrivate()
+{}
+
+qreal QAiAnimationPrivate::position() const
+{
+ return m_Position;
+}
+
+void QAiAnimationPrivate::setPosition(qreal pos)
+{
+ m_Position = pos;
+ m_bPositionDirty = true;
+ emit positionChanged();
+}
+
+//-----------------------------------------------------------------------------------------------------------------------
+
+QAiAnimation::QAiAnimation(QObject *parent) :
+ QGLSceneAnimation(parent)
+{
+ d_ptr = new QAiAnimationPrivate(this);
+ QObject::connect(d_ptr,SIGNAL(positionChanged()),this,SIGNAL(positionChanged()));
+}
+
+QAiAnimation::QAiAnimation(QObject *parent, const QString &name) :
+ QGLSceneAnimation(parent,name)
+{
+ d_ptr = new QAiAnimationPrivate(this);
+ QObject::connect(d_ptr,SIGNAL(positionChanged()),this,SIGNAL(positionChanged()));
+}
+
+QAiAnimation::~QAiAnimation()
+{
+}
+
+qreal QAiAnimation::duration() const
+{
+ return d_ptr->m_Duration;
+}
+
+qreal QAiAnimation::position() const
+{
+ return d_ptr->m_Position;
+}
+
+void QAiAnimation::setPosition(qreal pos)
+{
+ d_ptr->m_Position = AdjustPosition(pos);
+ d_ptr->m_bPositionDirty = true;
+ if (d_ptr->m_bValid && d_ptr->m_bActive) {
+ emit positionChanged();
+ }
+}
+
+bool QAiAnimation::loop() const
+{
+ return d_ptr->m_bLoop;
+}
+
+void QAiAnimation::setLoop(bool l)
+{
+ d_ptr->m_bLoop = l;
+ d_ptr->m_pPropertyAnim->setLoopCount( d_ptr->m_bLoop?-1:0 );
+ emit loopChanged();
+}
+
+QList<QString> QAiAnimation::affectedNodes() const
+{
+ return d_ptr->m_AffectedNodes;
+}
+
+QList<QGLSceneAnimation::NodeTransform> QAiAnimation::transformations() const
+{
+ if (d_ptr->m_bValid && d_ptr->m_bActive) {
+ if (d_ptr->m_bPositionDirty) {
+ d_ptr->m_bPositionDirty = false;
+ if (d_ptr->m_Position<0.0 || d_ptr->m_Position>d_ptr->m_Duration) {
+ for (int i=0; i<d_ptr->m_TransformTracks.size(); ++i) {
+ Q_ASSERT(d_ptr->m_TransformTracks.at(i).m_BeforeBegin==TB_DEFAULT);
+ Q_ASSERT(d_ptr->m_TransformTracks.at(i).m_AfterEnd==TB_DEFAULT);
+ d_ptr->m_Transformations[i] = d_ptr->m_DefaultTransformations.at(i);
+ }
+ } else {
+ for (int i=0; i<d_ptr->m_TransformTracks.size(); ++i) {
+ const TransformTrack& rTrack = d_ptr->m_TransformTracks.at(i);
+ QGLSceneAnimation::NodeTransform& rTransform = d_ptr->m_Transformations[i];
+ rTransform.m_Scale = rTrack.m_ScaleTrack.GetValue(d_ptr->m_Position);
+ rTransform.m_Rotate = rTrack.m_RotateTrack.GetValue(d_ptr->m_Position);
+ rTransform.m_Translate = rTrack.m_TranslateTrack.GetValue(d_ptr->m_Position);
+ }
+ }
+ }
+ return d_ptr->m_Transformations;
+ } else {
+ return QList<QGLSceneAnimation::NodeTransform>();
+ }
+}
+
+template<typename T>
+void GetMinMaxTime(int size, T* pKeys, double& rMin, double& rMax)
+{
+ Q_ASSERT(pKeys!=0);
+ rMin = 1E9;
+ rMax = -1E9;
+ for (int i=0; i<size; ++i) {
+ if (rMin > pKeys[i].mTime)
+ rMin = pKeys[i].mTime;
+ if (rMax < pKeys[i].mTime)
+ rMax = pKeys[i].mTime;
+ }
+}
+
+void DumpAnimation(aiAnimation* pAnim)
+{
+#ifdef Q_DEBUG_ANIMATIONS
+ if (pAnim) {
+ qDebug("Animation:");
+ qDebug(" name = '%s'",pAnim->mName.data);
+ qDebug(" channels = %d",pAnim->mNumChannels);
+ for (unsigned int ch=0; ch<pAnim->mNumChannels; ++ch) {
+ qDebug(" channel %d:",ch);
+ aiNodeAnim* pNodeAnim = pAnim->mChannels[ch];
+ Q_ASSERT(pNodeAnim);
+ qDebug(" affected node = '%s'",pNodeAnim->mNodeName.data);
+ qDebug(" scale keys = %d",pNodeAnim->mNumScalingKeys);
+ double timeMin, timeMax;
+ GetMinMaxTime(pNodeAnim->mNumScalingKeys, pNodeAnim->mScalingKeys, timeMin, timeMax);
+ qDebug(" scale keys time = { %f .. %f }",timeMin,timeMax);
+ qDebug(" rotation keys = %d",pNodeAnim->mNumRotationKeys);
+ GetMinMaxTime(pNodeAnim->mNumRotationKeys, pNodeAnim->mRotationKeys, timeMin, timeMax);
+ qDebug(" rotation keys time = { %f .. %f }",timeMin,timeMax);
+ qDebug(" position keys = %d",pNodeAnim->mNumPositionKeys);
+ GetMinMaxTime(pNodeAnim->mNumPositionKeys, pNodeAnim->mPositionKeys, timeMin, timeMax);
+ qDebug(" position keys time = { %f .. %f }",timeMin,timeMax);
+ qDebug(" pre state = %d",pNodeAnim->mPreState);
+ qDebug(" post state = %d",pNodeAnim->mPostState);
+ Q_ASSERT(pNodeAnim->mPreState==aiAnimBehaviour_DEFAULT && pNodeAnim->mPostState==aiAnimBehaviour_DEFAULT);
+ }
+ }
+#else
+ Q_UNUSED(pAnim);
+#endif
+}
+
+void QAiAnimation::loadData(aiAnimation* pSrcAnim, const QMap<QString,aiMatrix4x4>& rDefaultTransforms, QMap<QString,QGLSceneAnimation::NodeTransform>& rDefaultTransformsRaw)
+{
+ Q_ASSERT(pSrcAnim);
+ DumpAnimation(pSrcAnim);
+ double timeMin = 1E9;
+ double timeMax = -1E9;
+ for (unsigned int ch=0; ch<pSrcAnim->mNumChannels; ++ch) {
+ aiNodeAnim* pNodeAnim = pSrcAnim->mChannels[ch];
+ Q_ASSERT(pNodeAnim);
+
+ QString NN(QLatin1String(pNodeAnim->mNodeName.data));
+ d_ptr->m_AffectedNodes.append(NN);
+
+ QGLSceneAnimation::NodeTransform transform;
+ QMap<QString,aiMatrix4x4>::const_iterator It = rDefaultTransforms.find(NN);
+ Q_ASSERT(It != rDefaultTransforms.end());
+ aiQuaternion rotation;
+ aiVector3D position;
+ aiVector3D scale;
+ It.value().Decompose(scale, rotation, position);
+ transform.m_Scale = QVector3D(scale.x,scale.y, scale.z);
+ transform.m_Translate = QVector3D(position.x, position.y, position.z);
+ transform.m_Rotate = QQuaternion(rotation.w, rotation.x, rotation.y, rotation.z);
+ d_ptr->m_DefaultTransformations.append(transform);
+ d_ptr->m_Transformations.append(transform);
+ rDefaultTransformsRaw.insert(NN,transform);
+
+ d_ptr->m_TransformTracks.append(TransformTrack());
+ TransformTrack& rTrack = d_ptr->m_TransformTracks.back();
+
+ Q_ASSERT(pNodeAnim->mNumScalingKeys > 0);
+ rTrack.m_ScaleTrack.m_Times.resize(pNodeAnim->mNumScalingKeys);
+ rTrack.m_ScaleTrack.m_Values.resize(pNodeAnim->mNumScalingKeys);
+ for (unsigned int i=0; i<pNodeAnim->mNumScalingKeys; ++i) {
+ rTrack.m_ScaleTrack.m_Times[i] = pNodeAnim->mScalingKeys[i].mTime;
+ rTrack.m_ScaleTrack.m_Values[i] = QVector3D(pNodeAnim->mScalingKeys[i].mValue.x,pNodeAnim->mScalingKeys[i].mValue.y,pNodeAnim->mScalingKeys[i].mValue.z);
+ if (timeMin > pNodeAnim->mScalingKeys[i].mTime)
+ timeMin = pNodeAnim->mScalingKeys[i].mTime;
+ if (timeMax < pNodeAnim->mScalingKeys[i].mTime)
+ timeMax = pNodeAnim->mScalingKeys[i].mTime;
+ }
+
+ Q_ASSERT(pNodeAnim->mNumRotationKeys > 0);
+ rTrack.m_RotateTrack.m_Times.resize(pNodeAnim->mNumRotationKeys);
+ rTrack.m_RotateTrack.m_Values.resize(pNodeAnim->mNumRotationKeys);
+ for (unsigned int i=0; i<pNodeAnim->mNumRotationKeys; ++i) {
+ rTrack.m_RotateTrack.m_Times[i] = pNodeAnim->mRotationKeys[i].mTime;
+ QQuaternion qq(pNodeAnim->mRotationKeys[i].mValue.w,pNodeAnim->mRotationKeys[i].mValue.x,pNodeAnim->mRotationKeys[i].mValue.y,pNodeAnim->mRotationKeys[i].mValue.z);
+ rTrack.m_RotateTrack.m_Values[i] = qq;
+ if (timeMin > pNodeAnim->mRotationKeys[i].mTime)
+ timeMin = pNodeAnim->mRotationKeys[i].mTime;
+ if (timeMax < pNodeAnim->mRotationKeys[i].mTime)
+ timeMax = pNodeAnim->mRotationKeys[i].mTime;
+ }
+
+ Q_ASSERT(pNodeAnim->mNumPositionKeys > 0);
+ rTrack.m_TranslateTrack.m_Times.resize(pNodeAnim->mNumPositionKeys);
+ rTrack.m_TranslateTrack.m_Values.resize(pNodeAnim->mNumPositionKeys);
+ for (unsigned int i=0; i<pNodeAnim->mNumPositionKeys; ++i) {
+ rTrack.m_TranslateTrack.m_Times[i] = pNodeAnim->mPositionKeys[i].mTime;
+ rTrack.m_TranslateTrack.m_Values[i] = QVector3D(pNodeAnim->mPositionKeys[i].mValue.x,pNodeAnim->mPositionKeys[i].mValue.y,pNodeAnim->mPositionKeys[i].mValue.z);
+ if (timeMin > pNodeAnim->mPositionKeys[i].mTime)
+ timeMin = pNodeAnim->mPositionKeys[i].mTime;
+ if (timeMax < pNodeAnim->mPositionKeys[i].mTime)
+ timeMax = pNodeAnim->mPositionKeys[i].mTime;
+ }
+
+ Q_ASSERT(pNodeAnim->mPreState==aiAnimBehaviour_DEFAULT && pNodeAnim->mPostState==aiAnimBehaviour_DEFAULT);
+ rTrack.m_BeforeBegin = TB_DEFAULT;
+ rTrack.m_AfterEnd = TB_DEFAULT;
+ }
+ Q_ASSERT(d_ptr->m_AffectedNodes.size() == d_ptr->m_TransformTracks.size());
+ Q_ASSERT(d_ptr->m_AffectedNodes.size() == d_ptr->m_DefaultTransformations.size());
+ Q_ASSERT(d_ptr->m_AffectedNodes.size() == d_ptr->m_Transformations.size());
+ qreal duration = timeMax-timeMin;
+ d_ptr->m_bValid = (duration > 0.000001);
+ if (d_ptr->m_bValid) {
+ qreal timeShift = -timeMin;
+ timeMax = -1E9;
+ for (int tr=0; tr<d_ptr->m_TransformTracks.size(); ++tr) {
+ TransformTrack& rTrack = d_ptr->m_TransformTracks[tr];
+ // fix scale
+ if (rTrack.m_ScaleTrack.m_Times.size() == 1) {
+ rTrack.m_ScaleTrack.m_bConst = true;
+ } else {
+ rTrack.m_ScaleTrack.m_bConst = false;
+ for (int t=0; t<rTrack.m_ScaleTrack.m_Times.size(); ++t) {
+ rTrack.m_ScaleTrack.m_Times[t] += timeShift;
+ Q_ASSERT(rTrack.m_ScaleTrack.m_Times[t] >= 0.0);
+ if (timeMax < rTrack.m_ScaleTrack.m_Times[t])
+ timeMax = rTrack.m_ScaleTrack.m_Times[t];
+ }
+ Q_ASSERT(rTrack.m_ScaleTrack.m_Times[0] >= 0.0);
+ Q_ASSERT(rTrack.m_ScaleTrack.m_Times[0] < 0.00001);
+ Q_ASSERT(qAbs((rTrack.m_ScaleTrack.m_Times.last()/duration)-1.0) < 0.00001);
+ }
+ // fix rotation
+ if (rTrack.m_RotateTrack.m_Times.size() == 1) {
+ rTrack.m_RotateTrack.m_bConst = true;
+ } else {
+ rTrack.m_RotateTrack.m_bConst = false;
+ for (int t=0; t<rTrack.m_RotateTrack.m_Times.size(); ++t) {
+ rTrack.m_RotateTrack.m_Times[t] += timeShift;
+ Q_ASSERT(rTrack.m_RotateTrack.m_Times[t] >= 0.0);
+ if (timeMax < rTrack.m_RotateTrack.m_Times[t])
+ timeMax = rTrack.m_RotateTrack.m_Times[t];
+ }
+ Q_ASSERT(rTrack.m_RotateTrack.m_Times[0] >= 0.0);
+ Q_ASSERT(rTrack.m_RotateTrack.m_Times[0] < 0.00001);
+ Q_ASSERT(qAbs((rTrack.m_RotateTrack.m_Times.last()/duration)-1.0) < 0.00001);
+ }
+ // fix translation
+ if (rTrack.m_TranslateTrack.m_Times.size() == 1) {
+ rTrack.m_TranslateTrack.m_bConst = true;
+ } else {
+ rTrack.m_TranslateTrack.m_bConst = false;
+ for (int t=0; t<rTrack.m_TranslateTrack.m_Times.size(); ++t) {
+ rTrack.m_TranslateTrack.m_Times[t] += timeShift;
+ Q_ASSERT(rTrack.m_TranslateTrack.m_Times[t] >= 0.0);
+ if (timeMax < rTrack.m_TranslateTrack.m_Times[t])
+ timeMax = rTrack.m_TranslateTrack.m_Times[t];
+ }
+ Q_ASSERT(rTrack.m_TranslateTrack.m_Times[0] >= 0.0);
+ Q_ASSERT(rTrack.m_TranslateTrack.m_Times[0] < 0.00001);
+ Q_ASSERT(qAbs((rTrack.m_TranslateTrack.m_Times.last()/duration)-1.0) < 0.00001);
+ }
+ }
+ Q_ASSERT(qAbs((timeMax/duration)-1.0) < 0.00001);
+ }
+ d_ptr->m_Duration = duration;
+
+ Q_ASSERT(d_ptr->m_pPropertyAnim==0);
+ d_ptr->m_pPropertyAnim = new QPropertyAnimation(d_ptr,"position",d_ptr);
+ d_ptr->m_pPropertyAnim->setStartValue(0.0f);
+ d_ptr->m_pPropertyAnim->setEndValue(d_ptr->m_Duration);
+ d_ptr->m_pPropertyAnim->setDuration(int(d_ptr->m_Duration*1000.0));
+ d_ptr->m_pPropertyAnim->setLoopCount(-1);
+}
+
+void QAiAnimation::play()
+{
+ if (d_ptr->m_bValid) {
+ bool bWasActive = d_ptr->m_bActive;
+ bool bWasPaused = d_ptr->m_bPaused;
+ d_ptr->m_bActive = true;
+ d_ptr->m_bPaused = false;
+ if (!bWasActive || bWasPaused) {
+ d_ptr->m_bPositionDirty = true;
+ emit positionChanged();
+ }
+ d_ptr->m_pPropertyAnim->start();
+ }
+}
+
+void QAiAnimation::stop()
+{
+ if (d_ptr->m_bValid) {
+ d_ptr->m_bActive = false;
+ d_ptr->m_pPropertyAnim->stop();
+ emit positionChanged();
+ }
+}
+
+void QAiAnimation::pause(bool val)
+{
+ if (d_ptr->m_bValid) {
+ bool bWasPaused = d_ptr->m_bPaused;
+ d_ptr->m_bPaused = val;
+ if (d_ptr->m_bPaused)
+ d_ptr->m_pPropertyAnim->pause();
+ else
+ d_ptr->m_pPropertyAnim->resume();
+ if (d_ptr->m_bActive && !d_ptr->m_bPaused && bWasPaused) {
+ d_ptr->m_bPositionDirty = true;
+ emit positionChanged();
+ }
+ }
+}
+
+qreal QAiAnimation::AdjustPosition(qreal newPos)
+{
+ if (d_ptr->m_bLoop) {
+ if (newPos>d_ptr->m_Duration) {
+ int n = int(newPos/d_ptr->m_Duration);
+ newPos -= qreal(n);
+ if (newPos<0.0)
+ return 0.0;
+ else if (newPos>d_ptr->m_Duration)
+ return d_ptr->m_Duration;
+ else
+ return newPos;
+ } else if (newPos<0.0) {
+ int n = int(newPos/d_ptr->m_Duration);
+ newPos -= qreal(n-1);
+ if (newPos<0.0)
+ return 0.0;
+ else if (newPos>d_ptr->m_Duration)
+ return d_ptr->m_Duration;
+ else
+ return newPos;
+ } else {
+ return newPos;
+ }
+ } else {
+ return newPos;
+ }
+}
+
+QT_END_NAMESPACE