#include "Animation3D.h" #include "Model3D.h" using namespace Framework; // create an animation for a specific bone // \param boneId the id of the bone // \param originPos the position of the bone at the beginning of the animation // \param originRot the rotation of the bone at the beginning of the animation BoneAnimation::BoneAnimation( int boneId, Vec3 originPos, Vec3 originRot) : ReferenceCounter(), boneId(boneId), frameCount(1), maxTime(0.0), loop(0) { frames = new KeyFrame[1]; frames[0].time = 0.0; frames[0].pos = originPos; frames[0].rot = originRot; current = frames[0]; } // destructor BoneAnimation::~BoneAnimation() { delete[] frames; } // make the animation to a loop that starts again if the end is reached void BoneAnimation::setLoop(bool loop) { this->loop = loop; } //! adds a keyframe //! \param time the time in seconds since the start of the animation at //! which the bone should have the given position and rotation relative //! to its parent bone \param pos the position of the bone \param rot //! the rotation of the bone void BoneAnimation::addKeyFrame(double time, Vec3 pos, Vec3 rot) { if (time < 0) throw "Illegal argument exception: time of a keyframe can not be lower " "than 0"; if (maxTime < time) maxTime = time; frameCount++; KeyFrame* tmp = new KeyFrame[frameCount]; bool added = 0; for (int i = 0; i < frameCount; i++) { if (i < frameCount - 1 && frames[i - (int)added].time <= time) { tmp[i] = frames[i]; } else { if (!added) { tmp[i].time = time; tmp[i].pos = pos; tmp[i].rot = rot; added = 1; } else { tmp[i] = frames[i - 1]; } } } delete[] frames; frames = tmp; } // adds a keyframe that is the same as the last keyframe with the time time // \returns true if the keyframe was added successfully, false if time is maller // than the current length of the animation bool BoneAnimation::doNothingUntil(double time) { if (time > maxTime) { KeyFrame& last = frames[frameCount - 1]; addKeyFrame(time, last.pos, last.rot); } return 0; } // calculates the positions and rotation at the next time // \param time the passed time in seconds since the last call void BoneAnimation::tick(double time) { if (frameCount <= 1) return; current.time += time; if (loop) { while (current.time >= maxTime) { current.time -= maxTime; } } else if (current.time > maxTime) { current.time = maxTime; } int last = -1; int next = -1; for (int i = 0; i < frameCount; i++) { last = next; next = i; if (frames[i].time > current.time) { break; } } float timePart = (float)(current.time - frames[last].time); current.pos = frames[last].pos + (frames[next].pos - frames[last].pos) * timePart; current.rot = frames[last].rot + (frames[next].rot - frames[last].rot) * timePart; } //! applys the animation on a given skeleton //! \param zS: the sceleton void BoneAnimation::apply(Skeleton* zSkelett) const { Bone* zK = zSkelett->zBone(boneId); if (zK) { zK->setPosition(current.pos); zK->setRotation(current.rot); } } // returns true if the animation has reached the last keyframe and does // not loop bool BoneAnimation::isFinished() const { return current.time >= maxTime; } // returns the bone id this animation is for int BoneAnimation::getBoneId() const { return boneId; } // returns the maximum time of the animation double BoneAnimation::getMaxTime() const { return maxTime; } //! Constructor SkeletonAnimation::SkeletonAnimation() : ReferenceCounter(), loop(0) {} // make the animation to a loop that starts again if the end is reached void SkeletonAnimation::setLoop(bool loop) { this->loop = loop; for (BoneAnimation* animation : subAnimations) { animation->setLoop(loop); } } // adds an animation for a specific bone // \param boneId the bone id // \param originPos the position of the bone at the beginning of the animation // \param originRot the rotation of the bone at the beginning of the animation bool SkeletonAnimation::addAnimation( int boneId, Vec3 originPos, Vec3 originRot) { for (BoneAnimation* animation : subAnimations) { if (animation->getBoneId() == boneId) { return 0; } } subAnimations.add(new BoneAnimation(boneId, originPos, originRot)); return 1; } //! adds a keyframe for a specific bone of the sceleton //! \param kId id of the bone //! \param time the time in seconds since the start of the animation at //! which the bone should have the given position and rotation relative //! to its parent bone \param pos the position of the bone \param rot //! the rotation of the bone bool SkeletonAnimation::addKeyFrame( int boneId, double time, Vec3 pos, Vec3 rot) { for (BoneAnimation* animation : subAnimations) { if (animation->getBoneId() == boneId) { animation->addKeyFrame(time, pos, rot); return 1; } } return 0; } //! applys the animation on a given skeleton //! \param zS: the sceleton void SkeletonAnimation::apply(Skeleton* zS) const { for (BoneAnimation* animation : subAnimations) { animation->apply(zS); } } // calculates the positions and rotation at the next time // \param time the passed time in seconds since the last call void SkeletonAnimation::tick(double time) { for (BoneAnimation* animation : subAnimations) { animation->tick(time); } } // returns true if the animation has reached the last keyframe and does // not loop bool SkeletonAnimation::isFinished() const { for (BoneAnimation* animation : subAnimations) { if (!animation->isFinished()) return 0; } return 1; } // returns the maximum time of the animation double SkeletonAnimation::getMaxTime() const { double max = 0; for (BoneAnimation* animation : subAnimations) { max = MAX(animation->getMaxTime(), max); } return max; } // returns the animation for a specific bone or 0 if it does not exist BoneAnimation* SkeletonAnimation::zAnimation(int boneId) const { for (BoneAnimation* animation : subAnimations) { if (animation->getBoneId() == boneId) return animation; } return 0; }