/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_BaseAnimEvaluator.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: 32369 $ *---------------------------------------------------------------------------*/ #ifndef NW_GFX_BASEANIMEVALUATOR_H_ #define NW_GFX_BASEANIMEVALUATOR_H_ #include #include #include #include #include #include namespace nw { namespace gfx { namespace internal { //! :private void ClearMaterialHash(anim::ResAnimGroupMember member); } //--------------------------------------------------------------------------- //! @brief アニメーション評価の基底クラスです。 //! 抽象クラスですのでインスタンス化して使用することはできません。 //--------------------------------------------------------------------------- class BaseAnimEvaluator : public AnimObject { public: NW_UT_RUNTIME_TYPEINFO; //! 関連付け対象がない場合のインデックスです。 //! //! :private static const int NotFoundIndex; //---------------------------------------- //! @name コンストラクタ/デストラクタ //@{ //! コンストラクタです。 BaseAnimEvaluator( os::IAllocator* allocator, u32 animType) : AnimObject(allocator, animType), m_IsCacheDirty(true), m_IsCacheExternal(false), m_UseSharedCache(false) { } //! デストラクタです。 virtual ~BaseAnimEvaluator() {} //@} //---------------------------------------- //! @name 基本操作 //@{ //! @brief アニメーションを関連付けます。 //! //! Bind よりも詳細なバインド結果を得ることができます。 //! //! @param[in] animGroup アニメーショングループです。 //! //! @return バインドの結果を返します。 //! //! @sa Bind virtual Result TryBind(AnimGroup* animGroup); //! アニメーションの関連付けを解除します。 virtual void Release() { m_AnimGroup = NULL; } //! フレームを更新します。 virtual void UpdateFrame() { // 更新フレームが 0 の場合は、SetStepFrame() で // キャッシュを更新済みです。 if (GetStepFrame() != 0.0f) { m_AnimFrameController.UpdateFrame(); m_IsCacheDirty = true; } } //! @brief アニメーションを変更します。 //! //! AnimEvaluator::Bind の後でアニメーションを差し替える場合に使用します。 //! AnimEvaluator を破棄して再生成するより高速です。 //! //! 再生フレームは 0.0f にリセットされます。 //! //! @param animData アニメーションデータです。 virtual void ChangeAnim(const nw::anim::ResAnim animData) { NW_NULL_ASSERT(m_AnimGroup); // このASSERTが失敗した場合は、Builder::MaxAnimMembers の値を大きくしてください NW_ASSERT(animData.GetMemberAnimSetCount() <= m_ReverseBindIndexTable.Capacity()); SetResAnim(animData); AnimGroup* animGroup = m_AnimGroup; Release(); Bind(animGroup); } //! @brief すべてのアニメーション対象メンバーを生成時の状態に戻します。 //! //! マテリアルに対して行う場合、マテリアルのバッファを生成する必要があります。 //! SceneBuilder クラスでモデルを作成する際のオプションで、 //! SceneBuilder::BufferOption を設定してください。 //! BufferOption には、 Model::MULTI_FLAG_ANIMATABLE_MATERIAL の内容が最低限必要です。 void Reset() { for (int i = (m_AnimGroup->GetMemberCount() - 1); i >= 0; --i) { this->ResetMember(i); } } //! @brief 指定されたメンバーを生成時の状態に戻します。 //! @param memberIdx 対象のメンバーです。 void ResetMember(int memberIdx); //@} //---------------------------------------- //! @name フレーム制御 //@{ //! フレームを取得します。 float GetFrame() const { return m_AnimFrameController.GetFrame(); } //! @brief フレームを設定します。 //! //! @param[in] frame 設定するフレームです。 //! void SetFrame(float frame) { m_AnimFrameController.SetFrame(frame); m_IsCacheDirty = true; } //! @brief フレームをリセットします。 //! //! @param[in] frame 設定するフレームです。 //! void ResetFrame(f32 frame) { m_AnimFrameController.GetAnimFrame().ResetFrame(frame); m_IsCacheDirty = true; } //! @brief フレームの増分を取得します。 //! @sa SetStepFrame float GetStepFrame() const { return m_AnimFrameController.GetStepFrame(); } //! @brief フレームの増分を設定します。 //! //! UpdateFrame() を呼び出すと、現在のフレームにフレームの増分が加算されます。 //! 例えば SetStepFrame(2.0f) とすると、2倍速再生になります。 //! //! @param[in] stepFrame フレームの増分です。 void SetStepFrame(float stepFrame) { m_AnimFrameController.SetStepFrame(stepFrame); if (stepFrame == 0.0f) { m_IsCacheDirty = true; UpdateCache(); } } //! @brief 開始フレームを取得します。 //! @sa SetStartFrame float GetStartFrame() const { return m_AnimFrameController.GetStartFrame(); } //! @brief 開始フレームを設定します。 //! //! 開始フレームを現在のフレームより後に設定した場合、 //! 現在のフレームを開始フレームに移動します。 //! //! @param[in] startFrame 開始フレームです。 void SetStartFrame(float startFrame) { m_AnimFrameController.SetStartFrame(startFrame); if (startFrame > GetFrame()) { SetFrame(startFrame); } } //! @brief 終了フレームを取得します。 //! @sa SetEndFrame float GetEndFrame() const { return m_AnimFrameController.GetEndFrame(); } //! @brief 終了フレームを設定します。 //! //! 終了フレームを現在のフレームより前に設定した場合、 //! 現在のフレームを終了フレームに移動します。 //! //! @param[in] endFrame 終了フレームです。 void SetEndFrame(float endFrame) { m_AnimFrameController.SetEndFrame(endFrame); if (endFrame < GetFrame()) { SetFrame(endFrame); } } //! アニメーション再生方法を取得します。 nw::anim::AnimFrameController::PlayPolicy GetPlayPolicy() const { return m_AnimFrameController.GetPlayPolicy(); } //! @brief アニメーション再生方法を設定します。 //! //! @param[in] playPolicy 設定するアニメーション再生方法です。 //! @sa nw::anim::AnimFrameController::SetPlayPolicy void SetPlayPolicy(nw::anim::AnimFrameController::PlayPolicy playPolicy) { m_AnimFrameController.SetPlayPolicy(playPolicy); m_IsCacheDirty = true; } //! アニメーションフレーム制御情報を取得します。 const nw::anim::AnimFrameController& AnimFrameController() const { return m_AnimFrameController; } //@} //---------------------------------------- //! @name 取得/設定 //@{ //! ユーザーデータを取得します。 const void* GetUserData() const { return m_AnimFrameController.GetUserData(); } //! ユーザーデータを取得します。 void* GetUserData() { return m_AnimFrameController.GetUserData(); } //! @brief ユーザーデータを設定します。 //! //! @param[in] userData 設定するユーザーデータです。 //! @sa nw::anim::AnimFrameController::SetUserData //! void SetUserData(void* userData) { m_AnimFrameController.SetUserData(userData); } //! ResAnimGroupMember のインデックスから ResMemberAnim のインデックスへの変換テーブルを取得します。 const ut::MoveArray& BindIndexTable() const { return m_BindIndexTable; } //! ResAnimGroupMember のインデックスから ResMemberAnim のインデックスへの変換テーブルを取得します。 ut::MoveArray& BindIndexTable() { return m_BindIndexTable; } //! ResMemberAnim のインデックスから ResAnimGroupMember のインデックスへの変換テーブルを取得します。 const ut::MoveArray& ReverseBindIndexTable() const { return m_ReverseBindIndexTable; } //! ResMemberAnim のインデックスから ResAnimGroupMember のインデックスへの変換テーブルを取得します。 ut::MoveArray& ReverseBindIndexTable() { return m_ReverseBindIndexTable; } //! アニメーションデータを取得します。 const nw::anim::ResAnim GetAnimData() const { return m_AnimData; } //@} //---------------------------------------- //! @name キャッシュ //@{ //! キャッシュバッファを取得します。 virtual const void* GetCacheBuffer() const { return NULL; } //! キャッシュバッファに必要なサイズ(バイト数)を取得します。 virtual int GetCacheBufferSizeNeeded() const { return 0; } //! @brief キャッシュバッファを設定します。 //! この関数で指定したキャッシュバッファはデストラクタで解放されません。 //! //! @param[in] buf キャッシュバッファ用のメモリの先頭アドレスです。 //! NULL の場合、キャッシュが無効になります。 //! @param[in] size キャッシュバッファのサイズ(バイト数)です。 //! virtual void SetCacheBuffer(void* buf, int size) { (void)buf; (void)size; } //@} protected: //! Initialize() の実行に必要なメモリサイズを取得します。 //! //! :private static void GetMemorySizeForInitialize( os::MemorySizeCalculator* pSize, const int maxMembers, const int maxAnimMembers) { os::MemorySizeCalculator& size = *pSize; size += sizeof(int) * maxMembers; size += sizeof(int) * maxAnimMembers; } //! メンバを初期化します。 //! //! :private Result Initialize( const nw::anim::ResAnim& animData, const int maxMembers, const int maxAnimMembers) { Result result = INITIALIZE_RESULT_OK; SetResAnim(animData); { void* memory = GetAllocator().Alloc(sizeof(int) * maxMembers); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_BindIndexTable = ut::MoveArray(memory, maxMembers, &GetAllocator()); } { void* memory = GetAllocator().Alloc(sizeof(int) * maxAnimMembers); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_ReverseBindIndexTable = ut::MoveArray(memory, maxAnimMembers, &GetAllocator()); } return result; } //! @brief TryBind のテンプレート関数です。 //! //! メンバアニメのバインドをカスタマイズできます。 //! //! @tparam T アニメメンバのインデックスを引く関数オブジェクトです。 //! //! :private template Result TryBindTemplate(AnimGroup* animGroup, T indexGetter) { 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."); NW_ASSERTMSG(animGroup->GetFullBakedAnimEnabled() == m_AnimData.IsFullBakedAnim(), "To use full baked animation, call SkeletalModel::SetFullBakedAnimEnabled(true) before Bind."); 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 = indexGetter(animGroup, member); // 辞書引きで見つからなかったときは、モデルに対象が存在しない if (bindTargetIdx == -1) { m_ReverseBindIndexTable[animIdx] = NotFoundIndex; continue; } m_BindIndexTable[bindTargetIdx] = animIdx; m_ReverseBindIndexTable[animIdx] = bindTargetIdx; ++boundAnimCount; //HACK anim::ResAnimGroupMember resAnimGroupMember = animGroup->GetResAnimGroupMember(bindTargetIdx); internal::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); } //! AnimGroupMemberのIndex -> MemberAnimのIndex //! //! :private ut::MoveArray m_BindIndexTable; //! MemberAnimのIndex -> AnimGroupMemberのIndex //! //! :private ut::MoveArray m_ReverseBindIndexTable; anim::AnimFrameController m_AnimFrameController; //!< @details :private anim::ResAnim m_AnimData; //!< @details :private //! キャッシュの内容が古ければ true //! //! :private bool m_IsCacheDirty; //! 外部から指定したキャッシュバッファなら true //! //! :private bool m_IsCacheExternal; //! キャッシュを他のAnimEvaluatorと共有するなら true ( SharedAnimCache を使用します。) //! //! :private bool m_UseSharedCache; private: //! アニメデータに対応するメンバインデックスをそのまま引く関数オブジェクトです。 //! //! :private class BasicIndexGetterFunctor { public: int operator() (AnimGroup* animGroup, anim::ResMemberAnim member) { return animGroup->GetResAnimGroupMemberIndex(member.GetPath()); } }; void SetResAnim(const nw::anim::ResAnim animData) { m_AnimData = animData; m_AnimFrameController.SetStepFrame(1.0f); m_AnimFrameController.SetStartFrame(0.0f); m_AnimFrameController.SetEndFrame(animData.GetFrameSize()); m_AnimFrameController.GetAnimFrame().ResetFrame(0.0f); switch (animData.GetLoopMode()) { case nw::anim::ResAnimData::LOOP_MODE_ONETIME: m_AnimFrameController.SetPlayPolicy(nw::anim::PlayPolicy_Onetime); break; case nw::anim::ResAnimData::LOOP_MODE_LOOP: m_AnimFrameController.SetPlayPolicy(nw::anim::PlayPolicy_Loop); break; default: NW_ASSERT(false); } m_IsCacheDirty = true; } }; } // namespace gfx } // namespace nw #endif // NW_GFX_BASEANIMEVALUATOR_H_