/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx 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, or (at your option) any later version. ### File Specific: main-programmer: Patrick Boenzli co-programmer: Benjamin Grauer 2005-04-17: Benjamin Grauer Rewritte all functions, so it will fit into the Animation-class 2005-04-25: Patrick Boenzli Extended the framework to support quatSlerp rotations. Each frame now supports diff mov/rot types. Implemented mov/rot functions */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_ANIM #include "animation3d.h" #include "p_node.h" using namespace std; /** \brief standard constructor */ Animation3D::Animation3D(PNode* object) { this->object = object; // create a new List this->keyFrameList = new tList(); KeyFrame3D* tmpKeyFrame = new KeyFrame3D; tmpKeyFrame->position = Vector(); tmpKeyFrame->direction = Quaternion(); keyFrameList->add(tmpKeyFrame); this->currentKeyFrame = tmpKeyFrame; this->nextKeyFrame = tmpKeyFrame; this->animFuncMov = &Animation3D::mLinear; this->animFuncRot = &Animation3D::rLinear; } /** \brief standard deconstructor deletes all the Keyframes */ Animation3D::~Animation3D(void) { // delete all the KeyFrames tIterator* itKF = keyFrameList->getIterator(); KeyFrame3D* enumKF = itKF->nextElement(); while (enumKF) { delete enumKF; enumKF = itKF->nextElement(); } delete itKF; delete this->keyFrameList; } /** \brief rewinds the Animation to the beginning (first KeyFrame and time == 0) */ void Animation3D::rewind(void) { this->currentKeyFrame = keyFrameList->firstElement(); this->nextKeyFrame = keyFrameList->nextElement(keyFrameList->firstElement()); this->localTime = 0.0; this->setAnimFuncMov(this->currentKeyFrame->animFuncMov); this->setAnimFuncRot(this->currentKeyFrame->animFuncRot); } /** \brief Appends a new Keyframe \param position The position of the new Keyframe \param direction The direction of the new Keyframe. \param duration The duration from the new KeyFrame to the next one \param animFuncMov The function to animate position between this keyFrame and the next one \param animFuncMov The function to animate rotation between this keyFrame and the next one */ void Animation3D::addKeyFrame(Vector position, Quaternion direction, float duration, ANIM_FUNCTION animFuncMov, ANIM_FUNCTION animFuncRot) { // some small check if (duration <= 0.0) duration = 1.0; // if the Rotation-Animation-function is set ANIM_NULL, animFuncRot will match animFuncRot if (animFuncMov == ANIM_NULL) animFuncMov = ANIM_DEFAULT_FUNCTION; if (animFuncRot == ANIM_NULL) animFuncRot = animFuncMov; KeyFrame3D* tmpKeyFrame; // when adding the first frame if (this->keyFrameCount == 0) { tmpKeyFrame = this->keyFrameList->firstElement(); this->setAnimFuncMov(animFuncMov); this->setAnimFuncRot(animFuncRot); } else { tmpKeyFrame = new KeyFrame3D; // when adding the second frame if (this->currentKeyFrame == this->nextKeyFrame) this->nextKeyFrame = tmpKeyFrame; this->keyFrameList->add(tmpKeyFrame); } tmpKeyFrame->position = position; //tmpKeyFrame->lastPosition = position; tmpKeyFrame->direction = direction; tmpKeyFrame->duration = duration; tmpKeyFrame->animFuncMov = animFuncMov; tmpKeyFrame->animFuncRot = animFuncRot; this->keyFrameCount++; } /** \brief ticks the Animation \param dt how much time to tick */ void Animation3D::tick(float dt) { if (this->bRunning) { this->localTime += dt; if (localTime >= this->currentKeyFrame->duration) { if (likely(this->keyFramesToPlay != 0)) { if (unlikely(this->keyFramesToPlay > 0)) --this->keyFramesToPlay; // switching to the next Key-Frame this->localTime -= this->currentKeyFrame->duration; this->currentKeyFrame = this->nextKeyFrame; // checking, if we should still Play the animation if (this->currentKeyFrame == this->keyFrameList->lastElement()) this->handleInfinity(); this->nextKeyFrame = this->keyFrameList->nextElement(this->currentKeyFrame); this->setAnimFuncMov(this->currentKeyFrame->animFuncMov); this->setAnimFuncRot(this->currentKeyFrame->animFuncRot); } else this->pause(); } /* now animate it */ (this->*animFuncMov)(this->localTime); (this->*animFuncRot)(this->localTime); } } /*==Movement Section==========================================================*/ /** \brief Sets The kind of movment Animation between this keyframe and the next one \param animFunc The Type of Animation to set */ void Animation3D::setAnimFuncMov(ANIM_FUNCTION animFuncMov) { switch (animFuncMov) { default: case ANIM_CONSTANT: this->animFuncMov = &Animation3D::mConstant; break; case ANIM_LINEAR: this->animFuncMov = &Animation3D::mLinear; //this->object->setRelCoor(this->currentKeyFrame->position); this->currentKeyFrame->lastPosition = Vector(); break; case ANIM_SINE: this->animFuncMov = &Animation3D::mSine; //this->object->setRelCoor(this->currentKeyFrame->position); this->currentKeyFrame->lastPosition = Vector(); break; case ANIM_COSINE: this->animFuncMov = &Animation3D::mCosine; //this->object->setRelCoor(this->currentKeyFrame->position); this->currentKeyFrame->lastPosition = Vector(); break; case ANIM_EXP: this->animFuncMov = &Animation3D::mExp; break; case ANIM_NEG_EXP: this->animFuncMov = &Animation3D::mNegExp; this->expFactorMov = -1.0 / this->currentKeyFrame->duration * logf(DELTA_X_3D); break; case ANIM_QUADRATIC: this->animFuncMov = &Animation3D::mQuadratic; break; case ANIM_RANDOM: this->animFuncMov = &Animation3D::mRandom; break; } } /** \brief stays at the value of the currentKeyFrame \param timePassed The time passed since this Keyframe began */ void Animation3D::mConstant(float timePassed) const { this->object->setRelCoor(this->currentKeyFrame->position); /* this->tmpVect = this->nextKeyFrame->position - this->currentKeyFrame->position; this->tmpVect = this->tmpVect * this->localTime / this->currentKeyFrame->duration; this->currentFrame->object->setRelCoor(*this->lastFrame->position + *this->tmpVect); this->lastPosition = this->tmpVect; */ } /** \brief linear interpolation between this keyframe and the next one \param timePassed The time passed since this Keyframe began \todo implement also do this for direction */ void Animation3D::mLinear(float timePassed) const { Vector v = (this->nextKeyFrame->position - this->currentKeyFrame->position) * (timePassed/this->currentKeyFrame->duration); this->object->shiftCoor(v - this->currentKeyFrame->lastPosition); this->currentKeyFrame->lastPosition = v; } /** \brief a Sinusodial Interpolation between this keyframe and the next one \param timePassed The time passed since this Keyframe began \todo implement */ void Animation3D::mSine(float timePassed) const { Vector v; if( timePassed < this->currentKeyFrame->duration/2.0) v = (this->nextKeyFrame->position - this->currentKeyFrame->position) * sin( M_PI * timePassed /this->currentKeyFrame->duration) / 2.0; else v = (this->nextKeyFrame->position - this->currentKeyFrame->position) * (2.0 + sin( M_PI * (- timePassed /this->currentKeyFrame->duration)) )/ 2.0; this->object->shiftCoor(v - this->currentKeyFrame->lastPosition); this->currentKeyFrame->lastPosition = v; } /** \brief a cosine interpolation between this keyframe and the next one \param timePassed The time passed since this Keyframe began \todo implement */ void Animation3D::mCosine(float timePassed) const { Vector v; v = (this->nextKeyFrame->position - this->currentKeyFrame->position) * (1.0 + cos( M_PI * timePassed / this->currentKeyFrame->duration))/2.0; this->object->shiftCoor(v - this->currentKeyFrame->lastPosition); this->currentKeyFrame->lastPosition = v; /* this->object->setRelCoor( this->nextKeyFrame->position - (this->nextKeyFrame->position - this->currentKeyFrame->position) * (1.0 + cos( M_PI * timePassed / this->currentKeyFrame->duration))/2.0); */ } /** \brief an exponential interpolation between this keyframe and the next one \param timePassed The time passed since this Keyframe began */ void Animation3D::mExp(float timePassed) const { PRINTF(0)("no exp animation3d defined\n"); this->mLinear(timePassed); } /** \brief a negative exponential interpolation between this keyframe and the next one \param timePassed The time passed since this Keyframe began */ void Animation3D::mNegExp(float timePassed) const { this->object->setRelCoor( this->currentKeyFrame->position + (this->nextKeyFrame->position - this->currentKeyFrame->position) * (1.0 - expf(- timePassed * expFactorMov)) ); } /** \brief a quadratic interpolation between this keyframe and the next one \param timePassed The time passed since this Keyframe began \todo implement */ void Animation3D::mQuadratic(float timePassed) const { PRINTF(0)("no quadratic animation3d defined\n"); this->mLinear(timePassed); } /** \brief some random animation (fluctuating) \param timePassed The time passed since this Keyframe began */ void Animation3D::mRandom(float timePassed) const { this->object->setRelCoor(this->currentKeyFrame->position + (this->nextKeyFrame->position - this->currentKeyFrame->position) * (float)rand()/(float)RAND_MAX); this->object->setRelDir(this->currentKeyFrame->direction + (this->nextKeyFrame->direction - this->currentKeyFrame->direction)* (float)rand()/(float)RAND_MAX); } /*==Rotation Section==========================================================*/ /** \brief Sets The kind of rotation Animation between this keyframe and the next one \param animFunc The Type of Animation to set */ void Animation3D::setAnimFuncRot(ANIM_FUNCTION animFuncRot) { switch (animFuncRot) { default: case ANIM_CONSTANT: this->animFuncRot = &Animation3D::rConstant; break; case ANIM_LINEAR: this->animFuncRot = &Animation3D::rLinear; break; case ANIM_SINE: this->animFuncRot = &Animation3D::rSine; break; case ANIM_COSINE: this->animFuncRot = &Animation3D::rCosine; break; case ANIM_EXP: this->animFuncRot = &Animation3D::rExp; break; case ANIM_NEG_EXP: this->animFuncRot = &Animation3D::rNegExp; this->expFactorRot = -1.0 / this->currentKeyFrame->duration * logf(DELTA_X_3D); break; case ANIM_QUADRATIC: this->animFuncRot = &Animation3D::rQuadratic; break; case ANIM_RANDOM: this->animFuncRot = &Animation3D::rRandom; break; } } /** \brief stays at the value of the currentKeyFrame \param timePassed The time passed since this Keyframe began */ void Animation3D::rConstant(float timePassed) const { this->object->setRelDir(this->currentKeyFrame->direction); } /** \brief linear interpolation between this keyframe and the next one \param timePassed The time passed since this Keyframe began \todo implement also do this for direction */ void Animation3D::rLinear(float timePassed) const { this->object->setRelDir(quatSlerp( this->nextKeyFrame->direction, this->currentKeyFrame->direction, timePassed/this->currentKeyFrame->duration) ); } /** \brief a Sinusodial Interpolation between this keyframe and the next one \param timePassed The time passed since this Keyframe began \todo implement */ void Animation3D::rSine(float timePassed) const { float scale; if( timePassed < this->currentKeyFrame->duration / 2.0) scale = sin( M_PI * timePassed / this->currentKeyFrame->duration); else scale = 1.0 - sin( M_PI * timePassed / this->currentKeyFrame->duration); this->object->setRelDir(quatSlerp( this->nextKeyFrame->direction, this->currentKeyFrame->direction, scale) ); } /** \brief a cosine interpolation between this keyframe and the next one \param timePassed The time passed since this Keyframe began \todo implement */ void Animation3D::rCosine(float timePassed) const { float scale = cos(M_PI * timePassed / this->currentKeyFrame->duration); this->object->setRelDir(quatSlerp( this->nextKeyFrame->direction, this->currentKeyFrame->direction, scale) ); } /** \brief an exponential interpolation between this keyframe and the next one \param timePassed The time passed since this Keyframe began */ void Animation3D::rExp(float timePassed) const { PRINTF(0)("exp rotation function not implemented\n"); } /** \brief a negative exponential interpolation between this keyframe and the next one \param timePassed The time passed since this Keyframe began */ void Animation3D::rNegExp(float timePassed) const { float scale = (1.0 - expf(- timePassed * expFactorRot)); this->object->setRelDir(quatSlerp( this->nextKeyFrame->direction, this->currentKeyFrame->direction, scale) ); } /** \brief a quadratic interpolation between this keyframe and the next one \param timePassed The time passed since this Keyframe began \todo implement */ void Animation3D::rQuadratic(float timePassed) const { PRINTF(0)("quadratic rotation alg not implemented\n"); } /** \brief some random animation (fluctuating) \param timePassed The time passed since this Keyframe began */ void Animation3D::rRandom(float timePassed) const { PRINTF(0)("random rotation alg not implemented\n"); }