/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_TransformAnim.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 namespace nw { namespace gfx { NW_UT_RUNTIME_TYPEINFO_DEFINITION(TransformAnimEvaluator , BaseAnimEvaluator); NW_UT_RUNTIME_TYPEINFO_DEFINITION(TransformAnimInterpolator, AnimInterpolator); NW_UT_RUNTIME_TYPEINFO_DEFINITION(TransformAnimAdder , AnimAdder); NW_UT_RUNTIME_TYPEINFO_DEFINITION(TransformAnimOverrider , AnimOverrider); //! トランスフォームアニメーションのブレンドオペレーションで使用する定数です。 const float TransformAnimBlendOp::WeightDiscard = -1.0f; namespace { const float s_VecSquareLenTol = 1.0e-6f; const bit32 XFORM_CONVERTED = CalculatedTransform::FLAG_CONVERTED_FOR_BLEND; const bit32 XFORM_IGNORE_S = CalculatedTransform::FLAG_IS_IGNORE_SCALE; const bit32 XFORM_IGNORE_R = CalculatedTransform::FLAG_IS_IGNORE_ROTATE; const bit32 XFORM_IGNORE_T = CalculatedTransform::FLAG_IS_IGNORE_TRANSLATE; const bit32 XFORM_IGNORE_SRT = XFORM_IGNORE_S | XFORM_IGNORE_R | XFORM_IGNORE_T; const float WEIGHT_DISCARD = TransformAnimBlendOp::WeightDiscard; /*!--------------------------------------------------------------------------* @brief SRT ごとの重みがすべてゼロかどうか判定します。 @param[in] weights SRT ごとの重みです。 @return 重みがすべてゼロなら true を返します。 *---------------------------------------------------------------------------*/ bool CheckWeightsNearyZero(const float* weights) { return AnimWeightNearlyEqualZero(weights[0]) && AnimWeightNearlyEqualZero(weights[1]) && AnimWeightNearlyEqualZero(weights[2]); } /*!--------------------------------------------------------------------------* @brief 必要があれば SRT ごとの重みを無効化します。 @param[in] weights SRT ごとの重みです。 @param[in] animObj アニメーションオブジェクトです。 *---------------------------------------------------------------------------*/ void DisableSRTWeightsIfNeeded(float* weights, const AnimObject* animObj) { const TransformAnimEvaluator* evaluator = ut::DynamicCast(animObj); if (evaluator != NULL) { if (evaluator->GetIsScaleDisabled()) { weights[0] = WEIGHT_DISCARD; } if (evaluator->GetIsRotateDisabled()) { weights[1] = WEIGHT_DISCARD; } if (evaluator->GetIsTranslateDisabled()) { weights[2] = WEIGHT_DISCARD; } } } /*!--------------------------------------------------------------------------* @brief CalculatedTransform の IGNORE フラグを元に戻します。 *---------------------------------------------------------------------------*/ void RestoreIgnoreFlags(CalculatedTransform* transform, const bit32 flagsBak) { transform->EnableFlags(XFORM_IGNORE_S, (flagsBak & XFORM_IGNORE_S) != 0); transform->EnableFlags(XFORM_IGNORE_R, (flagsBak & XFORM_IGNORE_R) != 0); transform->EnableFlags(XFORM_IGNORE_T, (flagsBak & XFORM_IGNORE_T) != 0); } /*!--------------------------------------------------------------------------* @brief Vector3 の値をすべて Flog で変換します。 *---------------------------------------------------------------------------*/ void FlogVector3(math::VEC3* dst) { dst->x = math::FLog(dst->x); dst->y = math::FLog(dst->y); dst->z = math::FLog(dst->z); } /*!--------------------------------------------------------------------------* @brief Vector3 のブレンド処理を行います。 @param[in,out] dst ブレンド処理結果です。 @param[in] src ブレンド処理の入力です。 @param[in] weight ブレンド比率です。 @param[in] overrideFlag 値を上書き更新するかどうかのフラグです。 *---------------------------------------------------------------------------*/ void BlendVector3( math::VEC3* dst, const math::VEC3* src, const float weight, const bool overrideFlag ) { if (overrideFlag) { VEC3Scale(dst, src, weight); } else { math::VEC3 tmp; VEC3Scale(&tmp, src, weight); VEC3Add(dst, dst, &tmp); } } /*!--------------------------------------------------------------------------* @brief 行列の回転成分にゼロベクトルがあれば補正します。 @param[in,out] transform 対象となるトランスフォームです。 @param[in] useDefaultMtx デフォルトの行列を使用するかどうかです。 false なら回転成分を単位行列にします。 @param[in] defaultMtx デフォルトの行列です。 *---------------------------------------------------------------------------*/ void AdjustZeroRotateMatrix( CalculatedTransform* transform, const bool useDefaultMtx, const math::MTX34& defaultMtx) { if (!transform->IsEnabledFlags(XFORM_IGNORE_R)) { const math::MTX34& m = transform->TransformMatrix(); const math::VEC3* v0 = reinterpret_cast(&m.m[0]); const math::VEC3* v1 = reinterpret_cast(&m.m[1]); const math::VEC3* v2 = reinterpret_cast(&m.m[2]); if (math::VEC3SquareLen(v0) < s_VecSquareLenTol || math::VEC3SquareLen(v1) < s_VecSquareLenTol || math::VEC3SquareLen(v2) < s_VecSquareLenTol) { if (useDefaultMtx) { transform->SetRotateMatrix(defaultMtx); } else { transform->SetRotateMatrix( 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f); } } } } } // namespace //---------------------------------------------------------- void TransformAnimBlendOp::Apply(void* target, const anim::AnimResult* result) const { *reinterpret_cast(target) = *reinterpret_cast(result->GetValueBuffer()); } //---------------------------------------------------------- void TransformAnimBlendOp::ConvertToAnimResult( anim::AnimResult* result, const void* source ) const { *reinterpret_cast(result->GetValueBuffer()) = *reinterpret_cast(source); } //---------------------------------------------------------- void TransformAnimBlendOp::BlendScaleStandard( CalculatedTransform* dst, const CalculatedTransform* src, const float weight ) const { if (weight != WEIGHT_DISCARD) { // スケール値を直接ブレンド BlendVector3(&dst->m_Scale, &src->Scale(), weight, dst->IsEnabledFlags(XFORM_IGNORE_S)); dst->DisableFlags(XFORM_IGNORE_S); dst->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } } //---------------------------------------------------------- void TransformAnimBlendOp::BlendScaleAccurate( CalculatedTransform* dst, const CalculatedTransform* src, const float weight ) const { if (weight != WEIGHT_DISCARD) { // 正確なスケールブレンド // s = Π(s(i)^weight) = exp(Σ(log(s(i)) * weight)) // exp の計算は最後に行う math::VEC3 logScale = src->Scale(); FlogVector3(&logScale); BlendVector3(&dst->m_Scale, &logScale, weight, dst->IsEnabledFlags(XFORM_IGNORE_S)); dst->DisableFlags(XFORM_IGNORE_S); dst->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } } //---------------------------------------------------------- bool TransformAnimBlendOp::PostBlendAccurateScale(CalculatedTransform* transform) const { math::VEC3& scale = transform->m_Scale; scale.x = math::FExp(scale.x); scale.y = math::FExp(scale.y); scale.z = math::FExp(scale.z); transform->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); return true; } //---------------------------------------------------------- void TransformAnimBlendOp::BlendRotateMatrix( CalculatedTransform* dst, const CalculatedTransform* src, const float weight ) const { if (weight != WEIGHT_DISCARD) { math::MTX34& dstMtx = dst->m_TransformMatrix; const math::MTX34& srcMtx = src->TransformMatrix(); if (dst->IsEnabledFlags(XFORM_IGNORE_R)) { dstMtx.f._00 = srcMtx.f._00 * weight; dstMtx.f._01 = srcMtx.f._01 * weight; dstMtx.f._02 = srcMtx.f._02 * weight; dstMtx.f._10 = srcMtx.f._10 * weight; dstMtx.f._11 = srcMtx.f._11 * weight; dstMtx.f._12 = srcMtx.f._12 * weight; // ブレンド後の処理で回転行列を正規直交化するので、 2 行目の計算は不要 } else { dstMtx.f._00 += srcMtx.f._00 * weight; dstMtx.f._01 += srcMtx.f._01 * weight; dstMtx.f._02 += srcMtx.f._02 * weight; dstMtx.f._10 += srcMtx.f._10 * weight; dstMtx.f._11 += srcMtx.f._11 * weight; dstMtx.f._12 += srcMtx.f._12 * weight; // ブレンド後の処理で回転行列を正規直交化するので、 2 行目の計算は不要 } dst->DisableFlags(XFORM_IGNORE_R); dst->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } } //---------------------------------------------------------- void TransformAnimBlendOp::BlendRotateQuaternion( CalculatedTransform* dst, const CalculatedTransform* src, const float weight ) const { if (weight != WEIGHT_DISCARD) { math::MTX34& dstMtx = dst->m_TransformMatrix; const math::MTX34& srcMtx = src->TransformMatrix(); float& addedWeight = dstMtx.f._11; math::QUAT srcQ; if (src->IsEnabledFlags(XFORM_CONVERTED)) { srcQ = math::QUAT(srcMtx.f._00, srcMtx.f._01, srcMtx.f._02, srcMtx.f._10); } else { math::MTX34ToQUAT(&srcQ, &srcMtx); } math::QUAT dstQ; if (dst->IsEnabledFlags(XFORM_IGNORE_R)) { addedWeight = weight; dstQ = srcQ; } else { dstQ = math::QUAT(dstMtx.f._00, dstMtx.f._01, dstMtx.f._02, dstMtx.f._10); addedWeight += weight; const float t = (addedWeight != 0.0f) ? weight / addedWeight : 0.0f; math::QUATSlerp(&dstQ, &dstQ, &srcQ, t); } dstMtx.f._00 = dstQ.x; dstMtx.f._01 = dstQ.y; dstMtx.f._02 = dstQ.z; dstMtx.f._10 = dstQ.w; dst->DisableFlags(XFORM_IGNORE_R); dst->SetTransformMatrix(dstMtx); } } //---------------------------------------------------------- void TransformAnimBlendOp::BlendTranslate( CalculatedTransform* dst, const CalculatedTransform* src, const float weight ) const { if (weight != WEIGHT_DISCARD) { const math::MTX34& dstMtx = dst->TransformMatrix(); const math::MTX34& srcMtx = src->TransformMatrix(); math::VEC3 srcT(srcMtx.f._03, srcMtx.f._13, srcMtx.f._23); if (dst->IsEnabledFlags(XFORM_IGNORE_T)) { VEC3Scale(&srcT, &srcT, weight); dst->SetTranslate(srcT); dst->DisableFlags(XFORM_IGNORE_T); } else { math::VEC3 dstT(dstMtx.f._03, dstMtx.f._13, dstMtx.f._23); BlendVector3(&dstT, &srcT, weight, false); dst->SetTranslate(dstT); } } } //---------------------------------------------------------- bool TransformAnimBlendOp::OverrideTransform( CalculatedTransform* dst, const CalculatedTransform* src, const bit32 blendFlags ) const { const bool needToConverted = dst->IsEnabledFlags(XFORM_CONVERTED) && !src->IsEnabledFlags(XFORM_CONVERTED); math::MTX34 dstMtx = dst->TransformMatrix(); const math::MTX34& srcMtx = src->TransformMatrix(); if ( dst->IsEnabledFlags(XFORM_IGNORE_S) && !src->IsEnabledFlags(XFORM_IGNORE_S)) { dst->SetScale(src->Scale()); dst->DisableFlags(XFORM_IGNORE_S); if (needToConverted && (blendFlags & FLAG_ACCURATE_SCALE)) { math::VEC3& dstScale = dst->m_Scale; FlogVector3(&dstScale); dst->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } } if ( dst->IsEnabledFlags(XFORM_IGNORE_R) && !src->IsEnabledFlags(XFORM_IGNORE_R)) { dstMtx.f._00 = srcMtx.f._00; dstMtx.f._01 = srcMtx.f._01; dstMtx.f._02 = srcMtx.f._02; dstMtx.f._10 = srcMtx.f._10; dstMtx.f._11 = srcMtx.f._11; dstMtx.f._12 = srcMtx.f._12; dstMtx.f._20 = srcMtx.f._20; dstMtx.f._21 = srcMtx.f._21; dstMtx.f._22 = srcMtx.f._22; dst->DisableFlags(XFORM_IGNORE_R); if (needToConverted && (blendFlags & FLAG_QUATERNION_ROTATE)) { dst->RotateMatrixToQuaternion(); } } if ( dst->IsEnabledFlags(XFORM_IGNORE_T) && !src->IsEnabledFlags(XFORM_IGNORE_T)) { dstMtx.f._03 = srcMtx.f._03; dstMtx.f._13 = srcMtx.f._13; dstMtx.f._23 = srcMtx.f._23; dst->DisableFlags(XFORM_IGNORE_T); } dst->SetTransformMatrix(dstMtx); return !dst->IsEnabledFlags(XFORM_IGNORE_S) && !dst->IsEnabledFlags(XFORM_IGNORE_R) && !dst->IsEnabledFlags(XFORM_IGNORE_T); } //---------------------------------------------------------- void TransformAnimEvaluator::EvaluateMemberAnim( CalculatedTransform* result, anim::ResTransformAnim transformAnim, float frame, const math::Transform3* originalTransform, bool writeNoAnimMember ) const { const u32 flags = transformAnim.GetFlags(); const u32 FLAG_TRANSLATE_NOT_EXIST = anim::ResTransformAnimData::FLAG_TRANSLATE_NOT_EXIST; const u32 FLAG_ROTATE_NOT_EXIST = anim::ResTransformAnimData::FLAG_ROTATE_NOT_EXIST; const u32 FLAG_SCALE_NOT_EXIST = anim::ResTransformAnimData::FLAG_SCALE_NOT_EXIST; if (writeNoAnimMember) { math::Transform3 transform; if (m_IsScaleDisabled) { transform.scale = originalTransform->scale; } else { transform.scale.x = (flags & anim::ResTransformAnimData::FLAG_SCALE_X_NOT_EXIST) ? originalTransform->scale.x : transformAnim.EvaluateScaleX(frame); transform.scale.y = (flags & anim::ResTransformAnimData::FLAG_SCALE_Y_NOT_EXIST) ? originalTransform->scale.y : transformAnim.EvaluateScaleY(frame); transform.scale.z = (flags & anim::ResTransformAnimData::FLAG_SCALE_Z_NOT_EXIST) ? originalTransform->scale.z : transformAnim.EvaluateScaleZ(frame); } if (m_IsRotateDisabled) { transform.rotate = originalTransform->rotate; } else { transform.rotate.x = (flags & anim::ResTransformAnimData::FLAG_ROTATE_X_NOT_EXIST) ? originalTransform->rotate.x : transformAnim.EvaluateRotateX(frame); transform.rotate.y = (flags & anim::ResTransformAnimData::FLAG_ROTATE_Y_NOT_EXIST) ? originalTransform->rotate.y : transformAnim.EvaluateRotateY(frame); transform.rotate.z = (flags & anim::ResTransformAnimData::FLAG_ROTATE_Z_NOT_EXIST) ? originalTransform->rotate.z : transformAnim.EvaluateRotateZ(frame); } if (m_IsTranslateDisabled) { transform.translate = originalTransform->translate; } else { transform.translate.x = (flags & anim::ResTransformAnimData::FLAG_TRANSLATE_X_NOT_EXIST) ? originalTransform->translate.x : transformAnim.EvaluateTranslateX(frame); transform.translate.y = (flags & anim::ResTransformAnimData::FLAG_TRANSLATE_Y_NOT_EXIST) ? originalTransform->translate.y : transformAnim.EvaluateTranslateY(frame); transform.translate.z = (flags & anim::ResTransformAnimData::FLAG_TRANSLATE_Z_NOT_EXIST) ? originalTransform->translate.z : transformAnim.EvaluateTranslateZ(frame); } result->SetTransform(transform); } else { if (!m_IsScaleDisabled) { if (!(flags & anim::ResTransformAnimData::FLAG_SCALE_X_NOT_EXIST)) { result->DirectScale().x = transformAnim.EvaluateScaleX(frame); } if (!(flags & anim::ResTransformAnimData::FLAG_SCALE_Y_NOT_EXIST)) { result->DirectScale().y = transformAnim.EvaluateScaleY(frame); } if (!(flags & anim::ResTransformAnimData::FLAG_SCALE_Z_NOT_EXIST)) { result->DirectScale().z = transformAnim.EvaluateScaleZ(frame); } } if (!m_IsRotateDisabled) { if ((flags & FLAG_ROTATE_NOT_EXIST) != FLAG_ROTATE_NOT_EXIST) { const f32 rotX = (flags & anim::ResTransformAnimData::FLAG_ROTATE_X_NOT_EXIST) ? originalTransform->rotate.x : transformAnim.EvaluateRotateX(frame); const f32 rotY = (flags & anim::ResTransformAnimData::FLAG_ROTATE_Y_NOT_EXIST) ? originalTransform->rotate.y : transformAnim.EvaluateRotateY(frame); const f32 rotZ = (flags & anim::ResTransformAnimData::FLAG_ROTATE_Z_NOT_EXIST) ? originalTransform->rotate.z : transformAnim.EvaluateRotateZ(frame); result->SetRotateXYZ(rotX, rotY, rotZ); } } if (!m_IsTranslateDisabled) { if (!(flags & anim::ResTransformAnimData::FLAG_TRANSLATE_X_NOT_EXIST)) { result->DirectTransformMatrix().f._03 = transformAnim.EvaluateTranslateX(frame); } if (!(flags & anim::ResTransformAnimData::FLAG_TRANSLATE_Y_NOT_EXIST)) { result->DirectTransformMatrix().f._13 = transformAnim.EvaluateTranslateY(frame); } if (!(flags & anim::ResTransformAnimData::FLAG_TRANSLATE_Z_NOT_EXIST)) { result->DirectTransformMatrix().f._23 = transformAnim.EvaluateTranslateZ(frame); } } result->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } UpdateFlagsCommon(result); // 回転アニメーションが存在するならば、FLAG_IS_ROTATE_ZERO は無効にする。 if ((flags & FLAG_ROTATE_NOT_EXIST) != FLAG_ROTATE_NOT_EXIST) { result->DisableFlags(CalculatedTransform::FLAG_IS_ROTATE_ZERO); } // 同様に平行移動が存在するならば、FLAG_IS_TRANSLATE_ZERO は無効にする。 if ((flags & FLAG_TRANSLATE_NOT_EXIST) != FLAG_TRANSLATE_NOT_EXIST) { result->DisableFlags(CalculatedTransform::FLAG_IS_TRANSLATE_ZERO); } // 同様にスケールが存在するならば、FLAG_IS_SCALE_ONE は無効にする。 if ((flags & FLAG_SCALE_NOT_EXIST) != FLAG_SCALE_NOT_EXIST) { result->DisableFlags(CalculatedTransform::FLAG_IS_SCALE_ONE); } result->UpdateCompositeFlags(); } //---------------------------------------------------------- void TransformAnimEvaluator::EvaluateMemberBakedAnim( CalculatedTransform* result, anim::ResBakedTransformAnim transformAnim, float frame, const math::Transform3* originalTransform, bool writeNoAnimMember ) const { const u32 flags = transformAnim.GetFlags(); bit32 frameFlags = 0; if (!writeNoAnimMember) { // OriginalValueが書き込み済みの場合は、 // アニメのない要素のフラグを再利用する frameFlags = result->GetFlags(); } if (!(flags & anim::ResBakedTransformAnimData::FLAG_ROTATE_NOT_EXIST)) { frameFlags = ut::DisableFlag(frameFlags, gfx::CalculatedTransform::FLAG_IS_ROTATE_ZERO); transformAnim.EvaluateRotate(&result->m_TransformMatrix, &frameFlags, frame); result->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } if (!(flags & anim::ResBakedTransformAnimData::FLAG_TRANSLATE_NOT_EXIST)) { frameFlags = ut::DisableFlag(frameFlags, gfx::CalculatedTransform::FLAG_IS_TRANSLATE_ZERO); transformAnim.EvaluateTranslate(&result->m_TransformMatrix, &frameFlags, frame); result->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } if (!(flags & anim::ResBakedTransformAnimData::FLAG_SCALE_NOT_EXIST)) { frameFlags = ut::DisableFlag(frameFlags, gfx::CalculatedTransform::FLAG_IS_UNIFORM_SCALE | gfx::CalculatedTransform::FLAG_IS_SCALE_ONE); transformAnim.EvaluateScale(&result->m_Scale, &frameFlags, frame); result->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } if (writeNoAnimMember) { if (flags & anim::ResBakedTransformAnimData::FLAG_ROTATE_NOT_EXIST) { // TODO: 回転行列の生成は重いので、originalValueをCalculatedTransform型で保存するようにする result->SetRotateXYZ( originalTransform->rotate.x, originalTransform->rotate.y, originalTransform->rotate.z); if (originalTransform->rotate.IsZero()) { frameFlags |= gfx::CalculatedTransform::FLAG_IS_ROTATE_ZERO; } } if (flags & anim::ResBakedTransformAnimData::FLAG_TRANSLATE_NOT_EXIST) { result->SetTranslate(originalTransform->translate); if (originalTransform->translate.IsZero()) { frameFlags |= gfx::CalculatedTransform::FLAG_IS_TRANSLATE_ZERO; } } if (flags & anim::ResBakedTransformAnimData::FLAG_SCALE_NOT_EXIST) { result->SetScale(originalTransform->scale); const math::Vector3& scale = originalTransform->scale; if (scale.x == scale.y && scale.x == scale.z) { frameFlags |= gfx::CalculatedTransform::FLAG_IS_UNIFORM_SCALE; if (scale.x == 1.f) { frameFlags |= gfx::CalculatedTransform::FLAG_IS_SCALE_ONE; } } } } ApplyBakedFlags(result, frameFlags); } //---------------------------------------------------------- void TransformAnimEvaluator::UpdateFlagsCommon(CalculatedTransform* transform) const { transform->DisableFlags(XFORM_CONVERTED); if (m_IsScaleDisabled) { transform->EnableFlags(XFORM_IGNORE_S); } else { transform->DisableFlags(XFORM_IGNORE_S); } if (m_IsRotateDisabled) { transform->EnableFlags(XFORM_IGNORE_R); } else { transform->DisableFlags(XFORM_IGNORE_R); } if (m_IsTranslateDisabled) { transform->EnableFlags(XFORM_IGNORE_T); } else { transform->DisableFlags(XFORM_IGNORE_T); } } //---------------------------------------------------------- void TransformAnimEvaluator::UpdateFlags(CalculatedTransform* transform) const { UpdateFlagsCommon(transform); transform->UpdateFlags(); } //---------------------------------------------------------- void TransformAnimEvaluator::ApplyBakedFlags(CalculatedTransform* transform, bit32 flags) const { UpdateFlagsCommon(transform); // Bake済みデータで更新する anim::ResBakedTransformAnim::ApplyBakedFlags(transform, flags); } //---------------------------------------------------------- const anim::AnimResult* TransformAnimEvaluator::GetResult( void* target, int memberIdx ) const { return GetResultCommon(target, memberIdx, true); } //---------------------------------------------------------- void TransformAnimEvaluator::ResetNoAnimMember(AnimGroup* animGroup, anim::ResAnim animData) { using namespace anim; for (int memberIdx = 0; memberIdx < animGroup->GetMemberCount(); ++memberIdx) { const int animIdx = m_BindIndexTable[memberIdx]; if (animIdx == NotFoundIndex){ continue; } ResMemberAnim memberAnim = animData.GetMemberAnimSet(animIdx); switch (memberAnim.GetPrimitiveType()) { case ResMemberAnim::PRIMITIVETYPE_TRANSFORM: case ResMemberAnim::PRIMITIVETYPE_BAKED_TRANSFORM: { u32 rotateAndTranslateMask; u32 scaleMask; if (memberAnim.GetPrimitiveType() == ResMemberAnim::PRIMITIVETYPE_TRANSFORM) { rotateAndTranslateMask = ResTransformAnimData::FLAG_ROTATE_NOT_EXIST | ResTransformAnimData::FLAG_TRANSLATE_NOT_EXIST; scaleMask = ResTransformAnimData::FLAG_SCALE_NOT_EXIST; } else { rotateAndTranslateMask = ResBakedTransformAnimData::FLAG_ROTATE_NOT_EXIST | ResBakedTransformAnimData::FLAG_TRANSLATE_NOT_EXIST; scaleMask = ResBakedTransformAnimData::FLAG_SCALE_NOT_EXIST; } CalculatedTransform* target = static_cast(animGroup->GetTargetPtr(memberIdx)); const math::Transform3* originalValue = static_cast(animGroup->GetOriginalValue(memberIdx)); const u32 flags = memberAnim.GetFlags(); bool modified = false; if (flags & rotateAndTranslateMask) { // TODO: 回転行列の生成は重いので、originalValueをCalculatedTransform型で保存するようにする target->SetRotateAndTranslate(originalValue->rotate, originalValue->translate); modified = true; } if (flags & scaleMask) { target->SetScale(originalValue->scale); modified = true; } if (modified) { UpdateFlags(target); if (!m_CacheTransforms.Empty()) { m_CacheTransforms[animIdx] = *target; } } } break; default: NW_ASSERT(false); } } } //---------------------------------------------------------- const anim::AnimResult* TransformAnimEvaluator::GetResultFast(void* target, int memberIdx) const { return GetResultCommon(target, memberIdx, false); } //---------------------------------------------------------- const anim::AnimResult* TransformAnimEvaluator::GetResultCommon(void* target, int memberIdx, bool writeNoAnimMember) const { using namespace anim; //----------------------------------------------------------------- // メンバに関連付けられたアニメーションが存在しない場合は // target を変更せずに NULL を返す if (!HasMemberAnim(memberIdx)) { return NULL; } //----------------------------------------------------------------- CalculatedTransform* transform = reinterpret_cast(target); if (!m_CacheTransforms.Empty() && !m_IsCacheDirty && m_AnimData.ptr() != NULL) { //----------------------------------------------------------------- // キャッシュがあればキャッシュの値を target にコピー const int animIdx = m_BindIndexTable[memberIdx]; *transform = m_CacheTransforms[animIdx]; } else { //----------------------------------------------------------------- // アニメーションカーブを評価して対象に書き込む const math::Transform3* originalValue = static_cast(m_AnimGroup->GetOriginalValue(memberIdx)); if (m_AnimData.ptr() != NULL) { const int animIdx = m_BindIndexTable[memberIdx]; ResMemberAnim memberAnim = m_AnimData.GetMemberAnimSet(animIdx); switch (memberAnim.GetPrimitiveType()) { case ResMemberAnim::PRIMITIVETYPE_TRANSFORM: { ResTransformAnim transformAnim = static_cast(memberAnim); EvaluateMemberAnim(transform, transformAnim, m_AnimFrameController.GetFrame(), originalValue, writeNoAnimMember); } break; case ResMemberAnim::PRIMITIVETYPE_BAKED_TRANSFORM: { ResBakedTransformAnim transformAnim = static_cast(memberAnim); EvaluateMemberBakedAnim(transform, transformAnim, m_AnimFrameController.GetFrame(), originalValue, writeNoAnimMember); } break; default: NW_ASSERT(false); break; } } else { // アニメーションデータがない場合はオリジナル値をコピー transform->SetTransform(*originalValue); UpdateFlags(transform); } } // target を AnimResult にキャストして返す(親のブレンダで使用) return reinterpret_cast(target); } //---------------------------------------------------------- Result TransformAnimEvaluator::TryBind(AnimGroup* animGroup) { Result result; NW_NULL_ASSERT(m_AnimData.ptr()); result = BaseAnimEvaluator::TryBind(animGroup); if (result.IsFailure()) { return result; } ResetNoAnimMember(animGroup, m_AnimData); m_AnimGroup = animGroup; return result; } //---------------------------------------------------------- void TransformAnimEvaluator::UpdateCache() { if (!m_CacheTransforms.Empty() && m_IsCacheDirty) { if (m_AnimData.ptr() != NULL) { for (int memberIdx = 0; memberIdx < m_AnimGroup->GetMemberCount(); ++memberIdx) { const int animIdx = m_BindIndexTable[memberIdx]; if (animIdx != NotFoundIndex) { GetResult(&m_CacheTransforms[animIdx], memberIdx); } } } m_IsCacheDirty = false; } } //---------------------------------------------------------- const anim::AnimResult* TransformAnimInterpolator::GetResult( void* target, int memberIdx ) const { //---------------------------------------------------------- // ブレンドオペレーションを取得 const anim::AnimBlendOp* blendOp = m_AnimGroup->GetBlendOperation(memberIdx); 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 (CheckWeightsNearyZero(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(XFORM_CONVERTED); transform->EnableFlags(XFORM_CONVERTED); //---------------------------------------------------------- // target の IGNORE フラグをオンにして未書き込み状態とする transform->EnableFlags(XFORM_IGNORE_SRT); //---------------------------------------------------------- // すべての子アニメーションについてループ CalculatedTransform workResult; bool written = false; bool firstRotateFlag = true; math::MTX34 firstRotateMtx; //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] }; DisableSRTWeightsIfNeeded(srcWeights, animObj); if (!CheckWeightsNearyZero(srcWeights)) { // 退避した CONVERTED フラグを work に反映 // 子が CONVERTED を扱う Blender であれば CONVERTED なままの結果が返ってくることを期待 // 子が Evaluator の場合はフラグがオフで返ってくるが blendOp 内で変換されるので問題ない workResult.EnableFlags(XFORM_CONVERTED, 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(XFORM_IGNORE_R)) { firstRotateFlag = false; firstRotateMtx = workResult.TransformMatrix(); } // ブレンド if (!blendOp->Blend(reinterpret_cast(transform), NULL, childResult, srcWeights)) { break; } } } } //---------------------------------------------------------- // target の CONVERTED フラグを復元 if (!convertedBak) { transform->DisableFlags(XFORM_CONVERTED); } if (!written) // 有効な子アニメーションなし { RestoreIgnoreFlags(transform, flagsBak); return NULL; } //---------------------------------------------------------- // ブレンド後の処理があれば実行 if (!convertedBak && blendOp->HasPostBlend()) { if (!blendOp->PostBlend(reinterpret_cast(transform), NULL)) { // ブレンド結果の回転行列の行がゼロベクトルなら // 最初のアニメーションの回転行列を採用します。 // 結果が品質上問題となる場合は // 元データを微妙にずらして回避する必要があります。 AdjustZeroRotateMatrix(transform, !firstRotateFlag, firstRotateMtx); } } transform->UpdateFlags(); } 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(XFORM_CONVERTED); transform->EnableFlags(XFORM_CONVERTED); //---------------------------------------------------------- // target の IGNORE フラグをオンにして未書き込み状態とする transform->EnableFlags(XFORM_IGNORE_SRT); //---------------------------------------------------------- // すべての子アニメーションについてループ CalculatedTransform workResult; bool firstRotateFlag = true; math::MTX34 firstRotateMtx; 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 (!CheckWeightsNearyZero(srcWeights)) { // 退避した CONVERTED フラグを work に反映 // 子が CONVERTED を扱う Blender であれば CONVERTED なままの結果が返ってくることを期待 // 子が Evaluator の場合はフラグがオフで返ってくるが blendOp 内で変換されるので問題ない workResult.EnableFlags(XFORM_CONVERTED, 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.UpdateFlags(); childResult = reinterpret_cast(&workResult); } const bool evaluatorFlag = ut::DynamicCast(animObj) != NULL; // ブレンド結果の回転行列の行がゼロベクトルになった場合のために // 最初のアニメーションの回転行列を保存しておく if (evaluatorFlag && firstRotateFlag && !workResult.IsEnabledFlags(XFORM_IGNORE_R)) { firstRotateFlag = false; firstRotateMtx = workResult.TransformMatrix(); } // ブレンド if (!blendOp->Blend(reinterpret_cast(transform), NULL, childResult, srcWeights)) { break; } } } //---------------------------------------------------------- // target の CONVERTED フラグを復元 if (!convertedBak) { transform->DisableFlags(XFORM_CONVERTED); } //---------------------------------------------------------- // ブレンド後の処理があれば実行 if (!convertedBak && blendOp->HasPostBlend()) { if (!blendOp->PostBlend(reinterpret_cast(transform), NULL)) { // ブレンド結果の回転行列の行がゼロベクトルなら // 最初のアニメーションの回転行列を採用します。 // 結果が品質上問題となる場合は // 元データを微妙にずらして回避する必要があります。 AdjustZeroRotateMatrix(transform, !firstRotateFlag, firstRotateMtx); } } transform->UpdateFlags(); } // target を AnimResult にキャストして返す(親のブレンダで使用) return reinterpret_cast(target); } //---------------------------------------------------------- const anim::AnimResult* TransformAnimAdder::GetResult( void* target, int memberIdx ) const { //---------------------------------------------------------- // ブレンドオペレーションを取得 const anim::AnimBlendOp* blendOp = m_AnimGroup->GetBlendOperation(memberIdx); //---------------------------------------------------------- // 補間は操作空間があればそこで行われるべきなので // target の CONVERTED フラグを退避してからオンに CalculatedTransform* transform = reinterpret_cast(target); const bit32 flagsBak = transform->GetFlags(); const bool convertedBak = transform->IsEnabledFlags(XFORM_CONVERTED); transform->EnableFlags(XFORM_CONVERTED); //---------------------------------------------------------- // target の IGNORE フラグをオンにして未書き込み状態とする transform->EnableFlags(XFORM_IGNORE_SRT); //---------------------------------------------------------- // すべての子アニメーションについてループ CalculatedTransform workResult; bool written = false; bool firstRotateFlag = true; math::MTX34 firstRotateMtx; //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]; float srcWeights[3] = { childWeight, childWeight, childWeight }; DisableSRTWeightsIfNeeded(srcWeights, animObj); if (!CheckWeightsNearyZero(srcWeights)) { // 退避した CONVERTED フラグを work に反映 // 子が CONVERTED を扱う Blender であれば CONVERTED なままの結果が返ってくることを期待 // 子が Evaluator の場合はフラグがオフで返ってくるが blendOp 内で変換されるので問題ない workResult.EnableFlags(XFORM_CONVERTED, 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(XFORM_IGNORE_R)) { firstRotateFlag = false; firstRotateMtx = workResult.TransformMatrix(); } // ブレンド if (!blendOp->Blend(reinterpret_cast(transform), NULL, childResult, srcWeights)) { break; } } } } //---------------------------------------------------------- // target の CONVERTED フラグを復元 if (!convertedBak) { transform->DisableFlags(XFORM_CONVERTED); } if (!written) // 有効な子アニメーションなし { RestoreIgnoreFlags(transform, flagsBak); return NULL; } //---------------------------------------------------------- // ブレンド後の処理があれば実行 if (!convertedBak && blendOp->HasPostBlend()) { if (!blendOp->PostBlend(reinterpret_cast(transform), NULL)) { // ブレンド結果の回転行列の行がゼロベクトルなら // 最初のアニメーションの回転行列を採用します。 // 結果が品質上問題となる場合は // 元データを微妙にずらして回避する必要があります。 AdjustZeroRotateMatrix(transform, !firstRotateFlag, firstRotateMtx); } } transform->UpdateFlags(); // target を AnimResult にキャストして返す(親のブレンダで使用) return reinterpret_cast(target); } //---------------------------------------------------------- const anim::AnimResult* TransformAnimOverrider::GetResult( void* target, int memberIdx ) const { //---------------------------------------------------------- // ブレンドオペレーションを取得 const anim::AnimBlendOp* blendOp = m_AnimGroup->GetBlendOperation(memberIdx); //---------------------------------------------------------- // target の IGNORE フラグをオンにして未書き込み状態とする CalculatedTransform* transform = reinterpret_cast(target); const bit32 flagsBak = transform->GetFlags(); transform->EnableFlags(XFORM_IGNORE_SRT); //---------------------------------------------------------- // すべての子アニメーションについてループ CalculatedTransform workResult; bool written = false; for (int animIdx = m_AnimObjects.Size() - 1; animIdx >= 0; --animIdx) { const AnimObject* animObj = m_AnimObjects[animIdx]; if (animObj == NULL) { continue; } float srcWeights[3] = { 1.0f, 1.0f, 1.0f }; DisableSRTWeightsIfNeeded(srcWeights, animObj); if (!CheckWeightsNearyZero(srcWeights)) { // 評価 const anim::AnimResult* childResult = animObj->GetResult(&workResult, memberIdx); if (childResult != NULL) { written = true; if (blendOp->Override(reinterpret_cast(transform), childResult)) // 全成分を上書きしたら true が返る { break; } } } } if (!written) // 有効な子アニメーションなし { RestoreIgnoreFlags(transform, flagsBak); return NULL; } transform->UpdateFlags(); // target を AnimResult にキャストして返す(親のブレンダで使用) return reinterpret_cast(target); } } // namespace gfx } // namespace nw