/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_AnimObject.cpp Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 25777 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include namespace { // HACK using namespace nw::gfx; using namespace nw::anim; void ClearMaterialHash(ResAnimGroupMember member) { ResMaterial material(reinterpret_cast(member.GetResMaterialPtr())); switch(member.GetObjectType()) { case ResAnimGroupMember::OBJECT_TYPE_MATERIAL_COLOR: material.SetMaterialColorHash(0x0); break; case ResAnimGroupMember::OBJECT_TYPE_TEXTURE_SAMPLER: material.SetTextureMappersHash(0x0); material.SetTextureSamplersHash(0x0); break; case ResAnimGroupMember::OBJECT_TYPE_TEXTURE_MAPPER: // TODO: サンプラーが変化しない場合はHashの再計算が可能 material.SetTextureMappersHash(0x0); break; case ResAnimGroupMember::OBJECT_TYPE_BLEND_OPERATION: material.SetFragmentOperationHash(0x0); break; case ResAnimGroupMember::OBJECT_TYPE_TEXTURE_COORDINATOR: material.SetTextureCoordinatorsHash(0x0); break; } } } namespace nw { namespace gfx { NW_UT_RUNTIME_TYPEINFO_ROOT_DEFINITION(AnimObject); NW_UT_RUNTIME_TYPEINFO_DEFINITION(BaseAnimEvaluator, AnimObject); NW_UT_RUNTIME_TYPEINFO_DEFINITION(AnimEvaluator , BaseAnimEvaluator); NW_UT_RUNTIME_TYPEINFO_DEFINITION(AnimBlender , AnimObject); NW_UT_RUNTIME_TYPEINFO_DEFINITION(AnimInterpolator , AnimBlender); NW_UT_RUNTIME_TYPEINFO_DEFINITION(AnimAdder , AnimBlender); NW_UT_RUNTIME_TYPEINFO_DEFINITION(AnimOverrider , AnimBlender); //! 汎用アニメーション評価で使用する定数です。 const int BaseAnimEvaluator::NotFoundIndex = -1; namespace { /*!--------------------------------------------------------------------------* @brief タイプに対応するアニメーションのブレンドオペレーションを取得します。 *---------------------------------------------------------------------------*/ anim::AnimBlendOp* GetAnimBlendOpByType(int blendOpType) { static anim::AnimBlendOpBool blendOpBool; static anim::AnimBlendOpInt blendOpInt; static anim::AnimBlendOpFloat blendOpFloat; static anim::AnimBlendOpVector2 blendOpVector2; static anim::AnimBlendOpVector3 blendOpVector3; static anim::AnimBlendOpRgbaColor blendOpRgbaColor; static anim::AnimBlendOpTexture blendOpTexture; static TransformAnimBlendOpStandard blendOpTransform; static TransformAnimBlendOpAccScale blendOpTransformAccScale; static TransformAnimBlendOpQuat blendOpTransformQuat; static TransformAnimBlendOpAccScaleQuat blendOpTransformAccScaleQuat; switch (blendOpType) { case anim::ResAnimGroup::BLENDOP_BOOL: return &blendOpBool; case anim::ResAnimGroup::BLENDOP_INT: return &blendOpInt; case anim::ResAnimGroup::BLENDOP_FLOAT: return &blendOpFloat; case anim::ResAnimGroup::BLENDOP_VECTOR2: return &blendOpVector2; case anim::ResAnimGroup::BLENDOP_VECTOR3: return &blendOpVector3; case anim::ResAnimGroup::BLENDOP_RGBA_COLOR: return &blendOpRgbaColor; case anim::ResAnimGroup::BLENDOP_TEXTURE: return &blendOpTexture; case anim::ResAnimGroup::BLENDOP_CALCULATED_TRANSFORM: return &blendOpTransform; case anim::ResAnimGroup::BLENDOP_CALCULATED_TRANSFORM_ACCURATE_SCALE: return &blendOpTransformAccScale; case anim::ResAnimGroup::BLENDOP_CALCULATED_TRANSFORM_QUAT: return &blendOpTransformQuat; case anim::ResAnimGroup::BLENDOP_CALCULATED_TRANSFORM_ACCURATE_SCALE_QUAT: return &blendOpTransformAccScaleQuat; default: return NULL; } } } // namespace //---------------------------------------------------------- AnimGroup::AnimGroup( anim::ResAnimGroup resAnimGroup, SceneNode* sceneNode, os::IAllocator* allocator) : GfxObject(allocator), m_ResAnimGroup(resAnimGroup), m_SceneNode(sceneNode), m_PreEvaluateCallback(NULL) { } //---------------------------------------------------------- void AnimGroup::GetMemorySizeForInitialize( os::MemorySizeCalculator* pSize, const anim::ResAnimGroup resAnimGroup, bool useOriginalValue) { const int blendOpCount = resAnimGroup.GetBlendOperationsCount(); const int memberCount = resAnimGroup.GetMemberInfoSetCount(); os::MemorySizeCalculator& size = *pSize; size += sizeof(anim::AnimBlendOp*) * blendOpCount; size += sizeof(int) * memberCount; size += sizeof(void*) * memberCount; size += sizeof(void*) * memberCount; if (useOriginalValue) { size += sizeof(void*) * memberCount; } } //---------------------------------------------------------- Result AnimGroup::Initialize(bool useOriginalValue) { Result result = INITIALIZE_RESULT_OK; { const int blendOpCount = m_ResAnimGroup.GetBlendOperationsCount(); NW_ASSERT(blendOpCount > 0); void* memory = GetAllocator().Alloc(sizeof(anim::AnimBlendOp*) * blendOpCount); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_BlendOperations = ut::MoveArray(memory, blendOpCount, &GetAllocator()); for (int blendOpIdx = 0; blendOpIdx < blendOpCount; ++blendOpIdx) { const int blendOpType = m_ResAnimGroup.GetBlendOperations(blendOpIdx); m_BlendOperations.PushBack(GetAnimBlendOpByType(blendOpType)); } } const int memberCount = GetMemberCount(); NW_ASSERT(memberCount > 0); { void* memory = GetAllocator().Alloc(sizeof(int) * memberCount); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_TargetObjectIndicies = ut::MoveArray(memory, memberCount, &GetAllocator()); m_TargetObjectIndicies.Resize(memberCount); } { void* memory = GetAllocator().Alloc(sizeof(void*) * memberCount); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_TargetObjects = ut::MoveArray(memory, memberCount, &GetAllocator()); m_TargetObjects.Resize(memberCount); } { void* memory = GetAllocator().Alloc(sizeof(void*) * memberCount); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_TargetPtrs = ut::MoveArray(memory, memberCount, &GetAllocator()); m_TargetPtrs.Resize(memberCount); } if (useOriginalValue) { void* memory = GetAllocator().Alloc(sizeof(void*) * memberCount); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_OriginalValues = ut::MoveArray(memory, memberCount, &GetAllocator()); m_OriginalValues.Resize(memberCount); } return result; } //---------------------------------------------------------- Result BaseAnimEvaluator::TryBind(AnimGroup* animGroup) { NW_NULL_ASSERT(animGroup); NW_ASSERT(std::strcmp(m_AnimData.GetTargetAnimGroupName(), animGroup->GetName()) == 0); NW_ASSERT(m_AnimGroup == NULL); const int memberCount = animGroup->GetMemberCount(); bool resultResize = m_BindIndexTable.Resize(memberCount); NW_ASSERTMSG(resultResize, "Member count exceeded upper limit. Increase AnimEvaluator::Builder::MaxMembers."); const int animMemberCount = m_AnimData.GetMemberAnimSetCount(); bool resultReverseResize = m_ReverseBindIndexTable.Resize(animMemberCount); NW_ASSERTMSG(resultReverseResize, "Animation member count exceeded upper limit. Increase AnimEvaluator::Builder::MaxAnimMembers."); int boundAnimCount = 0; for (int memberIdx = 0; memberIdx < memberCount; ++memberIdx) { // 見つからなかったときのインデックスで埋める m_BindIndexTable[memberIdx] = NotFoundIndex; } for (int animIdx = 0; animIdx < animMemberCount; ++animIdx) { anim::ResMemberAnim member = m_AnimData.GetMemberAnimSet(animIdx); const int bindTargetIdx = animGroup->GetResAnimGroupMemberIndex(member.GetPath()); // 辞書引きで見つからなかったときは、モデルに対象が存在しない if (bindTargetIdx == -1) { m_ReverseBindIndexTable[animIdx] = NotFoundIndex; continue; } m_BindIndexTable[bindTargetIdx] = animIdx; m_ReverseBindIndexTable[animIdx] = bindTargetIdx; ++boundAnimCount; //HACK anim::ResAnimGroupMember resAnimGroupMember = animGroup->GetResAnimGroupMember(bindTargetIdx); ClearMaterialHash(resAnimGroupMember); } m_AnimGroup = animGroup; // すべてのメンバアニメーションがバインドされました。 if (boundAnimCount == m_AnimData.GetMemberAnimSetCount()) { return Result(BIND_RESULT_OK); } // バインドされたメンバアニメーションが無かったので失敗とします。 if (boundAnimCount == 0) { return Result(BIND_RESULT_NO_MEMBER_BOUND | Result::MASK_FAIL_BIT); } // 上のどちらでもない場合は、バインドには成功したが // すべてのメンバアニメがバインドされなかったことを通知します。 return Result(BIND_RESULT_NOT_ALL_ANIM_MEMBER_BOUND); } //---------------------------------------------------------- void BaseAnimEvaluator::ResetMember(int memberIdx) { NW_NULL_ASSERT(m_AnimGroup); if (!m_AnimGroup->HasOriginalValue()) { return; } anim::ResGraphicsAnimGroup data = m_AnimGroup->GetResGraphicsAnimGroup(); NW_ASSERT(data.IsValid()); // 使用先によってOriginalValueにどの型が入っているかが異なる switch (data.GetTargetType()) { case anim::ResGraphicsAnimGroup::TARGET_TYPE_BONE: case anim::ResGraphicsAnimGroup::TARGET_TYPE_MATERIAL: case anim::ResGraphicsAnimGroup::TARGET_TYPE_VISIBILITY: case anim::ResGraphicsAnimGroup::TARGET_TYPE_LIGHT: case anim::ResGraphicsAnimGroup::TARGET_TYPE_CAMERA: { const ResAnimGroupMember resAnimGroupMember = m_AnimGroup->GetResAnimGroupMember(memberIdx); resAnimGroupMember.SetValueForType( m_AnimGroup->GetTargetObject(memberIdx), m_AnimGroup->GetOriginalValue(memberIdx)); } break; default: // 未対応 // memo: // そもそもHasOriginalValueがfalseならばここにはこないので、 // ここにきたら実装忘れのはず NW_ASSERTMSG(false, "The type[%d] has original value, but there is no implementation.\n", data.GetTargetType()); } m_IsCacheDirty = true; } //---------------------------------------------------------- int AnimEvaluator::GetCacheBufferSizeNeeded() const { return GetCacheBufferSizeNeeded(m_AnimData); } //---------------------------------------------------------- int AnimEvaluator::GetCacheBufferSizeNeeded(const anim::ResAnim& animData) { const int headBytes = anim::AnimResult().GetOffsetToValueBuffer(); int size = 0; const int memberAnimCount = animData.GetMemberAnimSetCount(); for (int animIdx = 0; animIdx < memberAnimCount; ++animIdx) { const anim::ResMemberAnim memberAnim = animData.GetMemberAnimSet(animIdx); const int primBytes = memberAnim.GetPrimitiveSize(); size += ut::RoundUp(headBytes + primBytes, sizeof(bit32)); } return size; } //---------------------------------------------------------- void AnimEvaluator::SetCacheBufferPointers() { const int headBytes = anim::AnimResult().GetOffsetToValueBuffer(); u8* cachePtr = reinterpret_cast(m_CacheBuf); const int memberAnimCount = m_AnimData.GetMemberAnimSetCount(); for (int animIdx = 0; animIdx < memberAnimCount; ++animIdx) { m_CachePtrs[animIdx] = reinterpret_cast(cachePtr); const anim::ResMemberAnim memberAnim = m_AnimData.GetMemberAnimSet(animIdx); const int primBytes = memberAnim.GetPrimitiveSize(); cachePtr += ut::RoundUp(headBytes + primBytes, sizeof(bit32)); } } //---------------------------------------------------------- AnimEvaluator::AnimEvaluator( os::IAllocator* allocator) : BaseAnimEvaluator(allocator), m_CacheBuf(NULL) { } //---------------------------------------------------------- void AnimEvaluator::GetMemorySizeForInitialize( os::MemorySizeCalculator* pSize, const nw::anim::ResAnim& animData, const int maxMembers, const int maxAnimMembers, bool allocCache) { os::MemorySizeCalculator& size = *pSize; BaseAnimEvaluator::GetMemorySizeForInitialize(pSize, maxMembers, maxAnimMembers); size += sizeof(anim::AnimResult*) * maxAnimMembers; if (allocCache) { size += AnimEvaluator::GetCacheBufferSizeNeeded(animData); } } //---------------------------------------------------------- Result AnimEvaluator::Initialize( const nw::anim::ResAnim& animData, const int maxMembers, const int maxAnimMembers, bool allocCache) { // 親クラスの初期化を通す Result result = BaseAnimEvaluator::Initialize(animData, maxMembers, maxAnimMembers); NW_ENSURE_AND_RETURN(result); void* memory = GetAllocator().Alloc(sizeof(anim::AnimResult*) * maxAnimMembers); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_CachePtrs = ut::MoveArray(memory, maxAnimMembers, &GetAllocator()); m_CachePtrs.Resize(animData.GetMemberAnimSetCount()); if (allocCache) { // サイズ0でのAllocを回避する // CreateEmpty~Anim()などを使用した場合に起こりうる if (animData.GetMemberAnimSetCount() != 0) { m_CacheBuf = GetAllocator().Alloc(GetCacheBufferSizeNeeded()); if (m_CacheBuf == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); SetCacheBufferPointers(); } } return result; } //---------------------------------------------------------- const anim::AnimResult* AnimEvaluator::GetResult( void* target, int memberIdx ) const { //----------------------------------------------------------------- // メンバに関連付けられたアニメーションが存在しない場合は // target を変更せずに NULL を返す if (!HasMemberAnim(memberIdx)) { return NULL; } //----------------------------------------------------------------- const anim::AnimBlendOp* blendOp = m_AnimGroup->GetBlendOperation(memberIdx); const int animIdx = m_BindIndexTable[memberIdx]; if (m_CacheBuf != NULL && !m_IsCacheDirty) { //----------------------------------------------------------------- // キャッシュがあればキャッシュの値を返す if (blendOp != NULL) { return m_CachePtrs[animIdx]; } else { anim::ResMemberAnim memberAnim = m_AnimData.GetMemberAnimSet(animIdx); memberAnim.ApplyCacheForType(target, m_CachePtrs[animIdx]); // ブレンドオペレーションがない場合、返り値は使用されないが // ブレンダに評価したことを伝えるために target を返す return reinterpret_cast(target); } } else { //----------------------------------------------------------------- // アニメーションカーブを評価して対象に書き込む anim::ResMemberAnim memberAnim = m_AnimData.GetMemberAnimSet(animIdx); const void* originalValue = m_AnimGroup->HasOriginalValue() ? m_AnimGroup->GetOriginalValue(memberIdx) : NULL; if (blendOp != NULL) { anim::AnimResult* result = reinterpret_cast(target); bit32 flags = memberAnim.EvaluateResultForType( result->GetValueBuffer(), result->GetFlags(), m_AnimFrameController.GetFrame(), originalValue); result->SetFlags(flags); return result; } else { memberAnim.EvaluateResultForType( target, 0, m_AnimFrameController.GetFrame(), originalValue); // ブレンドオペレーションがない場合、返り値は使用されないが // ブレンダに評価したことを伝えるために target を返す return reinterpret_cast(target); } } } //---------------------------------------------------------- void AnimEvaluator::UpdateCache() { if (m_CacheBuf != NULL && m_IsCacheDirty) { int animCount = m_AnimData.GetMemberAnimSetCount(); for (int animIdx = 0; animIdx < animCount; ++animIdx) { anim::AnimResult* result = m_CachePtrs[animIdx]; result->ResetFlags(); // バインドされたメンバーがモデルに存在しない場合もあるのでチェック const int memberIdx = m_ReverseBindIndexTable[animIdx]; if (memberIdx == BaseAnimEvaluator::NotFoundIndex) { continue; } this->GetResult(result, memberIdx); } m_IsCacheDirty = false; } } //---------------------------------------------------------- const anim::AnimResult* AnimInterpolator::GetResult( void* target, int memberIdx ) const { //---------------------------------------------------------- // ブレンドオペレーションがない場合および // ブレンドオペレーションにブレンド処理がない場合は上書き処理 const anim::AnimBlendOp* blendOp = m_AnimGroup->GetBlendOperation(memberIdx); if (blendOp == NULL || !blendOp->HasBlend()) { for (int animIdx = m_AnimObjects.Size() - 1; animIdx >= 0; --animIdx) { if (m_AnimObjects[animIdx] == NULL) { continue; } const anim::AnimResult* childResult = m_AnimObjects[animIdx]->GetResult(target, memberIdx); if (childResult != NULL) { return childResult; } } return NULL; } if (m_IsOldMethod) { // TODO: こちらのブロックを整理。 //---------------------------------------------------------- // 有効な子アニメーションの重みの合計を求める float weightSum = 0.0f; const AnimObject* lastAnimObj = NULL; int validAnimCount = 0; for (int animIdx = 0; animIdx < m_AnimObjects.Size(); ++animIdx) { if (m_AnimObjects[animIdx] == NULL) { continue; } const float weight = m_Weights[animIdx]; if (!AnimWeightNearlyEqualZero(weight)) { if (m_AnimObjects[animIdx]->HasMemberAnim(memberIdx)) { weightSum += weight; lastAnimObj = m_AnimObjects[animIdx]; ++validAnimCount; } } } if (validAnimCount == 0) { // 有効な子アニメーションが存在しない場合は // target を変更せずに NULL を返す return NULL; } else if (validAnimCount == 1) { // 有効な子アニメーションが 1 つだけの場合は // そのアニメーションの結果を返す return lastAnimObj->GetResult(target, memberIdx); } // 重みの合計が 1.0 でない場合は正規化スケールを計算 const float weightNormalizeScale = GetAnimWeightNormalizeScale(weightSum); //---------------------------------------------------------- // 補間は操作空間があればそこで行われるべきなので // target の CONVERTED フラグを退避してからオンに anim::AnimResult* result = reinterpret_cast(target); const bool convertedBak = result->IsEnabledFlags(anim::AnimResult::FLAG_CONVERTED); result->EnableFlags(anim::AnimResult::FLAG_CONVERTED, true); //---------------------------------------------------------- // すべての子アニメーションについてループ anim::AnimResult workResult; bool written = false; float compWeights[anim::AnimResult::MAX_COMPONENTS]; //for (int animIdx = 0; animIdx < m_AnimObjects.Size(); ++animIdx) for (int animIdx = m_AnimObjects.Size() - 1; animIdx >= 0; --animIdx) { if (m_AnimObjects[animIdx] == NULL) { continue; } const float childWeight = m_Weights[animIdx] * weightNormalizeScale; if (!AnimWeightNearlyEqualZero(childWeight)) { // 退避した CONVERTED フラグを work に反映 // 子が CONVERTED を扱う Blender であれば CONVERTED なままの結果が返ってくることを期待 // 子が Evaluator の場合はフラグがオフで返ってくるが blendOp 内で変換されるので問題ない workResult.EnableFlags(anim::AnimResult::FLAG_CONVERTED, convertedBak); // 評価 const anim::AnimResult* childResult = m_AnimObjects[animIdx]->GetResult(&workResult, memberIdx); if (childResult != NULL) { written = true; if (!blendOp->Blend(result, compWeights, childResult, &childWeight)) { break; } } } } //---------------------------------------------------------- // target の CONVERTED フラグを復元 if (!convertedBak) { result->DisableFlags(anim::AnimResult::FLAG_CONVERTED); } if (!written) // 有効な子アニメーションなし { return NULL; } //---------------------------------------------------------- // ブレンド後の処理があれば実行 if (!convertedBak && blendOp->HasPostBlend()) { blendOp->PostBlend(result, compWeights); } return result; } else { bool isValidAnim = false; for (int animIdx = 0; animIdx < m_AnimObjects.Size(); ++animIdx) { if (m_AnimObjects[animIdx] && m_AnimObjects[animIdx]->HasMemberAnim(memberIdx)) { isValidAnim = true; break; } } // 有効な子アニメーションが存在しない場合は // target を変更せずに NULL を返す if (!isValidAnim) { return NULL; } // アニメーション重みの正規化を必要なら行います。 if (m_IsWeightDirty && m_IsWeightNormalizationEnabled) { NormalizeWeight(); } //---------------------------------------------------------- // 補間は操作空間があればそこで行われるべきなので // target の CONVERTED フラグを退避してからオンに anim::AnimResult* result = reinterpret_cast(target); const bool convertedBak = result->IsEnabledFlags(anim::AnimResult::FLAG_CONVERTED); result->EnableFlags(anim::AnimResult::FLAG_CONVERTED, true); //---------------------------------------------------------- // すべての子アニメーションについてループ anim::AnimResult workResult; float compWeights[anim::AnimResult::MAX_COMPONENTS]; //for (int animIdx = 0; animIdx < m_AnimObjects.Size(); ++animIdx) for (int animIdx = m_AnimObjects.Size() - 1; animIdx >= 0; --animIdx) { const float childWeight = m_NormalizedWeights[animIdx]; if (!AnimWeightNearlyEqualZero(childWeight)) { // 退避した CONVERTED フラグを work に反映 // 子が CONVERTED を扱う Blender であれば CONVERTED なままの結果が返ってくることを期待 // 子が Evaluator の場合はフラグがオフで返ってくるが blendOp 内で変換されるので問題ない workResult.EnableFlags(anim::AnimResult::FLAG_CONVERTED, convertedBak); // 評価 const anim::AnimResult* childResult = NULL; if (m_AnimObjects[animIdx] != NULL) { childResult = m_AnimObjects[animIdx]->GetResult(&workResult, memberIdx); } if (childResult == NULL) { NW_ASSERT(GetAnimGroup()->HasOriginalValue()); blendOp->ConvertToAnimResult( &workResult, GetAnimGroup()->GetOriginalValue(memberIdx)); childResult = &workResult; NW_NULL_ASSERT(childResult); } if (!blendOp->Blend(result, compWeights, childResult, &childWeight)) { break; } } } //---------------------------------------------------------- // target の CONVERTED フラグを復元 if (!convertedBak) { result->DisableFlags(anim::AnimResult::FLAG_CONVERTED); } //---------------------------------------------------------- // ブレンド後の処理があれば実行 if (!convertedBak && blendOp->HasPostBlend()) { blendOp->PostBlend(result, compWeights); } return result; } } //---------------------------------------------------------- const anim::AnimResult* AnimAdder::GetResult( void* target, int memberIdx ) const { //---------------------------------------------------------- // ブレンドオペレーションがない場合および // ブレンドオペレーションにブレンド処理がない場合は上書き処理 const anim::AnimBlendOp* blendOp = m_AnimGroup->GetBlendOperation(memberIdx); if (blendOp == NULL || !blendOp->HasBlend()) { for (int animIdx = m_AnimObjects.Size() - 1; animIdx >= 0; --animIdx) { if (m_AnimObjects[animIdx] == NULL) { continue; } const anim::AnimResult* childResult = m_AnimObjects[animIdx]->GetResult(target, memberIdx); if (childResult != NULL) { return childResult; } } return NULL; } //---------------------------------------------------------- // 補間は操作空間があればそこで行われるべきなので // target の CONVERTED フラグを退避してからオンに anim::AnimResult* result = reinterpret_cast(target); const bool convertedBak = result->IsEnabledFlags(anim::AnimResult::FLAG_CONVERTED); result->EnableFlags(anim::AnimResult::FLAG_CONVERTED, true); //---------------------------------------------------------- // すべての子アニメーションについてループ anim::AnimResult workResult; bool written = false; //for (int animIdx = 0; animIdx < m_AnimObjects.Size(); ++animIdx) for (int animIdx = m_AnimObjects.Size() - 1; animIdx >= 0; --animIdx) { if (m_AnimObjects[animIdx] == NULL) { continue; } const float childWeight = m_Weights[animIdx]; if (!AnimWeightNearlyEqualZero(childWeight)) { // 退避した CONVERTED フラグを work に反映 // 子が CONVERTED を扱う Blender であれば CONVERTED なままの結果が返ってくることを期待 // 子が Evaluator の場合はフラグがオフで返ってくるが blendOp 内で変換されるので問題ない workResult.EnableFlags(anim::AnimResult::FLAG_CONVERTED, convertedBak); // 評価 const anim::AnimResult* childResult = m_AnimObjects[animIdx]->GetResult(&workResult, memberIdx); if (childResult != NULL) { written = true; if (!blendOp->Blend(result, NULL, childResult, &childWeight)) { break; } } } } //---------------------------------------------------------- // target の CONVERTED フラグを復元 if (!convertedBak) { result->DisableFlags(anim::AnimResult::FLAG_CONVERTED); } if (!written) // 有効な子アニメーションなし { return NULL; } //---------------------------------------------------------- // ブレンド後の処理があれば実行 if (!convertedBak && blendOp->HasPostBlend()) { blendOp->PostBlend(result, NULL); } return result; } //---------------------------------------------------------- const anim::AnimResult* AnimOverrider::GetResult( void* target, int memberIdx ) const { //---------------------------------------------------------- // ブレンドオペレーションがない場合および // ブレンドオペレーションにブレンド処理がない場合は上書き処理 const anim::AnimBlendOp* blendOp = m_AnimGroup->GetBlendOperation(memberIdx); if (blendOp == NULL || !blendOp->HasBlend()) { for (int animIdx = m_AnimObjects.Size() - 1; animIdx >= 0; --animIdx) { if (m_AnimObjects[animIdx] == NULL) { continue; } const anim::AnimResult* childResult = m_AnimObjects[animIdx]->GetResult(target, memberIdx); if (childResult != NULL) { return childResult; } } return NULL; } //---------------------------------------------------------- // すべての子アニメーションについてループ anim::AnimResult* result = reinterpret_cast(target); anim::AnimResult workResult; bool written = false; for (int animIdx = m_AnimObjects.Size() - 1; animIdx >= 0; --animIdx) { if (m_AnimObjects[animIdx] == NULL) { continue; } const anim::AnimResult* childResult = m_AnimObjects[animIdx]->GetResult(&workResult, memberIdx); if (childResult != NULL) { written = true; if (blendOp->Override(result, childResult)) // 全成分を上書きしたら true が返る { return result; } } } if (!written) // 有効な子アニメーションなし { return NULL; } return result; } //---------------------------------------------------------- void AnimBinding::Evaluate(anim::ResGraphicsAnimGroup::EvaluationTiming timing) { for (int animGroupIdx = 0; animGroupIdx < m_AnimGroups.Size(); ++animGroupIdx) { AnimGroup* animGroup = m_AnimGroups[animGroupIdx]; // アニメーションの有無と評価タイミングをチェック if (animGroup == NULL || animGroup->GetResGraphicsAnimGroup().GetEvaluationTiming() != timing) { continue; } for (int animObjectIdx = 0; animObjectIdx < m_AnimObjectCountPerGroup; ++animObjectIdx) { const int index = animGroupIdx * m_AnimObjectCountPerGroup + animObjectIdx; AnimObject* animObj = m_AnimObjects[index]; if (animObj == NULL) { continue; } // アニメーション評価結果の内部キャッシュが古ければ更新 animObj->UpdateCache(); // 高速化のため、ブレンダの有無・Transformかどうかで分岐 if (ut::IsTypeOf(animObj)) { EvaluateSimple(animGroup, static_cast(animObj)); } else if (ut::IsTypeOf(animObj)) { EvaluateTransformSimple(animGroup, static_cast(animObj)); } else { EvaluateBlender(animGroup, animObj); } } } } //---------------------------------------------------------- void AnimBinding::EvaluateSimple(AnimGroup* animGroup, AnimEvaluator* evaluator) { NW_ASSERT(!(animGroup->GetResAnimGroup().GetFlags() & anim::ResAnimGroup::FLAG_IS_CALCULATED_TRANSFORM)); // 汎用アニメーションを評価 int lastTargetObjIdx = -1; bool targetObjSkipFlag = false; int animCount = evaluator->GetAnimData().GetMemberAnimSetCount(); for (int animIdx = 0; animIdx < animCount; ++animIdx) { int memberIdx = evaluator->ReverseBindIndexTable()[animIdx]; if (memberIdx == BaseAnimEvaluator::NotFoundIndex) { continue; } EvaluateMember(animGroup, memberIdx, evaluator, lastTargetObjIdx, targetObjSkipFlag); } } //---------------------------------------------------------- void AnimBinding::EvaluateTransformSimple(AnimGroup* animGroup, TransformAnimEvaluator* evaluator) { NW_ASSERT(animGroup->GetResAnimGroup().GetFlags() & anim::ResAnimGroup::FLAG_IS_CALCULATED_TRANSFORM); // トランスフォームアニメーションを評価 int animCount = evaluator->GetAnimData().GetMemberAnimSetCount(); for (int animIdx = 0; animIdx < animCount; ++animIdx) { int memberIdx = evaluator->ReverseBindIndexTable()[animIdx]; if (memberIdx == BaseAnimEvaluator::NotFoundIndex) { continue; } EvaluateTransformMemberFast(animGroup, memberIdx, evaluator); } } //---------------------------------------------------------- void AnimBinding::EvaluateBlender(AnimGroup* animGroup, AnimObject* animObj) { if (animGroup->GetResAnimGroup().GetFlags() & anim::ResAnimGroup::FLAG_IS_CALCULATED_TRANSFORM) { // トランスフォームアニメーションを評価 int memberCount = animGroup->GetMemberCount(); for (int memberIdx = 0; memberIdx < memberCount; ++memberIdx) { EvaluateTransformMember(animGroup, memberIdx, animObj); } } else { // 汎用アニメーションを評価 int lastTargetObjIdx = -1; bool targetObjSkipFlag = false; int memberCount = animGroup->GetMemberCount(); for (int memberIdx = 0; memberIdx < memberCount; ++memberIdx) { EvaluateMember(animGroup, memberIdx, animObj, lastTargetObjIdx, targetObjSkipFlag); } } } //---------------------------------------------------------- void AnimBinding::EvaluateTransformMember(AnimGroup* animGroup, int memberIdx, AnimObject* animObj) { AnimGroup::PreEvaluateCallback preEvaluateCallback = animGroup->GetPreEvaluateCallback(); // 評価前コールバックの呼び出し if (preEvaluateCallback != NULL) { const int targetObjIdx = animGroup->GetTargetObjectIndex(memberIdx); if (!preEvaluateCallback(animGroup, targetObjIdx)) { return; } } // 評価 CalculatedTransform* target = static_cast(animGroup->GetTargetPtr(memberIdx)); animObj->GetResult(target, memberIdx); // Setter経由で設定する必要はないので、Setterは呼び出さない // Setter内部で回転行列の生成を行うので、呼び出すとパフォーマンスにも大きく影響する } //---------------------------------------------------------- void AnimBinding::EvaluateTransformMemberFast(AnimGroup* animGroup, int memberIdx, TransformAnimEvaluator* evaluator) { AnimGroup::PreEvaluateCallback preEvaluateCallback = animGroup->GetPreEvaluateCallback(); // 評価前コールバックの呼び出し if (preEvaluateCallback != NULL) { const int targetObjIdx = animGroup->GetTargetObjectIndex(memberIdx); if (!preEvaluateCallback(animGroup, targetObjIdx)) { return; } } // 評価 CalculatedTransform* target = static_cast(animGroup->GetTargetPtr(memberIdx)); evaluator->GetResultFast(target, memberIdx); // Setter経由で設定する必要はないので、Setterは呼び出さない // Setter内部で回転行列の生成を行うので、呼び出すとパフォーマンスにも大きく影響する } //---------------------------------------------------------- void AnimBinding::EvaluateMember(AnimGroup* animGroup, int memberIdx, AnimObject* animObj, int& lastTargetObjIdx, bool& targetObjSkipFlag) { AnimGroup::PreEvaluateCallback preEvaluateCallback = animGroup->GetPreEvaluateCallback(); // 評価前コールバックの呼び出し if (preEvaluateCallback != NULL) { const int targetObjIdx = animGroup->GetTargetObjectIndex(memberIdx); if (targetObjIdx != lastTargetObjIdx) { // 高速化のため前回と対象オブジェクトが異なる場合だけ評価前コールバックを呼ぶ targetObjSkipFlag = !preEvaluateCallback(animGroup, targetObjIdx); lastTargetObjIdx = targetObjIdx; } if (targetObjSkipFlag) { return; } } // 評価 anim::ResAnimGroupMember member = animGroup->GetResAnimGroupMember(memberIdx); void* target = NULL; ut::Offset texturePatternTarget; if (member.GetObjectType() == anim::ResAnimGroupMember::OBJECT_TYPE_TEXTURE_MAPPER && member.GetMemberType() == anim::ResTextureMapperMember::MEMBER_TYPE_TEXTURE) { // テクスチャパターンアニメーションは、targetを上書きせずにSetterのみ呼び出す // // マテリアルのバッファが有効な場合は、targetは動的に確保されたReferenceTextureを指す // それを上書きすると、解放時にアロケータで確保されていないポインタを解放しようとしてクラッシュする target = &texturePatternTarget; } else { target = animGroup->GetTargetPtr(memberIdx); } const anim::AnimBlendOp* blendOp = animGroup->GetBlendOperation(memberIdx); const anim::AnimResult* resultPtr = NULL; if (blendOp != NULL) { // ブレンドオペレーションがある場合は // AnimResult オブジェクト経由でターゲットに書き込む anim::AnimResult result; resultPtr = animObj->GetResult(&result, memberIdx); if (resultPtr != NULL) { blendOp->Apply(target, resultPtr); // VALID フラグがオンの成分のみ書き込み } } else { // ブレンドオペレーションがない場合はターゲットを直接上書き resultPtr = animObj->GetResult(target, memberIdx); } // transformのメンバの場合は、setterを呼びださない。 bool isTransformMember = ( (member.GetObjectType() == anim::ResAnimGroupMember::OBJECT_TYPE_TRANSFORM) && (member.GetMemberType() == anim::ResTransformMember::MEMBER_TYPE_TRANSFORM) ); if (!isTransformMember && resultPtr) { // メモリ書き込み以外の処理が必要なメンバのためにSetterを呼び出す // (コマンドキャッシュの更新などが行われる) // // Setterがtargetも書き換えるので2回書き込んでいる。 // 1回にしたいが、VALIDフラグがオフの成分があるとtargetの値の取得が必要。 // するとバッファの確保 -> Get -> Apply -> Setとなり、かえって遅くなりそう。 void* object = animGroup->GetTargetObject(memberIdx); member.SetValueForType(object, target); } } } // namespace gfx } // namespace nw