/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_TransformAnimEvaluator.h 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 $ *---------------------------------------------------------------------------*/ #ifndef NW_GFX_TRANSFORMANIMEVALUATOR_H_ #define NW_GFX_TRANSFORMANIMEVALUATOR_H_ #include namespace nw { namespace gfx { //--------------------------------------------------------------------------- //! @brief トランスフォームアニメーションを評価するクラスです。 //! //! アニメーションデータを保持し、ファンクションカーブの評価を行います。 //--------------------------------------------------------------------------- class TransformAnimEvaluator : public BaseAnimEvaluator { public: NW_UT_RUNTIME_TYPEINFO; //---------------------------------------- //! @name 作成 //@{ //! トランスフォームアニメーション評価を構築するクラスです。 class Builder { public: //! コンストラクタです。 Builder() : m_AnimData(NULL), m_MaxMembers(64), m_MaxAnimMembers(64), m_AllocCache(false) {} //! アニメーションデータを設定します。 Builder& AnimData(const anim::ResAnim& animData) { m_AnimData = animData; return *this; } //! @brief アニメーション対象メンバの最大数を設定します。 //! //! TransformAnimEvaluator::Bind に渡す AnimGroup の AnimGroup::GetMemberCount の値を設定してください。 //! 複数の AnimGroup に Bind する場合は、最大値を設定してください。 Builder& MaxMembers(int maxMembers) { NW_ASSERT(maxMembers > 0); m_MaxMembers = maxMembers; return *this; } //! @brief 実際にアニメーションするメンバの最大数を設定します。 //! //! AnimData() に渡す anim::res::ResAnim の anim::res::ResAnim::GetMemberAnimSetCount の値を設定してください。 //! TransformAnimEvaluator::ChangeAnim で複数の ResAnim を切り替える場合は、最大値を設定してください。 Builder& MaxAnimMembers(int maxAnimMembers) { NW_ASSERT(maxAnimMembers > 0); m_MaxAnimMembers = maxAnimMembers; return *this; } //! キャッシュバッファを確保してキャッシュを有効にするかどうかを設定します。 Builder& AllocCache(bool allocCache) { m_AllocCache = allocCache; return *this; } //! @brief 生成時に必要なメモリサイズを取得します。 //! //! メモリサイズは Builder の設定によって変化します。 //! すべての設定が終わった後にこの関数を呼び出してください。 //! //! @param[in] alignment 計算に用いるアライメントです。2 のべき乗である必要があります。 size_t GetMemorySize(size_t alignment = os::IAllocator::DEFAULT_ALIGNMENT) const { os::MemorySizeCalculator size(alignment); GetMemorySizeInternal(&size); return size.GetSizeWithPadding(alignment); } //! @details :private void GetMemorySizeInternal(os::MemorySizeCalculator* pSize) const { os::MemorySizeCalculator& size = *pSize; size += sizeof(TransformAnimEvaluator); BaseAnimEvaluator::GetMemorySizeForInitialize(pSize, m_MaxMembers, m_MaxAnimMembers); if (m_AllocCache) { size += sizeof(CalculatedTransform) * m_MaxAnimMembers; } } //! @brief トランスフォームアニメーション評価を生成します。 //! //! @param[in] allocator アロケータです。 //! //! @return 生成されたトランスフォームアニメーション評価です。 //! TransformAnimEvaluator* Create(os::IAllocator* allocator) { void* buf = allocator->Alloc(sizeof(TransformAnimEvaluator)); if (buf == NULL) { return NULL; } TransformAnimEvaluator* evaluator = new(buf) TransformAnimEvaluator(allocator); Result result = evaluator->Initialize(m_AnimData, m_MaxMembers, m_MaxAnimMembers, m_AllocCache); NW_ASSERT(result.IsSuccess()); return evaluator; } private: anim::ResAnim m_AnimData; int m_MaxMembers; int m_MaxAnimMembers; bool m_AllocCache; }; //@} //---------------------------------------- //! @name 基本操作 //@{ //! @brief アニメーションを関連付けます。 //! //! Bind() よりも詳細なバインド結果を得ることができます。 //! //! アニメーションデータがあるボーンの、アニメーションカーブがないSRT要素に、 //! この時点でバインドポーズをコピーします。 //! そのため、この関数を呼び出した後からアニメーション評価を行うまでの間は //! スケルトンの姿勢が正しくない状態になりますのでご注意ください。 //! //! @param[in] animGroup アニメーショングループです。 //! //! @return バインドの結果を返します。 //! //! @sa Bind //! @sa BindResult virtual Result TryBind(AnimGroup* animGroup); //! @brief アニメーションを変更します。 //! //! 内部で Bind() を呼び出すため、スケルトンの姿勢が変化します。 //! //! GetCacheBufferSizeNeeded() の値が変化しますので、 //! SetCacheBuffer() している場合はバッファの再設定が必要です。 //! //! 内部で確保したキャッシュバッファは、自動的にサイズ変更されます。 //! //! @sa Bind //! @sa BaseAnimEvaluator::ChangeAnim //! @param animData アニメーションデータです。 virtual void ChangeAnim(const nw::anim::ResAnim animData) { // BaseAnimEvaluator::ChangeAnim()内でBind()が呼ばれ、 // そこでキャッシュを書き換えるので、キャッシュのResizeを先に行う if (!m_IsCacheExternal && !m_CacheTransforms.Empty()) { m_CacheTransforms.Resize(animData.GetMemberAnimSetCount()); } BaseAnimEvaluator::ChangeAnim(animData); } //@} //---------------------------------------- //! @name 評価 //@{ //! @brief メンバ単位でアニメーション結果を取得します。 //! //! @param[out] target アニメーション結果を書き込む対象です。 //! @param[in] memberIdx メンバインデックスです。 //! //! @return アニメーション結果を適用した場合は NULL でない値を返します。 //! virtual const anim::AnimResult* GetResult( void* target, int memberIdx) const; //@} //---------------------------------------- //! @name 取得/設定 //@{ //! スケールアニメを無効化しているかを取得します。 bool GetIsScaleDisabled() const { return m_IsScaleDisabled; } //! スケールアニメを無効化しているかを設定します。 void SetIsScaleDisabled(bool isDisabled) { m_IsScaleDisabled = isDisabled; } //! 回転アニメを無効化しているかを取得します。 bool GetIsRotateDisabled() const { return m_IsRotateDisabled; } //! 回転アニメを無効化しているかを設定します。 void SetIsRotateDisabled(bool isDisabled) { m_IsRotateDisabled = isDisabled; } //! 移動アニメを無効化しているかを取得します。 bool GetIsTranslateDisabled() const { return m_IsTranslateDisabled; } //! 移動アニメを無効化しているかを設定します。 void SetIsTranslateDisabled(bool isDisabled) { m_IsTranslateDisabled = isDisabled; } //! @brief メンバに関連付けられたアニメーションが存在するかどうかを取得します。 //! //! @param[in] memberIdx メンバインデックスです。 //! //! @return アニメーションが存在すれば true を返します。 //! virtual bool HasMemberAnim(int memberIdx) const { NW_MINMAXLT_ASSERT(memberIdx, 0, m_BindIndexTable.Size()); if (m_AnimData.ptr() == NULL) { return (0 <= memberIdx && memberIdx < m_AnimGroup->GetMemberCount()); } else { return m_BindIndexTable[memberIdx] != NotFoundIndex; } } //@} //---------------------------------------- //! @name キャッシュ //@{ //---------------------------------------------------------- //! :private void UpdateCacheNonVirtual() { 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; } } //! アニメーション評価結果の内部キャッシュが古ければ更新します。 virtual void UpdateCache() { this->UpdateCacheNonVirtual(); } //! キャッシュバッファに必要なサイズ(バイト数)を取得します。 virtual int GetCacheBufferSizeNeeded() const { return m_AnimData.GetMemberAnimSetCount() * sizeof(CalculatedTransform); } //! キャッシュバッファを取得します。 virtual const void* GetCacheBuffer() const { return m_CacheTransforms.Elements(); } //! @brief キャッシュバッファを設定します。 //! この関数で指定したキャッシュバッファはデストラクタで開放されません。 //! //! @param[in] buf キャッシュバッファ用のメモリの先頭アドレスです。 //! NULL の場合、キャッシュが無効になります。 //! @param[in] size キャッシュバッファのサイズ(バイト数)です。 //! virtual void SetCacheBuffer(void* buf, int size) { if (buf != NULL) { NW_ASSERT(size >= GetCacheBufferSizeNeeded()); const int maxCalculatedTransforms = size / sizeof(CalculatedTransform); m_CacheTransforms = ut::MoveArray(buf, maxCalculatedTransforms); m_CacheTransforms.Resize(maxCalculatedTransforms); m_IsCacheDirty = true; m_IsCacheExternal = true; } else { m_CacheTransforms = ut::MoveArray(); } } //! @brief 必要があれば SRT ごとの重みを無効化します。 //! //! @details //! 引数 animObj が TransformAnimEvaluator のインスタンスの場合に、 //! アニメーション無効化の設定を重みに反映します。 //! //! @param[in] weights SRT ごとの重みです。 //! @param[in] animObj アニメーションオブジェクトです。 //! //! @sa GetIsScaleDisabled //! @sa GetIsRotateDisabled //! @sa GetIsTranslateDisabled //! //! :private static void DisableSRTWeightsIfNeeded(float* weights, const AnimObject* animObj) { const TransformAnimEvaluator* evaluator = ut::DynamicCast(animObj); if (evaluator != NULL) { if (evaluator->GetIsScaleDisabled()) { weights[0] = TransformAnimBlendOp::WeightDiscard; } if (evaluator->GetIsRotateDisabled()) { weights[1] = TransformAnimBlendOp::WeightDiscard; } if (evaluator->GetIsTranslateDisabled()) { weights[2] = TransformAnimBlendOp::WeightDiscard; } } } //! @brief SRT ごとの重みがすべてゼロかどうか判定します。 //! //! @param[in] weights SRT ごとの重みです。 //! //! @return 重みがすべてゼロなら true を返します。 //! //! :private static bool CheckWeightsNearlyZero(const float* weights) { NW_NULL_ASSERT(weights); return AnimWeightNearlyEqualZero(weights[0]) && AnimWeightNearlyEqualZero(weights[1]) && AnimWeightNearlyEqualZero(weights[2]); } //@} protected: //---------------------------------------- //! @name コンストラクタ/デストラクタ //@{ //! コンストラクタです。 //! //! :private TransformAnimEvaluator( os::IAllocator* allocator) : BaseAnimEvaluator(allocator, ANIMTYPE_TRANSFORM_SIMPLE), m_IsScaleDisabled(false), m_IsRotateDisabled(false), m_IsTranslateDisabled(false) { } //! デストラクタです。 //! //! :private virtual ~TransformAnimEvaluator() {} //@} //! @details :private Result Initialize( const anim::ResAnim& animData, const int maxMembers, const int maxAnimMembers, bool allocCache) { Result result = BaseAnimEvaluator::Initialize(animData, maxMembers, maxAnimMembers); NW_ENSURE_AND_RETURN(result); if (allocCache) { void* memory = GetAllocator().Alloc(sizeof(CalculatedTransform) * maxAnimMembers); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_CacheTransforms = ut::MoveArray(memory, maxAnimMembers, &GetAllocator()); m_CacheTransforms.Resize(animData.GetMemberAnimSetCount()); } return result; } bool m_IsScaleDisabled; //!< @details :private bool m_IsRotateDisabled; //!< @details :private bool m_IsTranslateDisabled; //!< @details :private ut::MoveArray m_CacheTransforms; //!< @details :private private: // アニメーションカーブが存在しないメンバを、OriginalValueで初期化します。 void ResetNoAnimMember(AnimGroup* animGroup, anim::ResAnim animData); const anim::AnimResult* GetResultFast(void* target, int memberIdx) const; const anim::AnimResult* GetResultCommon(void* target, int memberIdx, bool writeNoAnimMember) const; // メンバアニメーションを評価します。 // @param[in] writeNoAnimMember trueなら、アニメーションのない要素にOriginalValueを上書きします。 void EvaluateMemberAnim( CalculatedTransform* result, anim::ResTransformAnim transformAnim, float frame, const math::Transform3* originalTransform, bool writeNoAnimMember) const; // ベイク済みのメンバアニメーションを評価します。 // @param[in] writeNoAnimMember trueなら、アニメーションのない要素にOriginalValueを上書きします。 void EvaluateMemberBakedAnim( CalculatedTransform* result, anim::ResBakedTransformAnim transformAnim, float frame, const math::Transform3* originalTransform, bool writeNoAnimMember) const; void UpdateFlagsCommon(CalculatedTransform* transform) const; void UpdateFlags(CalculatedTransform* transform) const; // CalculatedTransformのフラグを、Bakeされた情報をもとにして更新します。 void ApplyBakedFlags(CalculatedTransform* transform, bit32 flags) const; friend class AnimBinding; }; } // namespace gfx } // namespace nw #endif // NW_GFX_TRANSFORMANIMEVALUATOR_H_