/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_AnimObject.h 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: 28677 $ *---------------------------------------------------------------------------*/ #ifndef NW_GFX_ANIMOBJECT_H_ #define NW_GFX_ANIMOBJECT_H_ #include #include #include #include #include #include #include namespace nw { namespace gfx { class SceneNode; class AnimEvaluator; class TransformAnimEvaluator; //--------------------------------------------------------------------------- //! @brief アニメーションの重みを比較します。 //! //! @param[in] weight 重み //! @param[in] value 比較する値 //! //! @return 重みと比較する値の誤差が許容値以下なら true を返します。 //--------------------------------------------------------------------------- NW_INLINE bool AnimWeightNearlyEqual(const float weight, const float value) { // 0.1%未満は無視することとします。 const float Epsilon = 0.001f; return math::FAbs(weight - value) <= Epsilon; } //--------------------------------------------------------------------------- //! @brief アニメーションの重みが 0 にほぼ等しいか比較します。 //! //! @param[in] weight 重み //! //! @return 重みと 0 との誤差が許容値以下なら true を返します。 //--------------------------------------------------------------------------- NW_INLINE bool AnimWeightNearlyEqualZero(const float weight) { return AnimWeightNearlyEqual(weight, 0.0f); } //--------------------------------------------------------------------------- //! @brief アニメーションの重みが 1 にほぼ等しいか比較します。 //! //! @param[in] weight 重み //! //! @return 重みと 1 との誤差が許容値以下なら true を返します。 //--------------------------------------------------------------------------- NW_INLINE bool AnimWeightNearlyEqualOne(const float weight) { return AnimWeightNearlyEqual(weight, 1.0f); } //--------------------------------------------------------------------------- //! @brief アニメーションブレンドの重みを正規化するスケールを取得します。 //! //! @param[in] weightSum 重みの合計です。 //! //! @return 重みを正規化するスケールです。 //--------------------------------------------------------------------------- NW_INLINE float GetAnimWeightNormalizeScale(float weightSum) { return AnimWeightNearlyEqualOne(weightSum) || AnimWeightNearlyEqualZero(weightSum) ? 1.0f : 1.0f / weightSum; } //--------------------------------------------------------------------------- //! @brief アニメーション対象を抽象化するクラスです。 //! //! アニメーション対象メンバへのポインタを保持します。 //! 対象メンバとはボーンのトランスフォーム、マテリアルのディフューズカラーといった単位です。 //! //! また、対象メンバのオリジナル値(インスタンス生成時の値)を保存しています。 //--------------------------------------------------------------------------- class AnimGroup : public GfxObject { public: //! @brief 評価前コールバック関数の定義です。 //! 対象オブジェクトの評価が必要なら true を返します。 typedef bool (*PreEvaluateCallback)(AnimGroup* animGroup, int targetObjIdx); //---------------------------------------- //! @name 作成 //@{ //! アニメーショングループを構築するクラスです。 class Builder { public: //! コンストラクタです。 Builder() : m_SceneNode(NULL), m_UseOriginalValue(false) {} //! アニメーショングループリソースを設定します。 Builder& ResAnimGroup(anim::ResAnimGroup resAnimGroup) { m_ResAnimGroup = resAnimGroup; return *this; } //! 対象となるシーンノードを設定します。 Builder& SetSceneNode(SceneNode* sceneNode) { m_SceneNode = sceneNode; return *this; } //! オリジナル値を使用するかどうかを設定します。 Builder& UseOriginalValue(bool use) { m_UseOriginalValue = use; 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(AnimGroup); AnimGroup::GetMemorySizeForInitialize(pSize, m_ResAnimGroup, m_UseOriginalValue); } //! @brief アニメーショングループを生成します。 //! //! @param[in] allocator アロケータです。 //! //! @return 生成されたアニメーショングループです。 //! AnimGroup* Create(os::IAllocator* allocator) { void* buf = allocator->Alloc(sizeof(AnimGroup)); if (buf == NULL) { return NULL; } AnimGroup* animGroup = new(buf) AnimGroup(m_ResAnimGroup, m_SceneNode, allocator); Result result = animGroup->Initialize(m_UseOriginalValue); if (result.IsSuccess()) { return animGroup; } else { SafeDestroy(animGroup); return NULL; } } private: SceneNode* m_SceneNode; anim::ResAnimGroup m_ResAnimGroup; bool m_UseOriginalValue; }; //@} //---------------------------------------- //! @name 取得/設定 //@{ //! アニメーショングループリソースを取得します。 anim::ResAnimGroup GetResAnimGroup() const { return m_ResAnimGroup; } //! グラフィックスアニメーショングループリソースを取得します。 anim::ResGraphicsAnimGroup GetResGraphicsAnimGroup() const { return *reinterpret_cast(&m_ResAnimGroup); } //! アニメーショングループメンバリソースを取得します。 anim::ResAnimGroupMember GetResAnimGroupMember(int memberIdx) const { return m_ResAnimGroup.GetMemberInfoSet(memberIdx); } //! アニメーショングループメンバリソースを取得します。 anim::ResAnimGroupMember GetResAnimGroupMember(const char* key) const { return m_ResAnimGroup.GetMemberInfoSet(key); } //! アニメーショングループメンバリソースのインデックスを取得します。 int GetResAnimGroupMemberIndex(const char* key) const { return m_ResAnimGroup.GetMemberInfoSetIndex(key); } //! 名前を取得します。 const char* GetName() const { return m_ResAnimGroup.GetName(); } //! アニメーショングループメンバ数を取得します。 s32 GetMemberCount() const { return m_ResAnimGroup.GetMemberInfoSetCount(); } //! アニメーショングループ内のブレンドオペレーションを取得します。 const anim::AnimBlendOp* GetBlendOperationInGroup(int blendOpIdx) const { NW_MINMAXLT_ASSERT(blendOpIdx, 0, m_BlendOperations.Size()); return m_BlendOperations[blendOpIdx]; } //! アニメーショングループ内のブレンドオペレーションを設定します。 void SetBlendOperationInGroup(int blendOpIdx, anim::AnimBlendOp* blendOp) { NW_MINMAXLT_ASSERT(blendOpIdx, 0, m_BlendOperations.Size()); m_BlendOperations[blendOpIdx] = blendOp; } //! メンバに対するブレンドオペレーションを取得します。 const anim::AnimBlendOp* GetBlendOperation(int memberIdx) const { return GetBlendOperationInGroup( GetResAnimGroupMember(memberIdx).GetBlendOperationIndex()); } //! 対象オブジェクトが属するシーンノードを取得します。 SceneNode* GetSceneNode() const { return m_SceneNode; } //! 対象オブジェクトのインデックスを取得します。 int GetTargetObjectIndex(int memberIdx) const { NW_MINMAXLT_ASSERT(memberIdx, 0, m_TargetObjectIndicies.Size()); return m_TargetObjectIndicies[memberIdx]; } //! 対象オブジェクトのインデックスを設定します。 void SetTargetObjectIndex(int memberIdx, const int targetObjIdx) { NW_MINMAXLT_ASSERT(memberIdx, 0, m_TargetObjectIndicies.Size()); m_TargetObjectIndicies[memberIdx] = targetObjIdx; } //! 対象オブジェクトのポインタを取得します。 void* GetTargetObject(int memberIdx) const { NW_MINMAXLT_ASSERT(memberIdx, 0, m_TargetObjects.Size()); return m_TargetObjects[memberIdx]; } //! 対象オブジェクトのポインタを設定します。 void SetTargetObject(int memberIdx, void* object) { NW_MINMAXLT_ASSERT(memberIdx, 0, m_TargetObjects.Size()); m_TargetObjects[memberIdx] = object; } //! 対象メンバのポインタを取得します。 void* GetTargetPtr(int memberIdx) const { NW_MINMAXLT_ASSERT(memberIdx, 0, m_TargetPtrs.Size()); return m_TargetPtrs[memberIdx]; } //! 対象メンバのポインタを設定します。 void SetTargetPtr(int memberIdx, void* target) { NW_MINMAXLT_ASSERT(memberIdx, 0, m_TargetPtrs.Size()); m_TargetPtrs[memberIdx] = target; } //! メンバのオリジナル値を持っているかどうかを取得します。 bool HasOriginalValue() const { return m_OriginalValues.Size() != 0; } //! メンバのオリジナル値を取得します。 const void* GetOriginalValue(int memberIdx) const { NW_MINMAXLT_ASSERT(memberIdx, 0, m_OriginalValues.Size()); return m_OriginalValues[memberIdx]; } //! メンバのオリジナル値を設定します。 void SetOriginalValue(int memberIdx, const void* value) { NW_MINMAXLT_ASSERT(memberIdx, 0, m_OriginalValues.Size()); m_OriginalValues[memberIdx] = value; } //! 評価前コールバック関数を取得します。 PreEvaluateCallback GetPreEvaluateCallback() const { return m_PreEvaluateCallback; } //! 評価前コールバック関数を設定します。 void SetPreEvaluateCallback(PreEvaluateCallback function) { m_PreEvaluateCallback = function; } //! フルベイクアニメーションを使用するかを取得します。 //! //! :private bool GetFullBakedAnimEnabled() const { return m_FullBakedAnimEnabled; } //! フルベイクアニメーションを使用するかを設定します。 //! //! :private void SetFullBakedAnimEnabled(bool enable) { m_FullBakedAnimEnabled = enable; } //! @brief すべてのメンバをオリジナル値にリセットします。 void Reset(); //@} protected: //---------------------------------------- //! @name コンストラクタ/デストラクタ //@{ //! コンストラクタです。 //! //! :private AnimGroup( anim::ResAnimGroup resAnimGroup, SceneNode* sceneNode, os::IAllocator* allocator); //! デストラクタです。 //! //! :private virtual ~AnimGroup() {} //@} //! Initialize() の実行に必要なメモリサイズを取得します。 //! //! :private static void GetMemorySizeForInitialize(os::MemorySizeCalculator* pSize, const anim::ResAnimGroup resAnimGroup, bool useOriginalValue); //! メンバを初期化します。 //! //! :private Result Initialize(bool useOriginalValue); private: anim::ResAnimGroup m_ResAnimGroup; //!< アニメーショングループのリソースです。 ut::MoveArray m_BlendOperations; //!< ブレンドオペレーションです。 SceneNode* m_SceneNode; //!< 対象オブジェクトが属するシーンノードです。 ut::MoveArray m_TargetObjectIndicies; //!< メンバごとの対象オブジェクトのインデックスです。 ut::MoveArray m_TargetObjects; //!< メンバごとの対象オブジェクトのポインタです。 ut::MoveArray m_TargetPtrs; //!< メンバごとの対象のポインタです。 ut::MoveArray m_OriginalValues; //!< メンバごとのオリジナル値です。 PreEvaluateCallback m_PreEvaluateCallback; //!< 評価前コールバック関数です。 bool m_FullBakedAnimEnabled; //!< フルベイクアニメを使用するかを指定します。 }; //--------------------------------------------------------------------------- //! @brief アニメーション評価を抽象化するクラスです。 //! 抽象クラスですのでインスタンス化して使用することはできません。 //! //! フレームを進める、各フレームの値を取得する、などの動作を抽象化しています。 //! 単一のアニメーションを評価する AnimEvaluator, 複数のアニメーションをブレンドする AnimBlender などの派生クラスがあります。 //--------------------------------------------------------------------------- class AnimObject : public GfxObject { public: //! @details :private NW_UT_RUNTIME_TYPEINFO; //---------------------------------------- //! @name コンストラクタ/デストラクタ //@{ //! コンストラクタです。 AnimObject(os::IAllocator* allocator, u32 animType) : GfxObject(allocator), m_AnimGroup(NULL), m_AnimType(animType) {} //! デストラクタです。 virtual ~AnimObject() {} //@} //---------------------------------------- //! @name 基本操作 //@{ //! @brief アニメーションを関連付けます。 //! //! @param[in] animGroup アニメーショングループです。 //! //! @return バインドに成功したら true を返します。 //! //! @sa TryBind bool Bind(AnimGroup* animGroup) { return TryBind(animGroup).IsSuccess(); } //! @brief アニメーションを関連付けます。 //! //! Bind() よりも詳細なバインド結果を得ることができます。 //! //! @param[in] animGroup アニメーショングループです。 //! //! @return バインドの結果を返します。 //! //! @sa Bind //! @sa BindResult virtual Result TryBind(AnimGroup* animGroup) = 0; //! アニメーションの関連付けを解除します。 virtual void Release() = 0; //! フレームを更新します。 virtual void UpdateFrame() = 0; //@} //---------------------------------------- //! @name 評価 //@{ //! @brief メンバ単位でアニメーション結果を取得します。 //! //! @param[out] target アニメーション結果を書き込む対象です。 //! @param[in] memberIdx メンバインデックスです。 //! //! @return アニメーション結果を返します。 //! ブレンドオペレーションを使用する場合は、返り値のアニメーション結果を使用してください。 //! virtual const anim::AnimResult* GetResult( void* target, int memberIdx) const = 0; //@} //---------------------------------------- //! @name 取得/設定 //@{ //! アニメーショングループを取得します。 const AnimGroup* GetAnimGroup() const { return m_AnimGroup; } //! アニメーショングループを取得します。 AnimGroup* GetAnimGroup() { return m_AnimGroup; } //! アニメーショングループを設定します。 void SetAnimGroup(AnimGroup* group) { m_AnimGroup = group; } //! @brief メンバに関連付けられたアニメーションが存在するかどうかを取得します。 //! //! @param[in] memberIdx メンバインデックスです。 //! //! @return アニメーションが存在すれば true を返します。 //! virtual bool HasMemberAnim(int memberIdx) const = 0; //@} //---------------------------------------- //! @name キャッシュ //@{ //! アニメーション評価結果の内部キャッシュが古ければ更新します。 virtual void UpdateCache() = 0; //! @details :private enum { ANIMTYPE_SIMPLE, ANIMTYPE_TRANSFORM_SIMPLE, ANIMTYPE_BLENDER }; //! @details :private u32 GetAnimType() const { return m_AnimType; } //@} protected: //! @details :private AnimGroup* m_AnimGroup; //! @details :private u32 m_AnimType; }; //--------------------------------------------------------------------------- //! @brief アニメーションの関連付け情報のクラスです。 //! //! AnimObject と AnimGroup を関連付けて、アニメーションの再生を行います。 //--------------------------------------------------------------------------- class AnimBinding : public GfxObject { public: //! アニメーショングループの MoveArray の定義です。 //! //! :private typedef ut::MoveArray AnimGroupArray; //! アニメーションオブジェクトの MoveArray の定義です。 //! //! :private typedef ut::MoveArray AnimObjectArray; //---------------------------------------- //! @name 作成 //@{ //! アニメーションバインディングを構築するクラスです。 class Builder { public: //! コンストラクタです。 Builder() : m_MaxAnimGroups(1), m_MaxAnimObjectsPerGroup(1) {} //! 最大アニメーショングループ数を設定します。 Builder& MaxAnimGroups(int maxAnimGroups) { NW_ASSERT(maxAnimGroups > 0); m_MaxAnimGroups = maxAnimGroups; return *this; } //! @brief アニメーショングループ内での最大アニメーションオブジェクト数を設定します。 //! //! @details 複数のアニメーショングループを持つ場合は、そのうち最大のものを指定してください。 Builder& MaxAnimObjectsPerGroup(int maxAnimObjects) { NW_ASSERT(maxAnimObjects > 0); m_MaxAnimObjectsPerGroup = maxAnimObjects; 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(AnimBinding); AnimBinding::GetMemorySizeForInitialize(pSize, m_MaxAnimGroups, m_MaxAnimObjectsPerGroup); } //! @brief アニメーションバインディングを生成します。 //! //! @param[in] allocator アロケータです。 //! //! @return 生成されたアニメーションバインディングです。 //! AnimBinding* Create(os::IAllocator* allocator) { void* buf = allocator->Alloc(sizeof(AnimBinding)); if (buf == NULL) { return NULL; } AnimBinding* animBinding = new(buf) AnimBinding(allocator, m_MaxAnimObjectsPerGroup); Result result = animBinding->Initialize(m_MaxAnimGroups, m_MaxAnimObjectsPerGroup); if (result.IsSuccess()) { return animBinding; } else { SafeDestroy(animBinding); return NULL; } } private: int m_MaxAnimGroups; int m_MaxAnimObjectsPerGroup; }; //@} //---------------------------------------- //! @name 基本操作 //@{ //! @brief アニメーションを評価して対象に書き込みます。 //! //! SceneUpdater::UpdateAll から自動的に呼び出されます。 //! 通常は、ユーザーが呼び出す必要はありません。 //! //! @param[in] timing 評価タイミングです。 //! void Evaluate(anim::ResGraphicsAnimGroup::EvaluationTiming timing); //! @brief 設定されたすべてのアニメーションオブジェクトのフレームを更新します。 //! //! SceneUpdater::UpdateAll から自動的に呼び出されます。 //! 通常は、ユーザーが呼び出す必要はありません。 void UpdateFrame() { for (int animObjIdx = 0; animObjIdx < m_AnimObjects.Size(); ++animObjIdx) { if (m_AnimObjects[animObjIdx] != NULL) { m_AnimObjects[animObjIdx]->UpdateFrame(); } } } //@} //---------------------------------------- //! @name 取得/設定 //@{ //! アニメーショングループ数を取得します。 int GetAnimGroupCount() const { return m_AnimGroups.Size(); } //! アニメーショングループを取得します。 const AnimGroup* GetAnimGroup(int groupIdx) const { NW_MINMAXLT_ASSERT(groupIdx, 0, m_AnimGroups.Size()); return m_AnimGroups[groupIdx]; } //! アニメーショングループを取得します。 AnimGroup* GetAnimGroup(int groupIdx) { NW_MINMAXLT_ASSERT(groupIdx, 0, m_AnimGroups.Size()); return m_AnimGroups[groupIdx]; } //! @brief アニメーショングループ名から、登録されているグループのインデックスを取得します。 //! //! @param[in] animGroupName アニメーショングループ名です。 //! //! @return アニメーショングループのインデックスです。 //! animGroupName に対応するアニメーショングループがない場合は -1 を返します。 //! int GetAnimGroupIndex(const char* animGroupName) const { for (int groupIdx = 0; groupIdx < m_AnimGroups.Size(); ++groupIdx) { if (m_AnimGroups[groupIdx] != NULL && ::std::strcmp(m_AnimGroups[groupIdx]->GetName(), animGroupName) == 0) { return groupIdx; } } return -1; } //! アニメーショングループを設定します。 void SetAnimGroup(int groupIdx, AnimGroup* animGroup) { NW_MINMAXLT_ASSERT(groupIdx, 0, m_AnimGroups.Size()); m_AnimGroups[groupIdx] = animGroup; } //! アニメーションオブジェクトを取得します。 const AnimObject* GetAnimObject(int groupIdx, int objectIdx = 0) const { NW_MINMAXLT_ASSERT(groupIdx, 0, m_AnimObjects.Size()); NW_MINMAXLT_ASSERT(objectIdx, 0, m_AnimObjectCountPerGroup); const int index = groupIdx * m_AnimObjectCountPerGroup + objectIdx; return m_AnimObjects[index]; } //! アニメーションオブジェクトを取得します。 AnimObject* GetAnimObject(int groupIdx, int objectIdx = 0) { NW_MINMAXLT_ASSERT(groupIdx, 0, m_AnimGroups.Size()); NW_MINMAXLT_ASSERT(objectIdx, 0, m_AnimObjectCountPerGroup); const int index = groupIdx * m_AnimObjectCountPerGroup + objectIdx; return m_AnimObjects[index]; } //! @brief アニメーションオブジェクトを設定します。 //! アニメーションオブジェクトにバインドされたアニメーショングループの名前から、 //! アニメーションバインディング中の設定先グループを決定します。 //! //! @param[in] animObject アニメーションオブジェクトです。 //! アニメーショングループにバインドされている必要があります。 //! @param[in] objectIdx アニメーショングループについてのアニメーションオブジェクトのindexです。 //! 複数個のアニメーションオブジェクトを一つのグループにバインドする際に指定します。 //! //! @return 成功すれば true を返します。 //! bool SetAnimObject(AnimObject* animObject, int objectIdx = 0) { const AnimGroup* animGroup = animObject->GetAnimGroup(); NW_NULL_ASSERT(animGroup); return SetAnimObject(animGroup->GetName(), animObject, objectIdx); } //! @brief アニメーションオブジェクトを設定します。 //! //! @param[in] groupIdx アニメーションバインディング中の、バインド対象のグループのインデックスです。 //! @param[in] animObject アニメーションオブジェクトです。 //! @param[in] objectIdx アニメーショングループについてのアニメーションオブジェクトのindexです。 //! 複数個のアニメーションオブジェクトを一つのグループにバインドする際に指定します。 //! //! @return 成功すれば true を返します。 //! bool SetAnimObject(int groupIdx, AnimObject* animObject, int objectIdx = 0) { // ここでgroupIdxに入っているのは、対象のグループのインデックス if ((0 <= groupIdx && groupIdx < m_AnimGroups.Size()) && (0 <= objectIdx && objectIdx < m_AnimObjectCountPerGroup)) { const int index = groupIdx * m_AnimObjectCountPerGroup + objectIdx; m_AnimObjects[index] = animObject; return true; } return false; } //! @brief アニメーションオブジェクトを設定します。 //! //! @param[in] animGroupName アニメーショングループ名です。 //! @param[in] animObject アニメーションオブジェクトです。 //! @param[in] objectIdx アニメーショングループについてのアニメーションオブジェクトのindexです。 //! 複数個のアニメーションオブジェクトを一つのグループにバインドする際に指定します。 //! //! @return 成功すれば true を返します。 //! bool SetAnimObject(const char* animGroupName, AnimObject* animObject, int objectIdx = 0) { return SetAnimObject(GetAnimGroupIndex(animGroupName), animObject, objectIdx); } //@} protected: //---------------------------------------- //! @name コンストラクタ/デストラクタ //@{ //! コンストラクタです。 //! //! :private AnimBinding( os::IAllocator* allocator, int maxAnimObjects) : GfxObject(allocator) , m_AnimObjectCountPerGroup(maxAnimObjects) { } //! デストラクタです。 //! //! :private virtual ~AnimBinding() { // AnimGroups, AnimObjects共に、中身は生成元で削除します。 // AnimBindingでは参照しているだけなので、何も行いません。 } //@} //! Initialize() の実行に必要なメモリサイズを取得します。 //! //! :private static void GetMemorySizeForInitialize(os::MemorySizeCalculator* pSize, int maxAnimGroups, int maxAnimObjects) { os::MemorySizeCalculator& size = *pSize; const int animObjectCount = maxAnimGroups * maxAnimObjects; size += sizeof(AnimGroup*) * maxAnimGroups; size += sizeof(AnimObject*) * animObjectCount; } //! メンバを初期化します。 //! //! :private Result Initialize(int maxAnimGroups, int maxAnimObjects) { // AnimObjectの個数は、Builderで設定されたAnimObjectsの数 * Groupの数です。 const int animObjectCount = maxAnimGroups * maxAnimObjects; Result result = INITIALIZE_RESULT_OK; { void* memory = GetAllocator().Alloc(sizeof(AnimGroup*) * maxAnimGroups); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_AnimGroups = AnimGroupArray(memory, maxAnimGroups, &GetAllocator()); } { void* memory = GetAllocator().Alloc(sizeof(AnimObject*) * animObjectCount); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_AnimObjects = AnimObjectArray(memory, animObjectCount, &GetAllocator()); } for (int animGroupIdx = 0; animGroupIdx < maxAnimGroups; ++animGroupIdx) { m_AnimGroups.PushBackFast(NULL); } for (int animObjectIdx = 0; animObjectIdx < animObjectCount; ++animObjectIdx) { m_AnimObjects.PushBackFast(NULL); } return result; } private: //---------------------------------------- // 評価 // ブレンダを使用しない場合の評価処理です。 // 不要なループを省略し、高速に処理します。 NW_FORCE_INLINE void EvaluateSimple(AnimGroup* animGroup, AnimEvaluator* evaluator); NW_FORCE_INLINE void EvaluateTransformSimple(AnimGroup* animGroup, TransformAnimEvaluator* evaluator); // ブレンダを使用する場合の評価処理です。 NW_FORCE_INLINE void EvaluateBlender(AnimGroup* animGroup, AnimObject* animObj); NW_FORCE_INLINE void EvaluateTransformMember(AnimGroup* animGroup, int memberIdx, AnimObject* animObj); //!< @details :private NW_FORCE_INLINE void EvaluateTransformMemberFast(AnimGroup* animGroup, int memberIdx, TransformAnimEvaluator* evaluator); //!< @details :private NW_FORCE_INLINE void EvaluateMember(AnimGroup* animGroup, int memberIdx, AnimObject* animObj, int& lastTargetObjIdx, bool& targetObjSkipFlag); //!< @details :private AnimGroupArray m_AnimGroups; //!< @details :private AnimObjectArray m_AnimObjects; //!< @details :private const int m_AnimObjectCountPerGroup; //!< @details :private }; //--------------------------------------------------------------------------- //! @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) { } //! デストラクタです。 virtual ~BaseAnimEvaluator() {} //@} //---------------------------------------- //! @name 基本操作 //@{ //! @brief アニメーションを関連付けます。 //! //! Bind よりも詳細なバインド結果を得ることができます。 //! //! @param[in] animGroup アニメーショングループです。 //! //! @return バインドの結果を返します。 //! //! @sa Bind //! @sa TryBindResult 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::MaterialBufferCount と 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 virtual 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; } //! 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; private: 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; } }; //--------------------------------------------------------------------------- //! @brief 汎用アニメーションを評価するクラスです。 //! //! アニメーションデータを保持し、ファンクションカーブの評価を行います。 //--------------------------------------------------------------------------- class AnimEvaluator : 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 nw::anim::ResAnim& animData) { m_AnimData = animData; return *this; } //! @brief アニメーション対象メンバの最大数を設定します。 //! //! AnimEvaluator::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 の値を設定してください。 //! AnimEvaluator::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(AnimEvaluator); AnimEvaluator::GetMemorySizeForInitialize(pSize, m_AnimData, m_MaxMembers, m_MaxAnimMembers, m_AllocCache); } //! @brief 汎用アニメーション評価を生成します。 //! //! @param[in] allocator アロケータです。 //! //! @return 生成された汎用アニメーション評価です。 //! AnimEvaluator* Create(os::IAllocator* allocator) { void* buf = allocator->Alloc(sizeof(AnimEvaluator)); if (buf == NULL) { return NULL; } AnimEvaluator* animEvaluator = new(buf) AnimEvaluator(allocator); Result result = animEvaluator->Initialize(m_AnimData, m_MaxMembers, m_MaxAnimMembers, m_AllocCache); NW_ASSERT(result.IsSuccess()); return animEvaluator; } private: nw::anim::ResAnim m_AnimData; int m_MaxMembers; int m_MaxAnimMembers; bool m_AllocCache; }; //@} //---------------------------------------- //! @name 基本操作 //@{ //! @brief アニメーションを変更します。 //! //! GetCacheBufferSizeNeeded() の値が変化しますので、 //! SetCacheBuffer() している場合はバッファの再設定が必要です。 //! //! 内部で確保したキャッシュバッファは、自動的にサイズ変更されます。 //! //! @param animData アニメーションデータです。 //! @sa BaseAnimEvaluator::ChangeAnim virtual void ChangeAnim(const nw::anim::ResAnim animData) { // キャッシュサイズを求めるのにm_AnimDataを参照するので、 // 先に更新する必要がある BaseAnimEvaluator::ChangeAnim(animData); m_CachePtrs.Resize(animData.GetMemberAnimSetCount()); if (!m_IsCacheExternal && m_CacheBuf != NULL) { os::SafeFree(m_CacheBuf, &GetAllocator()); // サイズ0でのAllocを回避する // CreateEmpty~Anim()などを使用した場合に起こりうる if (animData.GetMemberAnimSetCount() != 0) { m_CacheBuf = GetAllocator().Alloc(GetCacheBufferSizeNeeded()); NW_NULL_ASSERT(m_CacheBuf); SetCacheBufferPointers(); } } } //@} //---------------------------------------- //! @name 評価 //@{ //! @brief メンバ単位でアニメーション結果を取得します。 //! //! @param[out] target アニメーション結果を書き込む対象です。 //! @param[in] memberIdx メンバインデックスです。 //! //! @return アニメーション結果を返します。 //! ブレンドオペレーションを使用する場合は、返り値のアニメーション結果を使用してください。 //! virtual const anim::AnimResult* GetResult( void* target, int memberIdx) const; //@} //---------------------------------------- //! @name 取得/設定 //@{ //! @brief メンバに関連付けられたアニメーションが存在するかどうかを取得します。 //! //! @param[in] memberIdx メンバインデックスです。 //! //! @return アニメーションが存在すれば true を返します。 //! virtual bool HasMemberAnim(int memberIdx) const { NW_MINMAXLT_ASSERT(memberIdx, 0, m_BindIndexTable.Size()); return m_BindIndexTable[memberIdx] != NotFoundIndex; } //@} //---------------------------------------- //! @name キャッシュ //@{ //! アニメーション評価結果の内部キャッシュが古ければ更新します。 virtual void UpdateCache() { this->UpdateCacheNonVirtual(); } //! :private void UpdateCacheNonVirtual(); //! キャッシュバッファに必要なサイズ(バイト数)を取得します。 //! @return 必要なサイズ(バイト数)です。 virtual int GetCacheBufferSizeNeeded() const; //! キャッシュバッファを取得します。 virtual const void* GetCacheBuffer() const { return m_CacheBuf; } //! @brief キャッシュバッファを設定します。 //! この関数で指定したキャッシュバッファはデストラクタで解放されません。 //! //! @param[in] buf キャッシュバッファ用のメモリの先頭アドレスです。 //! NULL の場合、キャッシュが無効になります。 //! @param[in] size キャッシュバッファのサイズ(バイト数)です。 //! virtual void SetCacheBuffer(void* buf, int size) { m_CacheBuf = buf; if (buf != NULL) { NW_ASSERT(size >= GetCacheBufferSizeNeeded()); (void)size; m_IsCacheDirty = true; m_IsCacheExternal = true; SetCacheBufferPointers(); } } //@} protected: //---------------------------------------- //! @name コンストラクタ/デストラクタ //@{ //! コンストラクタです。 //! //! :private AnimEvaluator( os::IAllocator* allocator); //! デストラクタです。 //! //! :private virtual ~AnimEvaluator() { if (!m_IsCacheExternal && m_CacheBuf != NULL) { GetAllocator().Free(m_CacheBuf); } } //@} //! Initialize() の実行に必要なメモリサイズを取得します。 //! //! :private static void GetMemorySizeForInitialize( os::MemorySizeCalculator* pSize, const nw::anim::ResAnim& animData, const int maxMembers, const int maxAnimMembers, bool allocCache); //! @details :private virtual Result Initialize( const nw::anim::ResAnim& animData, const int maxMembers, const int maxAnimMembers, bool allocCache); //! キャッシュバッファに必要なサイズ(バイト数)を取得します。 //! //! :private static int GetCacheBufferSizeNeeded(const anim::ResAnim& animData); //! 各メンバアニメのキャッシュバッファへのポインタを設定します。 //! //! :private void SetCacheBufferPointers(); //! AnimResult を実サイズで詰めたバッファ //! //! :private void* m_CacheBuf; //! 各メンバアニメの m_CacheBuf 中のポインタ //! //! :private ut::MoveArray m_CachePtrs; }; //--------------------------------------------------------------------------- //! @brief 汎用アニメーションブレンドの基底クラスです。 //! 抽象クラスですのでインスタンス化して使用することはできません。 //! //! AnimBlender は複数の AnimEvaluator のアニメーション評価結果をブレンドします。 //! AnimBlender で他の AnimBlender のアニメーション評価結果をブレンドすることも可能です。 //--------------------------------------------------------------------------- class AnimBlender : public AnimObject { public: NW_UT_RUNTIME_TYPEINFO; //! アニメーションオブジェクトの MoveArray の定義です。 //! //! :private typedef ut::MoveArray AnimObjectArray; //---------------------------------------- //! @name コンストラクタ/デストラクタ //@{ //! コンストラクタです。 AnimBlender( os::IAllocator* allocator) : AnimObject(allocator, ANIMTYPE_BLENDER) { } //! デストラクタです。 virtual ~AnimBlender() {} //@} //---------------------------------------- //! @name 基本操作 //@{ //! @brief アニメーションを関連付けます。 //! //! Bind よりも詳細なバインド結果を得ることができます。 //! //! @param[in] animGroup アニメーショングループです。 //! //! @return バインドの結果を返します。 //! //! @sa Bind //! @sa TryBindResult virtual Result TryBind(AnimGroup* animGroup) { NW_NULL_ASSERT(animGroup); NW_ASSERT(m_AnimGroup == NULL); m_AnimGroup = animGroup; return Result(BIND_RESULT_OK); } //! アニメーションの関連付けを解除します。 virtual void Release() { m_AnimGroup = NULL; } //! フレームを更新します。 virtual void UpdateFrame() { for (int animObjIdx = 0; animObjIdx < m_AnimObjects.Size(); ++animObjIdx) { if (m_AnimObjects[animObjIdx] != NULL) { m_AnimObjects[animObjIdx]->UpdateFrame(); } } } //@} //---------------------------------------- //! @name 取得/設定 //@{ //! @brief メンバに関連付けられたアニメーションが存在するかどうかを取得します。 //! //! @param[in] memberIdx メンバインデックスです。 //! //! @return アニメーションが存在すれば true を返します。 //! virtual bool HasMemberAnim(int memberIdx) const { for (int animObjIdx = 0; animObjIdx < m_AnimObjects.Size(); ++animObjIdx) { if (m_AnimObjects[animObjIdx] != NULL && m_AnimObjects[animObjIdx]->HasMemberAnim(memberIdx)) { return true; } } return false; } //@} //---------------------------------------- //! @name アニメーションオブジェクト //@{ //! @brief アニメーションオブジェクトを追加します。 //! //! @param[in] animObj 追加するアニメーションオブジェクトです。 //! NULL を指定しておいて、後で ReplaceAnimObject() で置き換えることも可能です。 //! void AddAnimObject(AnimObject* animObj) { NW_ASSERT(m_AnimObjects.Size() < m_AnimObjects.Capacity()); m_AnimObjects.PushBack(animObj); } //! @brief アニメーションオブジェクトを取得します。 //! //! @param[in] animObjIdx アニメーションオブジェクトのインデックスです。 //! //! @return アニメーションオブジェクトです。 //! const AnimObject* GetAnimObject(int animObjIdx) const { NW_MINMAXLT_ASSERT(animObjIdx, 0, m_AnimObjects.Size()); return m_AnimObjects[animObjIdx]; } //! @brief アニメーションオブジェクトを取得します。 //! //! @param[in] animObjIdx アニメーションオブジェクトのインデックスです。 //! //! @return アニメーションオブジェクトです。 //! AnimObject* GetAnimObject(int animObjIdx) { NW_MINMAXLT_ASSERT(animObjIdx, 0, m_AnimObjects.Size()); return m_AnimObjects[animObjIdx]; } //! @brief アニメーションオブジェクトを置き替えます。 //! //! @param[in] animObjIdx 置き替えるアニメーションオブジェクトのインデックスです。 //! @param[in] animObj アニメーションオブジェクトです。 //! NULL を指定した場合、アニメーションオブジェクトがない状態になります。 //! //! @return 置き替える前のアニメーションオブジェクトです。 //! AnimObject* ReplaceAnimObject(int animObjIdx, AnimObject* animObj) { NW_MINMAXLT_ASSERT(animObjIdx, 0, m_AnimObjects.Size()); AnimObject* oldObj = m_AnimObjects[animObjIdx]; m_AnimObjects[animObjIdx] = animObj; return oldObj; } //! アニメーションオブジェクトをクリアします。 void ClearAnimObjects() { m_AnimObjects.Clear(); } //! 追加されているアニメーションオブジェクト数を取得します。 int GetAnimObjectCount() const { return m_AnimObjects.Size(); } //! アニメーションオブジェクトの最大数を取得します。 int GetMaxAnimObjects() const { return m_AnimObjects.Capacity(); } //@} //---------------------------------------- //! @name キャッシュ //@{ //! アニメーション評価結果の内部キャッシュが古ければ更新します。 virtual void UpdateCache() { for (int animObjIdx = 0; animObjIdx < m_AnimObjects.Size(); ++animObjIdx) { if (m_AnimObjects[animObjIdx] != NULL) { m_AnimObjects[animObjIdx]->UpdateCache(); } } } //@} protected: //! Initialize() の実行に必要なメモリサイズを取得します。 //! //! :private static void GetMemorySizeForInitialize(os::MemorySizeCalculator* pSize, int maxAnimObjects) { os::MemorySizeCalculator& size = *pSize; size += sizeof(AnimObject*) * maxAnimObjects; } //! @details :private virtual Result Initialize(int maxAnimObjects) { Result result = INITIALIZE_RESULT_OK; void* memory = GetAllocator().Alloc(sizeof(AnimObject*) * maxAnimObjects); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_AnimObjects = AnimObjectArray(memory, maxAnimObjects, &GetAllocator()); return result; } AnimObjectArray m_AnimObjects; //!< @details :private }; //--------------------------------------------------------------------------- //! @brief 汎用アニメーション評価結果を補間ブレンドするクラスです。 //! //! 登録された全てのアニメーションオブジェクトの結果に重みをかけて、ブレンドした結果を採用します。 //! アニメーションが無いメンバの場合は、モデルのロード時の状態(OriginalValue)をブレンド計算に使用します。 //! //! デフォルトでは、ブレンドの重みは合計が 1.0 となるように正規化されてからブレンドされるので、重みの比率だけが意味を持ちます。 //! 正規化が不要な場合は、 SetNormalizationEnabled() で無効化できます。 //--------------------------------------------------------------------------- class AnimInterpolator : public AnimBlender { public: NW_UT_RUNTIME_TYPEINFO; //---------------------------------------- //! @name 作成 //@{ //! @brief 汎用アニメーション補間を構築するクラスです。 //! //! バージョン 1.0.1 以前の補間法に戻すためには、IsOldMethod か IgnoreNoAnimMember に true を指定してください。 //! //! 1.0.1 以前の補間方法と現在の補間方法の違いの詳細については、アニメーションのドキュメント(高度な機能)を参照ください。 class Builder { public: //! コンストラクタです。 Builder() : m_MaxAnimObjects(2), m_IgnoreNoAnimMember(false) {} //! 最大アニメーションオブジェクト数を設定します。 Builder& MaxAnimObjects(int maxAnimObjects) { NW_ASSERT(maxAnimObjects > 0); m_MaxAnimObjects = maxAnimObjects; return *this; } //! @brief アニメーションが存在しないメンバを無視するかどうかを設定します。 //! //! デフォルトでは、アニメーションが存在しないメンバはバインド時の値がブレンドされます。 //! IgnoreNoAnimMember に true を設定すると、 //! 重みの正規化がメンバ毎に行なわれ、アニメーションが存在しないメンバは重み 0 としてブレンドされます。 //! //! この挙動は バージョン 1.0.1 以前の補間法と同じです。 //! Builder& IgnoreNoAnimMember(bool ignoreNoAnimMember) { m_IgnoreNoAnimMember = ignoreNoAnimMember; 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(AnimInterpolator); AnimInterpolator::GetMemorySizeForInitialize(pSize, m_MaxAnimObjects); } //! @brief 汎用アニメーション補間を生成します。 //! //! @param[in] allocator アロケータです。 //! //! @return 生成された汎用アニメーション補間です。 //! AnimInterpolator* Create(os::IAllocator* allocator) { void* buf = allocator->Alloc(sizeof(AnimInterpolator)); if (buf == NULL) { return NULL; } AnimInterpolator* animInterpolator = new(buf) AnimInterpolator(allocator); Result result = animInterpolator->Initialize(m_MaxAnimObjects, m_IgnoreNoAnimMember); NW_ASSERT(result.IsSuccess()); return animInterpolator; } private: int m_MaxAnimObjects; bool m_IgnoreNoAnimMember; }; //@} //---------------------------------------- //! @name 評価 //@{ //! @brief メンバ単位でアニメーション結果を取得します。 //! //! @param[out] target アニメーション結果を書き込む対象です。 //! @param[in] memberIdx メンバインデックスです。 //! //! @return アニメーション結果を返します。 //! ブレンドオペレーションを使用する場合は、返り値のアニメーション結果を使用してください。 //! virtual const anim::AnimResult* GetResult( void* target, int memberIdx) const; //@} //---------------------------------------- //! @name 取得/設定 //@{ //! @brief アニメーションのブレンド重みを取得します。 //! //! @param[in] animObjIdx アニメーションオブジェクトのインデックスです。 //! //! @return 重みです。 //! float GetWeight(int animObjIdx) const { NW_MINMAXLT_ASSERT(animObjIdx, 0, m_Weights.Size()); return m_Weights[animObjIdx]; } //! @brief アニメーションのブレンド重みを設定します。 //! //! ブレンド重みの解釈については、 SetNormalizationEnabled() を参照してください。 //! //! @param[in] animObjIdx アニメーションオブジェクトのインデックスです。 //! @param[in] weight 重みです。 //! @sa SetNormalizationEnabled //! void SetWeight(int animObjIdx, float weight) { NW_MINMAXLT_ASSERT(animObjIdx, 0, m_Weights.Size()); m_Weights[animObjIdx] = weight; m_NormalizedWeights[animObjIdx] = weight; m_IsWeightDirty = true; } //! @brief アニメーションのブレンド重みを正規化するかを設定します。 //! //! true を指定すると、ブレンド重みが合計で 1 になるよう正規化してからブレンドを行います。 //! false を指定すると、SetWeight() で指定された重みがそのままブレンドに使用されますので、 //! 重みの設定に注意してください。 //! //! 正規化処理は、 SetWeight() 実行後の最初のアニメーションブレンド時に 1 度だけ行われます。 //! //! Builder::IsOldMethod で true を指定していると、この設定は無視されます。 //! //! デフォルト値は true です。 void SetNormalizationEnabled(bool enabled){ m_IsWeightNormalizationEnabled = enabled; } //! @brief アニメーションのブレンド重みを正規化するかの設定を取得します。 bool GetNormalizationEnabled() const { return m_IsWeightNormalizationEnabled; } //@} protected: //---------------------------------------- //! @name コンストラクタ/デストラクタ //@{ //! コンストラクタです。 //! //! :private AnimInterpolator( os::IAllocator* allocator) : AnimBlender(allocator), m_IsOldMethod(false), m_IsWeightDirty(false), m_IsWeightNormalizationEnabled(true) { } //! デストラクタです。 //! //! :private virtual ~AnimInterpolator() {} //@} //! Initialize() の実行に必要なメモリサイズを取得します。 //! //! :private static void GetMemorySizeForInitialize(os::MemorySizeCalculator* pSize, int maxAnimObjects) { os::MemorySizeCalculator& size = *pSize; AnimBlender::GetMemorySizeForInitialize(pSize, maxAnimObjects); size += sizeof(float) * maxAnimObjects; size += sizeof(float) * maxAnimObjects; } //! @details :private virtual Result Initialize(int maxAnimObjects, bool ignoreNoAnimMember) { Result result = AnimBlender::Initialize(maxAnimObjects); NW_ENSURE_AND_RETURN(result); { void* memory = GetAllocator().Alloc(sizeof(float) * maxAnimObjects); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_Weights = ut::MoveArray(memory, maxAnimObjects, &GetAllocator()); for (int animObjIdx = 0; animObjIdx < maxAnimObjects; ++animObjIdx) { m_Weights.PushBackFast(animObjIdx == 0 ? 1.0f : 0.0f); } } { void* memory = GetAllocator().Alloc(sizeof(float) * maxAnimObjects); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_NormalizedWeights = ut::MoveArray(memory, maxAnimObjects, &GetAllocator()); for (int animObjIdx = 0; animObjIdx < maxAnimObjects; ++animObjIdx) { m_NormalizedWeights.PushBackFast(animObjIdx == 0 ? 1.0f : 0.0f); } } // TODO: 変数名その他を変更。 m_IsOldMethod = ignoreNoAnimMember; return result; } //! @brief ブレンド重みを正規化します。 //! //! :private void NormalizeWeight() const { float weightSum = 0.0f; float normalizeScale; for (int i = 0; i < m_Weights.Size(); ++i) { weightSum += m_Weights[i]; } normalizeScale = GetAnimWeightNormalizeScale(weightSum); for (int i = 0; i < m_Weights.Size(); ++i) { m_NormalizedWeights[i] = m_Weights[i] * normalizeScale; } m_IsWeightDirty = false; } ut::MoveArray m_Weights; //!< @details :private mutable ut::MoveArray m_NormalizedWeights; //!< @details :private bool m_IsOldMethod; //!< @details :private mutable bool m_IsWeightDirty; //!< @details :private bool m_IsWeightNormalizationEnabled; //!< @details :private }; //--------------------------------------------------------------------------- //! @details :private //! @brief 汎用アニメーション評価結果を加算ブレンドするクラスです。 //--------------------------------------------------------------------------- class AnimAdder : public AnimBlender { public: NW_UT_RUNTIME_TYPEINFO; //---------------------------------------- //! @name 作成 //@{ //! 汎用アニメーション補間を構築するクラスです。 class Builder { public: //! コンストラクタです。 Builder() : m_MaxAnimObjects(2) {} //! 最大アニメーションオブジェクト数を設定します。 Builder& MaxAnimObjects(int maxAnimObjects) { NW_ASSERT(maxAnimObjects > 0); m_MaxAnimObjects = maxAnimObjects; 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(AnimAdder); AnimAdder::GetMemorySizeForInitialize(pSize, m_MaxAnimObjects); } //! @brief 汎用アニメーション補間を生成します。 //! //! @param[in] allocator アロケータです。 //! //! @return 生成された汎用アニメーション補間です。 //! AnimAdder* Create(os::IAllocator* allocator) { void* buf = allocator->Alloc(sizeof(AnimAdder)); if (buf == NULL) { return NULL; } AnimAdder* animAdder = new(buf) AnimAdder(allocator); Result result = animAdder->Initialize(m_MaxAnimObjects); NW_ASSERT(result.IsSuccess()); return animAdder; } private: int m_MaxAnimObjects; }; //@} //---------------------------------------- //! @name 評価 //@{ //! @brief メンバ単位でアニメーション結果を取得します。 //! //! @param[out] target アニメーション結果を書き込む対象です。 //! @param[in] memberIdx メンバインデックスです。 //! //! @return アニメーション結果を返します。 //! ブレンドオペレーションを使用する場合は、返り値のアニメーション結果を使用してください。 //! virtual const anim::AnimResult* GetResult( void* target, int memberIdx) const; //@} //---------------------------------------- //! @name 取得/設定 //@{ //! @brief アニメーションのブレンド重みを取得します。 //! //! @param[in] animObjIdx アニメーションオブジェクトのインデックスです。 //! //! @return 重みです。 //! float GetWeight(int animObjIdx) const { NW_MINMAXLT_ASSERT(animObjIdx, 0, m_Weights.Size()); return m_Weights[animObjIdx]; } //! @brief アニメーションのブレンド重みを設定します。 //! //! @param[in] animObjIdx アニメーションオブジェクトのインデックスです。 //! @param[in] weight 重みです。 //! void SetWeight(int animObjIdx, float weight) { NW_MINMAXLT_ASSERT(animObjIdx, 0, m_Weights.Size()); m_Weights[animObjIdx] = weight; } //@} protected: //---------------------------------------- //! @name コンストラクタ/デストラクタ //@{ //! コンストラクタです。 AnimAdder( os::IAllocator* allocator) : AnimBlender(allocator) { } //! デストラクタです。 virtual ~AnimAdder() {} //@} //! Initialize() の実行に必要なメモリサイズを取得します。 //! //! :private static void GetMemorySizeForInitialize(os::MemorySizeCalculator* pSize, int maxAnimObjects) { os::MemorySizeCalculator& size = *pSize; AnimBlender::GetMemorySizeForInitialize(pSize, maxAnimObjects); size += sizeof(float) * maxAnimObjects; } virtual Result Initialize(int maxAnimObjects) { Result result = AnimBlender::Initialize(maxAnimObjects); NW_ENSURE_AND_RETURN(result); void* memory = GetAllocator().Alloc(sizeof(float) * maxAnimObjects); if (memory == NULL) { result |= Result::MASK_FAIL_BIT; } NW_ENSURE_AND_RETURN(result); m_Weights = ut::MoveArray(memory, maxAnimObjects, &GetAllocator()); for (int animObjIdx = 0; animObjIdx < maxAnimObjects; ++animObjIdx) { m_Weights.PushBackFast(1.0f); } return result; } ut::MoveArray m_Weights; }; //--------------------------------------------------------------------------- //! @brief 汎用アニメーション評価結果を上書きブレンドするクラスです。 //! //! AddAnimObject() で登録された順番にアニメーションオブジェクトを評価します。 //! 同じメンバに複数のアニメーションがあった場合、後に登録されたアニメーションの結果で上書きされます。 //! アニメーションを持っていないメンバについては、上書き処理は行われません。 //--------------------------------------------------------------------------- class AnimOverrider : public AnimBlender { public: NW_UT_RUNTIME_TYPEINFO; //---------------------------------------- //! @name 作成 //@{ //! 汎用アニメーション補間を構築するクラスです。 class Builder { public: //! コンストラクタです。 Builder() : m_MaxAnimObjects(2) {} //! 最大アニメーションオブジェクト数を設定します。 Builder& MaxAnimObjects(int maxAnimObjects) { NW_ASSERT(maxAnimObjects > 0); m_MaxAnimObjects = maxAnimObjects; 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(AnimOverrider); AnimOverrider::GetMemorySizeForInitialize(pSize, m_MaxAnimObjects); } //! @brief 汎用アニメーション補間を生成します。 //! //! @param[in] allocator アロケータです。 //! //! @return 生成された汎用アニメーション補間です。 //! AnimOverrider* Create(os::IAllocator* allocator) { void* buf = allocator->Alloc(sizeof(AnimOverrider)); if (buf == NULL) { return NULL; } AnimOverrider* animOverrider = new(buf) AnimOverrider(allocator); Result result = animOverrider->Initialize(m_MaxAnimObjects); NW_ASSERT(result.IsSuccess()); return animOverrider; } private: int m_MaxAnimObjects; }; //@} //---------------------------------------- //! @name 評価 //@{ //! @brief メンバ単位でアニメーション結果を取得します。 //! //! @param[out] target アニメーション結果を書き込む対象です。 //! @param[in] memberIdx メンバインデックスです。 //! //! @return アニメーション結果を返します。 //! ブレンドオペレーションを使用する場合は、返り値のアニメーション結果を使用してください。 //! virtual const anim::AnimResult* GetResult( void* target, int memberIdx) const; //@} protected: //---------------------------------------- //! @name コンストラクタ/デストラクタ //@{ //! コンストラクタです。 //! //! :private AnimOverrider( os::IAllocator* allocator) : AnimBlender(allocator) {} //! デストラクタです。 //! //! :private virtual ~AnimOverrider() {} //@} }; } // namespace gfx } // namespace nw #endif // NW_GFX_ANIMOBJECT_H_