// // Urho3D Engine // Copyright (c) 2008-2011 Lasse Öörni // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // #include "Precompiled.h" #include "AnimatedModel.h" #include "Animation.h" #include "AnimationController.h" #include "AnimationState.h" #include "Context.h" #include "MemoryBuffer.h" #include "Profiler.h" #include "ResourceCache.h" #include "Scene.h" #include "SceneEvents.h" #include "VectorBuffer.h" #include "DebugNew.h" static String noBoneName; OBJECTTYPESTATIC(AnimationController); AnimationController::AnimationController(Context* context) : Component(context) { } AnimationController::~AnimationController() { } void AnimationController::RegisterObject(Context* context) { context->RegisterFactory(); ACCESSOR_ATTRIBUTE(AnimationController, VAR_BUFFER, "Animations", GetAnimationsAttr, SetAnimationsAttr, PODVector, PODVector(), AM_DEFAULT); } void AnimationController::Update(float timeStep) { AnimatedModel* model = GetComponent(); if (!model) return; PROFILE(UpdateAnimationController); // Loop through animations for (Vector::Iterator i = animations_.Begin(); i != animations_.End();) { bool remove = false; AnimationState* state = model->GetAnimationState(i->hash_); if (!state) remove = true; else { // Advance the animation if (i->speed_ != 0.0f) state->AddTime(i->speed_ * timeStep); float targetWeight = i->targetWeight_; float fadeTime = i->fadeTime_; // If non-looped animation at the end, activate autofade as applicable if (!state->IsLooped() && state->GetTime() >= state->GetLength() && i->autoFadeTime_ > 0.0f) { targetWeight = 0.0f; fadeTime = i->autoFadeTime_; } // Process weight fade float currentWeight = state->GetWeight(); if (currentWeight != targetWeight && fadeTime > 0.0f) { float weightDelta = 1.0f / fadeTime * timeStep; if (currentWeight < targetWeight) currentWeight = Min(currentWeight + weightDelta, targetWeight); else if (currentWeight > targetWeight) currentWeight = Max(currentWeight - weightDelta, targetWeight); state->SetWeight(currentWeight); } // Remove if weight zero and target weight zero if (state->GetWeight() == 0.0f && (targetWeight == 0.0f || fadeTime == 0.0f)) remove = true; } if (remove) { if (state) model->RemoveAnimationState(state); i = animations_.Erase(i); } else ++i; } } bool AnimationController::Play(const String& name, int layer, bool looped, float fadeInTime) { AnimatedModel* model = GetComponent(); if (!model) return false; // Check if already exists unsigned index; AnimationState* state; FindAnimation(name, index, state); if (!state) { Animation* newAnimation = GetSubsystem()->GetResource(name); state = model->AddAnimationState(newAnimation); if (!state) return false; } if (index == M_MAX_UNSIGNED) { AnimationControl newControl; Animation* animation = state->GetAnimation(); newControl.hash_ = animation->GetNameHash(); animations_.Push(newControl); index = animations_.Size() - 1; } state->SetLayer(layer); state->SetLooped(looped); if (fadeInTime > 0.0f) { animations_[index].targetWeight_ = 1.0f; animations_[index].fadeTime_ = fadeInTime; } else state->SetWeight(1.0f); return true; } bool AnimationController::PlayExclusive(const String& name, int layer, bool looped, float fadeTime) { FadeOthers(name, 0.0f, fadeTime); return Play(name, layer, looped, fadeTime); } bool AnimationController::Stop(const String& name, float fadeOutTime) { AnimatedModel* model = GetComponent(); if (!model) return false; unsigned index; AnimationState* state; FindAnimation(name, index, state); if (fadeOutTime <= 0.0f) { if (index != M_MAX_UNSIGNED) animations_.Erase(animations_.Begin() + index); if (state) model->RemoveAnimationState(state); } else { if (index != M_MAX_UNSIGNED) { animations_[index].targetWeight_ = 0.0f; animations_[index].fadeTime_ = fadeOutTime; } } return index != M_MAX_UNSIGNED || state != 0; } void AnimationController::StopLayer(int layer, float fadeOutTime) { AnimatedModel* model = GetComponent(); if (!model) return; for (Vector::Iterator i = animations_.Begin(); i != animations_.End();) { AnimationState* state = model->GetAnimationState(i->hash_); bool remove = false; if (state && state->GetLayer() == layer) { if (fadeOutTime <= 0.0f) { remove = true; if (state) model->RemoveAnimationState(state); } else { i->targetWeight_ = 0.0f; i->fadeTime_ = fadeOutTime; } } if (remove) i = animations_.Erase(i); else ++i; } } void AnimationController::StopAll(float fadeOutTime) { AnimatedModel* model = GetComponent(); if (!model) return; for (Vector::Iterator i = animations_.Begin(); i != animations_.End();) { bool remove = false; if (fadeOutTime <= 0.0f) { remove = true; AnimationState* state = model->GetAnimationState(i->hash_); if (state) model->RemoveAnimationState(state); } else { i->targetWeight_ = 0.0f; i->fadeTime_ = fadeOutTime; } if (remove) i = animations_.Erase(i); else ++i; } } bool AnimationController::Fade(const String& name, float targetWeight, float fadeTime) { unsigned index; AnimationState* state; FindAnimation(name, index, state); if (index == M_MAX_UNSIGNED) return false; animations_[index].targetWeight_ = Clamp(targetWeight, 0.0f, 1.0f); animations_[index].fadeTime_ = Max(fadeTime, M_EPSILON); return true; } bool AnimationController::FadeOthers(const String& name, float targetWeight, float fadeTime) { unsigned index; AnimationState* state; FindAnimation(name, index, state); if (index == M_MAX_UNSIGNED || !state) return false; AnimatedModel* model = GetComponent(); int layer = state->GetLayer(); for (unsigned i = 0; i < animations_.Size(); ++i) { if (i != index) { AnimationControl& control = animations_[i]; AnimationState* otherState = model->GetAnimationState(control.hash_); if (otherState && otherState->GetLayer() == layer) { control.targetWeight_ = Clamp(targetWeight, 0.0f, 1.0f); control.fadeTime_ = Max(fadeTime, M_EPSILON); } } } return true; } bool AnimationController::SetLayer(const String& name, int layer) { AnimationState* state = FindAnimationState(name); if (!state) return false; state->SetLayer(layer); return true; } bool AnimationController::SetStartBone(const String& name, const String& startBoneName) { AnimationState* state = FindAnimationState(name); if (!state) return false; AnimatedModel* model = GetComponent(); Bone* bone = model->GetSkeleton().GetBone(startBoneName); state->SetStartBone(bone); return true; } bool AnimationController::SetTime(const String& name, float time) { AnimationState* state = FindAnimationState(name); if (!state) return false; state->SetTime(time); return true; } bool AnimationController::SetSpeed(const String& name, float speed) { unsigned index; AnimationState* state; FindAnimation(name, index, state); if (index == M_MAX_UNSIGNED) return false; animations_[index].speed_ = speed; return true; } bool AnimationController::SetWeight(const String& name, float weight) { unsigned index; AnimationState* state; FindAnimation(name, index, state); if (index == M_MAX_UNSIGNED || !state) return false; state->SetWeight(weight); // Stop any ongoing fade animations_[index].fadeTime_ = 0.0f; return true; } bool AnimationController::SetLooped(const String& name, bool enable) { AnimationState* state = FindAnimationState(name); if (!state) return false; state->SetLooped(enable); return true; } bool AnimationController::SetAutoFade(const String& name, float fadeOutTime) { unsigned index; AnimationState* state; FindAnimation(name, index, state); if (index == M_MAX_UNSIGNED) return false; animations_[index].autoFadeTime_ = Max(fadeOutTime, 0.0f); return true; } bool AnimationController::IsPlaying(const String& name) const { unsigned index; AnimationState* state; FindAnimation(name, index, state); return index != M_MAX_UNSIGNED; } bool AnimationController::IsFadingIn(const String& name) const { unsigned index; AnimationState* state; FindAnimation(name, index, state); if (index == M_MAX_UNSIGNED || !state) return false; return animations_[index].fadeTime_ && animations_[index].targetWeight_ > state->GetWeight(); } bool AnimationController::IsFadingOut(const String& name) const { unsigned index; AnimationState* state; FindAnimation(name, index, state); if (index == M_MAX_UNSIGNED || !state) return false; return (animations_[index].fadeTime_ && animations_[index].targetWeight_ < state->GetWeight()) || (!state->IsLooped() && state->GetTime() >= state->GetLength() && animations_[index].autoFadeTime_); } int AnimationController::GetLayer(const String& name) const { AnimationState* state = FindAnimationState(name); if (!state) return 0; return state->GetLayer(); } Bone* AnimationController::GetStartBone(const String& name) const { AnimationState* state = FindAnimationState(name); if (!state) return 0; return state->GetStartBone(); } const String& AnimationController::GetStartBoneName(const String& name) const { Bone* bone = GetStartBone(name); return bone ? bone->name_ : noBoneName; } float AnimationController::GetTime(const String& name) const { AnimationState* state = FindAnimationState(name); return state ? state->GetTime() : 0.0f; } float AnimationController::GetWeight(const String& name) const { AnimationState* state = FindAnimationState(name); return state ? state->GetWeight() : 0.0f; } bool AnimationController::IsLooped(const String& name) const { AnimationState* state = FindAnimationState(name); return state ? state->IsLooped() : false; } float AnimationController::GetLength(const String& name) const { AnimationState* state = FindAnimationState(name); return state ? state->GetLength() : 0.0f; } float AnimationController::GetSpeed(const String& name) const { unsigned index; AnimationState* state; FindAnimation(name, index, state); if (index == M_MAX_UNSIGNED) return 0.0f; return animations_[index].speed_; } float AnimationController::GetFadeTarget(const String& name) const { unsigned index; AnimationState* state; FindAnimation(name, index, state); if (index == M_MAX_UNSIGNED) return 0.0f; return animations_[index].targetWeight_; } float AnimationController::GetFadeTime(const String& name) const { unsigned index; AnimationState* state; FindAnimation(name, index, state); if (index == M_MAX_UNSIGNED) return 0.0f; return animations_[index].targetWeight_; } float AnimationController::GetAutoFade(const String& name) const { unsigned index; AnimationState* state; FindAnimation(name, index, state); if (index == M_MAX_UNSIGNED) return 0.0f; return animations_[index].autoFadeTime_; } void AnimationController::SetAnimationsAttr(PODVector value) { MemoryBuffer buf(value); animations_.Resize(buf.ReadVLE()); for (Vector::Iterator i = animations_.Begin(); i != animations_.End(); ++i) { i->hash_ = buf.ReadStringHash(); i->speed_ = buf.ReadFloat(); i->targetWeight_ = buf.ReadFloat(); i->fadeTime_ = buf.ReadFloat(); i->autoFadeTime_ = buf.ReadFloat(); } } PODVector AnimationController::GetAnimationsAttr() const { VectorBuffer buf; buf.WriteVLE(animations_.Size()); for (Vector::ConstIterator i = animations_.Begin(); i != animations_.End(); ++i) { buf.WriteStringHash(i->hash_); buf.WriteFloat(i->speed_); buf.WriteFloat(i->targetWeight_); buf.WriteFloat(i->fadeTime_); buf.WriteFloat(i->autoFadeTime_); } return buf.GetBuffer(); } void AnimationController::OnNodeSet(Node* node) { if (node) { Scene* scene = node->GetScene(); if (scene) SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(AnimationController, HandleScenePostUpdate)); } } void AnimationController::FindAnimation(const String& name, unsigned& index, AnimationState*& state) const { AnimatedModel* model = GetComponent(); StringHash nameHash(name); // Find the AnimationState state = model ? model->GetAnimationState(nameHash) : 0; if (state) { // Either a resource name or animation name may be specified. We store resource names, so correct the hash if necessary nameHash = state->GetAnimation()->GetNameHash(); } // Find the internal control structure index = M_MAX_UNSIGNED; for (unsigned i = 0; i < animations_.Size(); ++i) { if (animations_[i].hash_ == nameHash) { index = i; break; } } } AnimationState* AnimationController::FindAnimationState(const String& name) const { AnimatedModel* model = GetComponent(); return model ? model->GetAnimationState(name) : 0; } void AnimationController::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData) { using namespace ScenePostUpdate; Update(eventData[P_TIMESTEP].GetFloat()); }