/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_TransformAnimInterpolator.cpp Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo and/or its licensed developers and are protected by national and international copyright laws. 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. The content herein is highly confidential and should be handled accordingly. $Revision: 31311 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include #include #include #include namespace nw { namespace gfx { NW_UT_RUNTIME_TYPEINFO_DEFINITION(TransformAnimInterpolator, AnimInterpolator); //---------------------------------------------------------- const anim::AnimResult* TransformAnimInterpolator::GetResult( void* target, int memberIdx ) const { //---------------------------------------------------------- // ブレンドオペレーションを取得 const anim::AnimBlendOp* blendOp = m_AnimGroup->GetBlendOperation(memberIdx); const bit32 TRANSFORM_FLAG_MASK = CalculatedTransform::FLAG_IS_IDENTITY | CalculatedTransform::FLAG_IS_ROTATE_TRANSLATE_ZERO | CalculatedTransform::FLAG_IS_ROTATE_ZERO | CalculatedTransform::FLAG_IS_TRANSLATE_ZERO | CalculatedTransform::FLAG_IS_SCALE_ONE | CalculatedTransform::FLAG_IS_UNIFORM_SCALE; if (m_IsOldMethod) { // TODO: こちらのブロックを整理。 //---------------------------------------------------------- // 有効な子アニメーションの重みの合計を SRT ごとに求める float weightSums[3] = { 0.0f, 0.0f, 0.0f }; for (int animIdx = 0; animIdx < m_AnimObjects.Size(); ++animIdx) { if (m_AnimObjects[animIdx] == NULL) { continue; } const float weight = m_Weights[animIdx]; if (!AnimWeightNearlyEqualZero(weight)) { const AnimObject* animObj = m_AnimObjects[animIdx]; if (animObj->HasMemberAnim(memberIdx)) { const TransformAnimEvaluator* evaluator = ut::DynamicCast(animObj); if (evaluator != NULL) { if (!evaluator->GetIsScaleDisabled()) { weightSums[0] += weight; } if (!evaluator->GetIsRotateDisabled()) { weightSums[1] += weight; } if (!evaluator->GetIsTranslateDisabled()) { weightSums[2] += weight; } } else { weightSums[0] += weight; weightSums[1] += weight; weightSums[2] += weight; } } } } // 有効な子アニメーションが存在しない場合は // target を変更せずに NULL を返す if (TransformAnimEvaluator::CheckWeightsNearlyZero(weightSums)) { return NULL; } // 重みの合計が 1.0 でない場合は正規化スケールを計算 float weightNormalizeScale[3] = { GetAnimWeightNormalizeScale(weightSums[0]), GetAnimWeightNormalizeScale(weightSums[1]), GetAnimWeightNormalizeScale(weightSums[2]) }; //---------------------------------------------------------- // 補間は操作空間があればそこで行われるべきなので // target の CONVERTED フラグを退避してからオンに CalculatedTransform* transform = reinterpret_cast(target); const bit32 flagsBak = transform->GetFlags(); const bool convertedBak = transform->IsEnabledFlags(CalculatedTransform::FLAG_CONVERTED_FOR_BLEND); transform->EnableFlags(CalculatedTransform::FLAG_CONVERTED_FOR_BLEND); //---------------------------------------------------------- // target の IGNORE フラグをオンにして未書き込み状態とする transform->EnableFlags(CalculatedTransform::FLAG_IS_IGNORE_ALL); //---------------------------------------------------------- // すべての子アニメーションについてループ CalculatedTransform workResult; bool written = false; bool firstRotateFlag = true; math::MTX34 firstRotateMtx; bit32 transformFlag = TRANSFORM_FLAG_MASK; //for (int animIdx = 0; animIdx < m_AnimObjects.Size(); ++animIdx) for (int animIdx = m_AnimObjects.Size() - 1; animIdx >= 0; --animIdx) { const AnimObject* animObj = m_AnimObjects[animIdx]; if (animObj == NULL) { continue; } const float childWeight = m_Weights[animIdx]; NW_ASSERT(childWeight >= 0.0f); float srcWeights[3] = { childWeight * weightNormalizeScale[0], childWeight * weightNormalizeScale[1], childWeight * weightNormalizeScale[2] }; TransformAnimEvaluator::DisableSRTWeightsIfNeeded(srcWeights, animObj); if (!TransformAnimEvaluator::CheckWeightsNearlyZero(srcWeights)) { // 退避した CONVERTED フラグを work に反映 // 子が CONVERTED を扱う Blender であれば CONVERTED なままの結果が返ってくることを期待 // 子が Evaluator の場合はフラグがオフで返ってくるが blendOp 内で変換されるので問題ない workResult.EnableFlags(CalculatedTransform::FLAG_CONVERTED_FOR_BLEND, convertedBak); // 評価 const anim::AnimResult* childResult = animObj->GetResult(&workResult, memberIdx); if (childResult != NULL) { written = true; const bool evaluatorFlag = ut::DynamicCast(animObj) != NULL; // ブレンド結果の回転行列の行がゼロベクトルになった場合のために // 最初のアニメーションの回転行列を保存しておく if (evaluatorFlag && firstRotateFlag && !workResult.IsEnabledFlags(CalculatedTransform::FLAG_IS_IGNORE_ROTATE)) { firstRotateFlag = false; firstRotateMtx = workResult.TransformMatrix(); } // ブレンド if (!blendOp->Blend(reinterpret_cast(transform), NULL, childResult, srcWeights)) { break; } transformFlag &= workResult.GetFlags(); } } } //---------------------------------------------------------- // target の CONVERTED フラグを復元 if (!convertedBak) { transform->DisableFlags(CalculatedTransform::FLAG_CONVERTED_FOR_BLEND); } if (!written) // 有効な子アニメーションなし { transform->RestoreFlags(CalculatedTransform::FLAG_IS_IGNORE_ALL, flagsBak); return NULL; } //---------------------------------------------------------- // ブレンド後の処理があれば実行 if (!convertedBak && blendOp->HasPostBlend()) { if (!blendOp->PostBlend(reinterpret_cast(transform), NULL)) { // ブレンド結果の回転行列の行がゼロベクトルなら // 最初のアニメーションの回転行列を採用します。 // 結果が品質上問題となる場合は // 元データを微妙にずらして回避する必要があります。 transform->AdjustZeroRotateMatrix(!firstRotateFlag, firstRotateMtx); } } transform->RestoreFlags(TRANSFORM_FLAG_MASK, transformFlag); } 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 フラグを退避してからオンに CalculatedTransform* transform = reinterpret_cast(target); const bit32 flagsBak = transform->GetFlags(); const bool convertedBak = transform->IsEnabledFlags(CalculatedTransform::FLAG_CONVERTED_FOR_BLEND); transform->EnableFlags(CalculatedTransform::FLAG_CONVERTED_FOR_BLEND); //---------------------------------------------------------- // target の IGNORE フラグをオンにして未書き込み状態とする transform->EnableFlags(CalculatedTransform::FLAG_IS_IGNORE_ALL); //---------------------------------------------------------- // すべての子アニメーションについてループ CalculatedTransform workResult; bool firstRotateFlag = true; math::MTX34 firstRotateMtx; bit32 transformFlag = TRANSFORM_FLAG_MASK; for (int animIdx = m_AnimObjects.Size() - 1; animIdx >= 0; --animIdx) { const AnimObject* animObj = m_AnimObjects[animIdx]; const float childWeight = m_NormalizedWeights[animIdx]; NW_ASSERT(childWeight >= 0.0f); const float srcWeights[3] = { childWeight, childWeight, childWeight }; if (!TransformAnimEvaluator::CheckWeightsNearlyZero(srcWeights)) { // 退避した CONVERTED フラグを work に反映 // 子が CONVERTED を扱う Blender であれば CONVERTED なままの結果が返ってくることを期待 // 子が Evaluator の場合はフラグがオフで返ってくるが blendOp 内で変換されるので問題ない workResult.EnableFlags(CalculatedTransform::FLAG_CONVERTED_FOR_BLEND, convertedBak); // 評価 const anim::AnimResult* childResult = NULL; if (animObj != NULL) { childResult = animObj->GetResult(&workResult, memberIdx); } if (childResult == NULL) { const math::Transform3* originalValue = static_cast(GetAnimGroup()->GetOriginalValue(memberIdx)); workResult.SetTransform(*originalValue); workResult.UpdateScaleFlags(); workResult.UpdateRotateFlagsStrictly(); workResult.UpdateTranslateFlags(); workResult.UpdateCompositeFlags(); childResult = reinterpret_cast(&workResult); } const bool evaluatorFlag = ut::DynamicCast(animObj) != NULL; // ブレンド結果の回転行列の行がゼロベクトルになった場合のために // 最初のアニメーションの回転行列を保存しておく if (evaluatorFlag && firstRotateFlag && !workResult.IsEnabledFlags(CalculatedTransform::FLAG_IS_IGNORE_ROTATE)) { firstRotateFlag = false; firstRotateMtx = workResult.TransformMatrix(); } // ブレンド if (!blendOp->Blend(reinterpret_cast(transform), NULL, childResult, srcWeights)) { break; } transformFlag &= workResult.GetFlags(); } } //---------------------------------------------------------- // target の CONVERTED フラグを復元 if (!convertedBak) { transform->DisableFlags(CalculatedTransform::FLAG_CONVERTED_FOR_BLEND); } //---------------------------------------------------------- // ブレンド後の処理があれば実行 if (!convertedBak && blendOp->HasPostBlend()) { if (!blendOp->PostBlend(reinterpret_cast(transform), NULL)) { // ブレンド結果の回転行列の行がゼロベクトルなら // 最初のアニメーションの回転行列を採用します。 // 結果が品質上問題となる場合は // 元データを微妙にずらして回避する必要があります。 transform->AdjustZeroRotateMatrix(!firstRotateFlag, firstRotateMtx); } } transform->RestoreFlags(TRANSFORM_FLAG_MASK, transformFlag); } // target を AnimResult にキャストして返す(親のブレンダで使用) return reinterpret_cast(target); } } // namespace gfx } // namespace nw