/**************************************************************************** Copyright (c) 2011-2013,WebJet Business Division,CYOU http://www.genesis-3d.com.cn 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 "animation/animation_stdneb.h" #include "animation/Animation.h" #include "animation/AnimationUtil.h" #include #define _VEC_ITERATOR(TYPE, VECTOR, ELEM) \ Util::Array< TYPE >::Iterator ELEM = VECTOR.Begin();\ Util::Array< TYPE >::Iterator __end = VECTOR.End(); #define _FOREACH(ELEM)\ while (ELEM != __end)\ { #define _FOREACH_VECTOR_BEGIN(TYPE, VECTOR, ELEM) \ _VEC_ITERATOR(TYPE, VECTOR, ELEM) \ _FOREACH(ELEM) #define _FOREACH_VECTOR_END(ELEM) \ ++ ELEM;\ } namespace Animations { static const float TINY_WEIGHT = 0.0001f; static const float NO_WEIGHT = 0.0f; static const float FULL_WEIGHT = 1.0f; inline void _quaternion_add_blend(Math::quaternion& target, const Math::quaternion& from, float weight) { float sign = Math::n_sgn(from.dot(target)); float qx = from.x() * weight * sign; float qy = from.y() * weight * sign; float qz = from.z() * weight * sign; float qw = from.w() * weight * sign; target.set_x(target.x() + qx ); target.set_y(target.y() + qy ); target.set_z(target.z() + qz ); target.set_w(target.w() + qw ); } struct CompareLayers : public std::binary_function { bool operator() (const AnimationLayer* lhs, const AnimationLayer* rhs) const { return lhs->GetLayerIndex() < rhs->GetLayerIndex(); } }; __ImplementClass( Animations::Animation, 'ANIM', Core::RefCounted); Animation::Animation() : m_bUpdateResult(false) , m_ClientCount(0) , m_bDirty(true) ,m_LocalTimer(0.0f) ,m_UpdateTime(0.0f) { } Animation::~Animation() { clearAnimClips(); clearClipControls(); clearLayers(); m_ToParentTrans.Clear(); m_SampledTrans.Clear(); m_SampledScale.Clear(); m_SampledRotation.Clear(); } void Animation::AddAnimClip(const GPtr &animClip) { RemoveAnimClip(animClip->GetName()); m_AnimClips.Append(animClip); if (0 == m_AnimationLayers.Size()) { buildLayer(DefaultLayer); } buildControl(animClip.get(), m_AnimationLayers[DefaultLayer]); } void Animation::RemoveAnimClip(const Resources::ResourceId& clipName) { int index = findClipIndex(clipName); if (InvalidIndex != index) { m_ClipControls[index]->Destroy(); m_AnimClips.EraseIndex(index); n_delete(m_ClipControls[index]); m_ClipControls.EraseIndex(index); } } int Animation::GetAnimClipCount() const { return m_AnimClips.Size(); } void Animation::SetLayer(const Resources::ResourceId& name, int layer) { ClipControl* cc = findControl(name); if (cc) { AnimationLayer* al = findLayer(layer); if (NULL == al) { al = buildLayer(layer); } cc->BindLayer(al); } } int Animation::GetLayer(const Resources::ResourceId& name) { ClipControl* cc = findControl(name); if (cc) { return cc->GetLayer()->GetLayerIndex(); } return (int)DefaultLayer; } void Animation::SetPlayRate(const Resources::ResourceId& name, float rate) { ClipControl* cc = findControl(name); if (cc) { cc->SetSpeed(rate); } } void Animation::SetWeight(const Resources::ResourceId& name, float weight) { ClipControl* cc = findControl(name); if (cc) { cc->SetWeight(weight); } } float Animation::GetWeight(const Resources::ResourceId& name) { ClipControl* cc = findControl(name); if (cc) { return cc->GetWeight(); } return 0.0f; } void Animation::SetWrapMode(const Resources::ResourceId& name, int wrapMode) { ClipControl* cc = findControl(name); if (cc) { cc->SetWrapMode(wrapMode); } } int Animation::GetWrapMode(const Resources::ResourceId& name) { ClipControl* cc = findControl(name); if (cc) { return cc->GetWrapMode(); } return (int)ClipControl::PlayUnknown; } void Animation::PlayAnim(const Resources::ResourceId &name, int playMode) { CrossFading(name, playMode, 0.0f); } void Animation::Stop() { _FOREACH_VECTOR_BEGIN(ClipControl*, m_ClipControls, node) (*node)->FadeOut(0.0f); _FOREACH_VECTOR_END(node); m_ToParentTrans.Clear(false); } void Animation::Stop(const Resources::ResourceId& name, float time) { ClipControl* cc = findControl(name); if (cc) { cc->FadeOut(time); } } void Animation::Pause(const Resources::ResourceId& name) { ClipControl* cc = findControl(name); if (cc) { cc->Pause(); } } void Animation::Resume(const Resources::ResourceId& name) { ClipControl* cc = findControl(name); if (cc) { //cc->FadeOut(0.0f); cc->FadeIn(0.0f); } } bool Animation::IsPlaying() { _FOREACH_VECTOR_BEGIN(ClipControl*, m_ClipControls, node) if(ClipControl::US_Playing == (*node)->GetState() || ClipControl::US_End == (*node)->GetState()) { return true; } _FOREACH_VECTOR_END(node); return false; } bool Animation::IsPaused() { _FOREACH_VECTOR_BEGIN(ClipControl*, m_ClipControls, node) if(ClipControl::US_Pause == (*node)->GetState()) { return true; } _FOREACH_VECTOR_END(node); return false; } bool Animation::IsPlaying(const Resources::ResourceId& name) { ClipControl* cc = findControl(name); if (cc) { return (ClipControl::US_Playing == cc->GetState() || ClipControl::US_End == cc->GetState()); } return false; } bool Animation::Contain(const Resources::ResourceId& name) { ClipControl* cc = findControl(name); return cc != NULL; } bool Animation::IsPaused(const Resources::ResourceId& name) { ClipControl* cc = findControl(name); if (cc) { return ClipControl::US_Pause == cc->GetState(); } return false; } void Animation::CrossFading(const Resources::ResourceId &name, int playMode, float fadingTime) { ClipControl* cc = findControl(name); if (cc) { AnimationLayer* layer = cc->GetLayer(); m_bDirty = true; if (ClipControl::StopAllAnim == playMode) { AnimationLayers::Iterator it = m_AnimationLayers.Begin(); while(it != m_AnimationLayers.End()) { if (*it != layer) { (*it)->FadeOutAll(fadingTime); } ++it; } cc->FadeIn(fadingTime); } else if (ClipControl::StopSameAnimLayer == playMode) { cc->FadeIn(fadingTime); } } } void Animation::Blending(const Resources::ResourceId &name, float targetWeight, float time) { ClipControl* cc = findControl(name); if (cc) { m_bDirty = true; cc->Blend(targetWeight, time); } } bool Animation::UpdateAnimation(float time) { m_LocalTimer += time; m_bUpdateResult = false; AnimationLayers::Iterator it = m_AnimationLayers.Begin(); while (it != m_AnimationLayers.End()) { if ((*it)->Update(time)) { m_bUpdateResult = true; } ++it; } if (m_ClientCount == 0) { m_bUpdateResult = false; return false; } if (m_bUpdateResult) { if(m_LocalTimer>m_UpdateTime) { sample(); m_LocalTimer = 0; } else { m_bUpdateResult = false; } } else { m_ToParentTrans.Clear(false); } return m_bUpdateResult; } void Animation::SetTime(const Resources::ResourceId& name, float time) { ClipControl* cc = findControl(name); if (cc) { cc->SetTime(time); } } float Animation::GetWrapTime(const Resources::ResourceId& name) { ClipControl* cc = findControl(name); if (cc) { return cc->GetCurrentWrapTime(); } return 0.0f; } void Animation::SetNormalizedTime(const Resources::ResourceId& name, float time) { ClipControl* cc = findControl(name); if (cc) { cc->SetNormalizeTime(time); } } void Animation::SetEnable(const Resources::ResourceId& name, bool enable) { ///lazy man. } bool Animation::IsEnable(const Resources::ResourceId& name) { ///lazy man. return false; } void Animation::AddAffectedNodes(const Resources::ResourceId &stateName, const Util::String& nodeName, bool recursive) { ClipControl* cc = findControl(stateName); if (cc) { ClipControl::AddAffectedBones(cc, m_NodeParentIndexVec, m_NodeNameVec, nodeName, recursive); } } void Animation::RemoveAffectedNodes(const Resources::ResourceId& stateName, const Util::String& nodeName, bool recursive) { ClipControl* cc = findControl(stateName); if (cc) { ClipControl::RemoveAffectedBones(cc, m_NodeParentIndexVec, m_NodeNameVec, nodeName, recursive); } } bool Animation::IsAnimNodeAffected( const Resources::ResourceId& stateName, const Util::String& nodeName ) { IndexT index = m_NodeNameVec.FindIndex(nodeName); if (InvalidIndex != index) { ClipControl* cc = findControl(stateName); if (cc) { return cc->IsAffected((Bone)index); } } return false; } bool Animation::BuildDefaultToRootTrans() { Math::float3 trans; Math::float3 scale; Math::quaternion rotation; m_DefaultToRootX.Clear(); Util::Array defaultToParentTrans; Util::Array defaultTrans; Util::Array defaultScale; Util::Array defaultRotation; int nodeCount = m_CheckedNodeList.Size(); defaultTrans.Resize(nodeCount, Math::float3(0.0, 0.0, 0.0)); defaultScale.Resize(nodeCount, Math::float3(0.0, 0.0, 0.0)); defaultRotation.Resize(nodeCount, Math::quaternion(0.0, 0.0, 0.0, 0.0)); //Use m_CheckedNodeList's nodeNameŁ¬find nodes in animClip for(int iNode = 0; iNode >& skelTree ) { //clean data m_NodeNameVec.Clear(false); m_NodeParentIndexVec.Clear(false); m_CheckedNodeList.Clear(false); //init m_CheckedNodeList CheckedNode tempNode; m_CheckedNodeList.Resize(skelTree.Size(),tempNode); //find in skelTreeŁ¬build NodeNameVec and NodeParentIndexVec for(int i = 0; iname; m_NodeNameVec.Append(skelTree[i]->name); ushort parentIndex = InvalidBone; if(skelTree[i]->parent.isvalid()) { Util::String parentName = skelTree[i]->parent->name; for(int nodeIndex = 0; nodeIndexpos.x(),skelTree[i]->pos.y(),skelTree[i]->pos.z()); m_CheckedNodeList[i].defRotation = skelTree[i]->rot; m_CheckedNodeList[i].defScale = Math::float3(skelTree[i]->scale.x(),skelTree[i]->scale.y(),skelTree[i]->scale.z()); } ClipControls::Iterator it = m_ClipControls.Begin(); while(it != m_ClipControls.End()) { (*it)->MatchSkeleton(m_NodeNameVec); ++it; } } inline bool _alloc_weight(float* free_weights, int bone_index, float get_from_free, float& out_take_away) { float& free = free_weights[bone_index]; if (free > NO_WEIGHT) { float take_away = free * get_from_free; if (take_away > TINY_WEIGHT) { if (take_away > free) { out_take_away = free; free = NO_WEIGHT; } else { free -= take_away; out_take_away = take_away; } return true; } } out_take_away = NO_WEIGHT; return false; } inline bool _alloc_weight_force(float* free_weights, int bone_index, float& take_away) { if (free_weights[bone_index] > NO_WEIGHT) { if(free_weights[bone_index] > take_away) { free_weights[bone_index] -= take_away; } else { take_away = free_weights[bone_index]; free_weights[bone_index] = NO_WEIGHT; } return true; } return false; } void Animation::blendFrameDataFromFree(int bone_index, float take_from_free, const Math::float3& pos, const Math::quaternion& rotate, const Math::float3& scale) { float take_away = NO_WEIGHT; if(_alloc_weight(&m_FreeWeights[0], bone_index, take_from_free, take_away)) { m_SampledTrans[bone_index] += (pos * take_away); m_SampledScale[bone_index] += (scale * take_away); _quaternion_add_blend(m_SampledRotation[bone_index], rotate, take_away); } } void Animation::blendFrameData(int bone_index, float take_force, const Math::float3& pos, const Math::quaternion& rotate, const Math::float3& scale) { float take_away = NO_WEIGHT; if(_alloc_weight_force(&m_FreeWeights[0], bone_index, take_force)) { m_SampledTrans[bone_index] += (pos * take_force); m_SampledScale[bone_index] += (scale * take_force); _quaternion_add_blend(m_SampledRotation[bone_index], rotate, take_force); } } template void Animation::blendControl(const ClipControl* cc, int nodeCount) { Math::float3 trans; Math::float3 scale; Math::quaternion rotation; float wrap_time = cc->GetCurrentWrapTime(); float weight = cc->GetCurrentWeight(); const Util::Array& bones = cc->GetAffectedBones(); if (bones.Size()) { for (int i = 0; i < bones.Size(); ++i) { Bone bone = bones[i]; if(m_FreeWeights[bone] > NO_WEIGHT) { cc->GetFrameDataNoCheck(wrap_time, bone, trans, rotation, scale); if (check_layer) { blendFrameData(bone, weight * m_LayerWeights[bone], trans, rotation, scale); } else { blendFrameDataFromFree(bone, weight, trans, rotation, scale); } } } } else { for (int bone_index = 0; bone_index < nodeCount; ++bone_index) { if(m_FreeWeights[bone_index] > NO_WEIGHT && cc->GetFrameData(wrap_time, bone_index, trans, rotation, scale)) { if (check_layer) { blendFrameData(bone_index, weight * m_LayerWeights[bone_index], trans, rotation, scale); } else { blendFrameDataFromFree(bone_index, weight, trans, rotation, scale); } } } } } void Animation::buildLayerWeight(const ClipControls& activeControls, int nodeCount) { m_LayerWeights.Clear(false); m_LayerWeights.AppendArray(m_FreeWeights); //m_LayerWeights.Clear(false); //m_LayerWeights.Resize(nodeCount, 0.0f); //ClipControls::Iterator it = activeControls.Begin(); //while(it != activeControls.End()) //{ // float weight = (*it)->GetCurrentWeight(); // const Util::Array& bones = (*it)->GetAffectedBones(); // if (bones.Size()) // { // for (int i = 0; i < bones.Size(); ++i) // { // Bone bone = bones[i]; // m_LayerWeights[bone] += (weight * m_FreeWeights[bone]); // } // } // else // { // for (int bone_index = 0; bone_index < nodeCount; ++bone_index) // { // if ((*it)->ContainBoneInfo(bone_index)) // { // m_LayerWeights[bone_index] += (weight * m_FreeWeights[bone_index]); // } // } // } // ++it; //} } void Animation::sample() { int nodeCount = m_NodeNameVec.Size(); m_ToParentTrans.Clear(false); m_SampledTrans.Clear(false); m_SampledScale.Clear(false); m_SampledRotation.Clear(false); m_FreeWeights.Clear(false); m_SampledTrans.Resize(nodeCount, Math::float3(0.0f, 0.0f, 0.0f)); m_SampledScale.Resize(nodeCount, Math::float3(0.0f, 0.0f, 0.0f)); m_SampledRotation.Resize(nodeCount, Math::quaternion(0.0f, 0.0f, 0.0f,0.0f)); m_FreeWeights.Resize(nodeCount, 1.0f); for (int i = m_AnimationLayers.Size() - 1; i >= 0; --i) { AnimationLayer* layer = m_AnimationLayers[i]; ClipControls& activeControls = layer->GetActiveList(); if (activeControls.Size() == 1) { blendControl(activeControls[0], nodeCount); } else if (activeControls.Size() >= 1) { buildLayerWeight(activeControls, nodeCount); ClipControls::Iterator it = activeControls.Begin(); while(it != activeControls.End()) { blendControl(*it, nodeCount); ++it; } } } for (int iNode = 0; iNode < nodeCount; ++iNode) { if (m_FreeWeights[iNode] > NO_WEIGHT) { m_SampledTrans[iNode] += (m_CheckedNodeList[iNode].defPosition * m_FreeWeights[iNode]); m_SampledScale[iNode] += (m_CheckedNodeList[iNode].defScale * m_FreeWeights[iNode]); _quaternion_add_blend(m_SampledRotation[iNode], m_CheckedNodeList[iNode].defRotation, m_FreeWeights[iNode]); } if(m_SampledRotation[iNode].length() != 0) m_SampledRotation[iNode] = m_SampledRotation[iNode].normalize(m_SampledRotation[iNode]); Math::float4 trans4(m_SampledTrans[iNode].x(), m_SampledTrans[iNode].y(), m_SampledTrans[iNode].z(), 1.0); Math::float4 scale4(m_SampledScale[iNode].x(), m_SampledScale[iNode].y(), m_SampledScale[iNode].z(), 1.0); Math::matrix44 toParent = Math::matrix44::transformation( scale4, m_SampledRotation[iNode], trans4); m_ToParentTrans.Append(toParent); } } void Animation::clearClipControls() { _FOREACH_VECTOR_BEGIN(ClipControl*, m_ClipControls, node) n_delete (*node); _FOREACH_VECTOR_END(node); m_ClipControls.Clear(); } void Animation::clearAnimClips() { m_AnimClips.Clear(); } void Animation::clearLayers() { _FOREACH_VECTOR_BEGIN(AnimationLayer*, m_AnimationLayers, node) n_delete (*node); _FOREACH_VECTOR_END(node); m_AnimationLayers.Clear(); } void Animation::buildControl(AnimationClip* clip, AnimationLayer* layer) { ClipControl* cc = n_new(ClipControl); m_ClipControls.Append(cc); cc->SetClip(clip); cc->BindLayer(layer); if (m_NodeNameVec.Size()) { cc->MatchSkeleton(m_NodeNameVec); } } AnimationLayer* Animation::buildLayer(int index) { n_assert(NULL == findLayer(index)); AnimationLayer* al = n_new(AnimationLayer); al->SetLayerIndex(index); m_AnimationLayers.Append(al); Util::CustomSortArray(m_AnimationLayers); return al; } AnimationLayer* Animation::findLayer(int index) const { _FOREACH_VECTOR_BEGIN(AnimationLayer*, m_AnimationLayers, node) if((*node)->GetLayerIndex() == index) { return *node; } _FOREACH_VECTOR_END(node); return NULL; } AnimationClip* Animation::findClip(const Resources::ResourceId& name) const { _FOREACH_VECTOR_BEGIN(GPtr, m_AnimClips, node) if((*node)->GetName() == name) { return (*node).get_unsafe(); } _FOREACH_VECTOR_END(node); return NULL; } ClipControl* Animation::findControl(const Resources::ResourceId& name) const { _FOREACH_VECTOR_BEGIN(ClipControl*, m_ClipControls, node) if((*node)->GetClip()->GetName() == name) { return (*node); } _FOREACH_VECTOR_END(node); return NULL; } int Animation::findClipIndex(const Resources::ResourceId& name) const { for (int i = 0; i < m_AnimClips.Size(); ++i) { if (m_AnimClips[i]->GetName() == name) { return i; } } return InvalidIndex; } void Animation::SetUpdateTime( float updateTime ) { m_UpdateTime = updateTime; } }