1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     gfx_BaseAnimEvaluator.h
4 
5   Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain proprietary
8   information of Nintendo and/or its licensed developers and are protected by
9   national and international copyright laws. They may not be disclosed to third
10   parties or copied or duplicated in any form, in whole or in part, without the
11   prior written consent of Nintendo.
12 
13   The content herein is highly confidential and should be handled accordingly.
14 
15   $Revision: 32369 $
16  *---------------------------------------------------------------------------*/
17 
18 #ifndef NW_GFX_BASEANIMEVALUATOR_H_
19 #define NW_GFX_BASEANIMEVALUATOR_H_
20 
21 #include <nw/anim/anim_AnimFrameController.h>
22 #include <nw/anim/res/anim_ResAnim.h>
23 #include <nw/gfx/gfx_AnimGroup.h>
24 #include <nw/gfx/gfx_AnimObject.h>
25 #include <nw/ut/ut_MoveArray.h>
26 #include <nw/ut/ut_RuntimeTypeInfo.h>
27 
28 namespace nw {
29 namespace gfx {
30 
31 namespace internal
32 {
33     //! :private
34     void ClearMaterialHash(anim::ResAnimGroupMember member);
35 }
36 
37 //---------------------------------------------------------------------------
38 //! @brief アニメーション評価の基底クラスです。
39 //! 抽象クラスですのでインスタンス化して使用することはできません。
40 //---------------------------------------------------------------------------
41 class BaseAnimEvaluator : public AnimObject
42 {
43 public:
44     NW_UT_RUNTIME_TYPEINFO;
45 
46     //! 関連付け対象がない場合のインデックスです。
47     //!
48     //! :private
49     static const int NotFoundIndex;
50 
51     //----------------------------------------
52     //! @name コンストラクタ/デストラクタ
53     //@{
54 
55     //! コンストラクタです。
BaseAnimEvaluator(os::IAllocator * allocator,u32 animType)56     BaseAnimEvaluator(
57         os::IAllocator* allocator, u32 animType)
58     : AnimObject(allocator, animType),
59       m_IsCacheDirty(true),
60       m_IsCacheExternal(false),
61       m_UseSharedCache(false)
62     {
63     }
64 
65     //! デストラクタです。
~BaseAnimEvaluator()66     virtual ~BaseAnimEvaluator() {}
67 
68     //@}
69 
70     //----------------------------------------
71     //! @name 基本操作
72     //@{
73 
74     //! @brief アニメーションを関連付けます。
75     //!
76     //! Bind よりも詳細なバインド結果を得ることができます。
77     //!
78     //! @param[in] animGroup アニメーショングループです。
79     //!
80     //! @return バインドの結果を返します。
81     //!
82     //! @sa Bind
83     virtual Result TryBind(AnimGroup* animGroup);
84 
85     //! アニメーションの関連付けを解除します。
Release()86     virtual void Release()
87     {
88         m_AnimGroup = NULL;
89     }
90 
91     //! フレームを更新します。
UpdateFrame()92     virtual void UpdateFrame()
93     {
94         // 更新フレームが 0 の場合は、SetStepFrame() で
95         // キャッシュを更新済みです。
96         if (GetStepFrame() != 0.0f)
97         {
98             m_AnimFrameController.UpdateFrame();
99             m_IsCacheDirty = true;
100         }
101     }
102 
103     //! @brief アニメーションを変更します。
104     //!
105     //! AnimEvaluator::Bind の後でアニメーションを差し替える場合に使用します。
106     //! AnimEvaluator を破棄して再生成するより高速です。
107     //!
108     //! 再生フレームは 0.0f にリセットされます。
109     //!
110     //! @param animData アニメーションデータです。
ChangeAnim(const nw::anim::ResAnim animData)111     virtual void ChangeAnim(const nw::anim::ResAnim animData)
112     {
113         NW_NULL_ASSERT(m_AnimGroup);
114 
115         // このASSERTが失敗した場合は、Builder::MaxAnimMembers の値を大きくしてください
116         NW_ASSERT(animData.GetMemberAnimSetCount() <= m_ReverseBindIndexTable.Capacity());
117 
118         SetResAnim(animData);
119 
120         AnimGroup* animGroup = m_AnimGroup;
121         Release();
122         Bind(animGroup);
123     }
124 
125     //! @brief すべてのアニメーション対象メンバーを生成時の状態に戻します。
126     //!
127     //! マテリアルに対して行う場合、マテリアルのバッファを生成する必要があります。
128     //! SceneBuilder クラスでモデルを作成する際のオプションで、
129     //! SceneBuilder::BufferOption を設定してください。
130     //! BufferOption には、 Model::MULTI_FLAG_ANIMATABLE_MATERIAL の内容が最低限必要です。
Reset()131     void Reset()
132     {
133         for (int i = (m_AnimGroup->GetMemberCount() - 1); i >= 0; --i)
134         {
135             this->ResetMember(i);
136         }
137     }
138 
139     //! @brief 指定されたメンバーを生成時の状態に戻します。
140     //! @param memberIdx 対象のメンバーです。
141     void ResetMember(int memberIdx);
142 
143     //@}
144 
145     //----------------------------------------
146     //! @name フレーム制御
147     //@{
148 
149     //! フレームを取得します。
GetFrame()150     float GetFrame() const { return m_AnimFrameController.GetFrame(); }
151 
152     //! @brief フレームを設定します。
153     //!
154     //! @param[in] frame 設定するフレームです。
155     //!
SetFrame(float frame)156     void SetFrame(float frame)
157     {
158         m_AnimFrameController.SetFrame(frame);
159         m_IsCacheDirty = true;
160     }
161 
162     //! @brief フレームをリセットします。
163     //!
164     //! @param[in] frame 設定するフレームです。
165     //!
ResetFrame(f32 frame)166     void ResetFrame(f32 frame)
167     {
168         m_AnimFrameController.GetAnimFrame().ResetFrame(frame);
169         m_IsCacheDirty = true;
170     }
171 
172     //! @brief フレームの増分を取得します。
173     //! @sa SetStepFrame
GetStepFrame()174     float GetStepFrame() const { return m_AnimFrameController.GetStepFrame(); }
175 
176     //! @brief フレームの増分を設定します。
177     //!
178     //! UpdateFrame() を呼び出すと、現在のフレームにフレームの増分が加算されます。
179     //! 例えば SetStepFrame(2.0f) とすると、2倍速再生になります。
180     //!
181     //! @param[in] stepFrame フレームの増分です。
SetStepFrame(float stepFrame)182     void SetStepFrame(float stepFrame)
183     {
184         m_AnimFrameController.SetStepFrame(stepFrame);
185         if (stepFrame == 0.0f)
186         {
187             m_IsCacheDirty = true;
188             UpdateCache();
189         }
190     }
191 
192     //! @brief 開始フレームを取得します。
193     //! @sa SetStartFrame
GetStartFrame()194     float GetStartFrame() const { return m_AnimFrameController.GetStartFrame(); }
195 
196     //! @brief 開始フレームを設定します。
197     //!
198     //! 開始フレームを現在のフレームより後に設定した場合、
199     //! 現在のフレームを開始フレームに移動します。
200     //!
201     //! @param[in] startFrame 開始フレームです。
SetStartFrame(float startFrame)202     void SetStartFrame(float startFrame)
203     {
204         m_AnimFrameController.SetStartFrame(startFrame);
205         if (startFrame > GetFrame())
206         {
207             SetFrame(startFrame);
208         }
209     }
210 
211     //! @brief 終了フレームを取得します。
212     //! @sa SetEndFrame
GetEndFrame()213     float GetEndFrame() const { return m_AnimFrameController.GetEndFrame(); }
214 
215     //! @brief 終了フレームを設定します。
216     //!
217     //! 終了フレームを現在のフレームより前に設定した場合、
218     //! 現在のフレームを終了フレームに移動します。
219     //!
220     //! @param[in] endFrame 終了フレームです。
SetEndFrame(float endFrame)221     void SetEndFrame(float endFrame)
222     {
223         m_AnimFrameController.SetEndFrame(endFrame);
224         if (endFrame < GetFrame())
225         {
226             SetFrame(endFrame);
227         }
228     }
229 
230     //! アニメーション再生方法を取得します。
GetPlayPolicy()231     nw::anim::AnimFrameController::PlayPolicy GetPlayPolicy() const
232     {
233         return m_AnimFrameController.GetPlayPolicy();
234     }
235 
236     //! @brief アニメーション再生方法を設定します。
237     //!
238     //! @param[in] playPolicy 設定するアニメーション再生方法です。
239     //! @sa nw::anim::AnimFrameController::SetPlayPolicy
SetPlayPolicy(nw::anim::AnimFrameController::PlayPolicy playPolicy)240     void SetPlayPolicy(nw::anim::AnimFrameController::PlayPolicy playPolicy)
241     {
242         m_AnimFrameController.SetPlayPolicy(playPolicy);
243         m_IsCacheDirty = true;
244     }
245 
246     //! アニメーションフレーム制御情報を取得します。
AnimFrameController()247     const nw::anim::AnimFrameController& AnimFrameController() const { return m_AnimFrameController; }
248 
249     //@}
250 
251     //----------------------------------------
252     //! @name 取得/設定
253     //@{
254 
255     //! ユーザーデータを取得します。
GetUserData()256     const void* GetUserData() const { return m_AnimFrameController.GetUserData(); }
257 
258     //! ユーザーデータを取得します。
GetUserData()259     void* GetUserData() { return m_AnimFrameController.GetUserData(); }
260 
261     //! @brief ユーザーデータを設定します。
262     //!
263     //! @param[in] userData 設定するユーザーデータです。
264     //! @sa nw::anim::AnimFrameController::SetUserData
265     //!
SetUserData(void * userData)266     void SetUserData(void* userData) { m_AnimFrameController.SetUserData(userData); }
267 
268     //! ResAnimGroupMember のインデックスから ResMemberAnim のインデックスへの変換テーブルを取得します。
BindIndexTable()269     const ut::MoveArray<int>& BindIndexTable() const { return m_BindIndexTable; }
270 
271     //! ResAnimGroupMember のインデックスから ResMemberAnim のインデックスへの変換テーブルを取得します。
BindIndexTable()272     ut::MoveArray<int>& BindIndexTable() { return m_BindIndexTable; }
273 
274     //! ResMemberAnim のインデックスから ResAnimGroupMember のインデックスへの変換テーブルを取得します。
ReverseBindIndexTable()275     const ut::MoveArray<int>& ReverseBindIndexTable() const { return m_ReverseBindIndexTable; }
276 
277     //! ResMemberAnim のインデックスから ResAnimGroupMember のインデックスへの変換テーブルを取得します。
ReverseBindIndexTable()278     ut::MoveArray<int>& ReverseBindIndexTable() { return m_ReverseBindIndexTable; }
279 
280     //! アニメーションデータを取得します。
GetAnimData()281     const nw::anim::ResAnim GetAnimData() const { return m_AnimData; }
282 
283     //@}
284 
285     //----------------------------------------
286     //! @name キャッシュ
287     //@{
288 
289     //! キャッシュバッファを取得します。
GetCacheBuffer()290     virtual const void* GetCacheBuffer() const { return NULL; }
291 
292     //! キャッシュバッファに必要なサイズ(バイト数)を取得します。
GetCacheBufferSizeNeeded()293     virtual int GetCacheBufferSizeNeeded() const { return 0; }
294 
295     //! @brief キャッシュバッファを設定します。
296     //! この関数で指定したキャッシュバッファはデストラクタで解放されません。
297     //!
298     //! @param[in] buf キャッシュバッファ用のメモリの先頭アドレスです。
299     //! NULL の場合、キャッシュが無効になります。
300     //! @param[in] size キャッシュバッファのサイズ(バイト数)です。
301     //!
SetCacheBuffer(void * buf,int size)302     virtual void SetCacheBuffer(void* buf, int size)
303     {
304         (void)buf;
305         (void)size;
306     }
307 
308     //@}
309 
310 protected:
311     //! Initialize() の実行に必要なメモリサイズを取得します。
312     //!
313     //! :private
GetMemorySizeForInitialize(os::MemorySizeCalculator * pSize,const int maxMembers,const int maxAnimMembers)314     static void GetMemorySizeForInitialize(
315         os::MemorySizeCalculator* pSize,
316         const int maxMembers,
317         const int maxAnimMembers)
318     {
319         os::MemorySizeCalculator& size = *pSize;
320 
321         size += sizeof(int) * maxMembers;
322         size += sizeof(int) * maxAnimMembers;
323     }
324 
325     //! メンバを初期化します。
326     //!
327     //! :private
Initialize(const nw::anim::ResAnim & animData,const int maxMembers,const int maxAnimMembers)328     Result Initialize(
329         const nw::anim::ResAnim& animData,
330         const int maxMembers,
331         const int maxAnimMembers)
332     {
333         Result result = INITIALIZE_RESULT_OK;
334 
335         SetResAnim(animData);
336 
337         {
338             void* memory = GetAllocator().Alloc(sizeof(int) * maxMembers);
339             if (memory == NULL)
340             {
341                 result |= Result::MASK_FAIL_BIT;
342             }
343             NW_ENSURE_AND_RETURN(result);
344 
345             m_BindIndexTable = ut::MoveArray<int>(memory, maxMembers, &GetAllocator());
346         }
347 
348         {
349             void* memory = GetAllocator().Alloc(sizeof(int) * maxAnimMembers);
350             if (memory == NULL)
351             {
352                 result |= Result::MASK_FAIL_BIT;
353             }
354             NW_ENSURE_AND_RETURN(result);
355 
356             m_ReverseBindIndexTable = ut::MoveArray<int>(memory, maxAnimMembers, &GetAllocator());
357         }
358 
359         return result;
360     }
361 
362     //! @brief TryBind のテンプレート関数です。
363     //!
364     //!        メンバアニメのバインドをカスタマイズできます。
365     //!
366     //! @tparam T アニメメンバのインデックスを引く関数オブジェクトです。
367     //!
368     //! :private
369     template <class T>
370     Result
TryBindTemplate(AnimGroup * animGroup,T indexGetter)371     TryBindTemplate(AnimGroup* animGroup, T indexGetter)
372     {
373         NW_NULL_ASSERT(animGroup);
374         NW_ASSERT(std::strcmp(m_AnimData.GetTargetAnimGroupName(), animGroup->GetName()) == 0);
375         NW_ASSERT(m_AnimGroup == NULL);
376 
377         const int memberCount = animGroup->GetMemberCount();
378         bool resultResize = m_BindIndexTable.Resize(memberCount);
379         NW_ASSERTMSG(resultResize,
380             "Member count exceeded upper limit. Increase AnimEvaluator::Builder::MaxMembers.");
381 
382         const int animMemberCount = m_AnimData.GetMemberAnimSetCount();
383         bool resultReverseResize = m_ReverseBindIndexTable.Resize(animMemberCount);
384         NW_ASSERTMSG(resultReverseResize,
385             "Animation member count exceeded upper limit. Increase AnimEvaluator::Builder::MaxAnimMembers.");
386 
387         NW_ASSERTMSG(animGroup->GetFullBakedAnimEnabled() == m_AnimData.IsFullBakedAnim(),
388             "To use full baked animation, call SkeletalModel::SetFullBakedAnimEnabled(true) before Bind.");
389 
390         int boundAnimCount = 0;
391         for (int memberIdx = 0; memberIdx < memberCount; ++memberIdx)
392         {
393             // 見つからなかったときのインデックスで埋める
394             m_BindIndexTable[memberIdx] = NotFoundIndex;
395         }
396 
397         for (int animIdx = 0; animIdx < animMemberCount; ++animIdx)
398         {
399             anim::ResMemberAnim member = m_AnimData.GetMemberAnimSet(animIdx);
400             const int bindTargetIdx = indexGetter(animGroup, member);
401 
402             // 辞書引きで見つからなかったときは、モデルに対象が存在しない
403             if (bindTargetIdx == -1)
404             {
405                 m_ReverseBindIndexTable[animIdx] = NotFoundIndex;
406                 continue;
407             }
408 
409             m_BindIndexTable[bindTargetIdx] = animIdx;
410             m_ReverseBindIndexTable[animIdx] = bindTargetIdx;
411             ++boundAnimCount;
412 
413             //HACK
414             anim::ResAnimGroupMember resAnimGroupMember =
415                 animGroup->GetResAnimGroupMember(bindTargetIdx);
416             internal::ClearMaterialHash(resAnimGroupMember);
417         }
418 
419         m_AnimGroup = animGroup;
420 
421         // すべてのメンバアニメーションがバインドされました。
422         if (boundAnimCount == m_AnimData.GetMemberAnimSetCount())
423         {
424             return Result(BIND_RESULT_OK);
425         }
426 
427         // バインドされたメンバアニメーションが無かったので失敗とします。
428         if (boundAnimCount == 0)
429         {
430             return Result(BIND_RESULT_NO_MEMBER_BOUND | Result::MASK_FAIL_BIT);
431         }
432 
433         // 上のどちらでもない場合は、バインドには成功したが
434         // すべてのメンバアニメがバインドされなかったことを通知します。
435         return Result(BIND_RESULT_NOT_ALL_ANIM_MEMBER_BOUND);
436     }
437 
438     //! AnimGroupMemberのIndex -> MemberAnimのIndex
439     //!
440     //! :private
441     ut::MoveArray<int> m_BindIndexTable;
442 
443     //! MemberAnimのIndex -> AnimGroupMemberのIndex
444     //!
445     //! :private
446     ut::MoveArray<int> m_ReverseBindIndexTable;
447 
448     anim::AnimFrameController m_AnimFrameController; //!< @details :private
449     anim::ResAnim m_AnimData; //!< @details :private
450 
451     //! キャッシュの内容が古ければ true
452     //!
453     //! :private
454     bool m_IsCacheDirty;
455 
456     //! 外部から指定したキャッシュバッファなら true
457     //!
458     //! :private
459     bool m_IsCacheExternal;
460 
461     //! キャッシュを他のAnimEvaluatorと共有するなら true ( SharedAnimCache を使用します。)
462     //!
463     //! :private
464     bool m_UseSharedCache;
465 
466 private:
467     //! アニメデータに対応するメンバインデックスをそのまま引く関数オブジェクトです。
468     //!
469     //! :private
470     class BasicIndexGetterFunctor
471     {
472     public:
operator()473         int operator() (AnimGroup* animGroup, anim::ResMemberAnim member)
474         {
475             return animGroup->GetResAnimGroupMemberIndex(member.GetPath());
476         }
477     };
478 
SetResAnim(const nw::anim::ResAnim animData)479     void SetResAnim(const nw::anim::ResAnim animData)
480     {
481         m_AnimData = animData;
482         m_AnimFrameController.SetStepFrame(1.0f);
483         m_AnimFrameController.SetStartFrame(0.0f);
484         m_AnimFrameController.SetEndFrame(animData.GetFrameSize());
485         m_AnimFrameController.GetAnimFrame().ResetFrame(0.0f);
486 
487         switch (animData.GetLoopMode())
488         {
489             case nw::anim::ResAnimData::LOOP_MODE_ONETIME:
490                 m_AnimFrameController.SetPlayPolicy(nw::anim::PlayPolicy_Onetime);
491                 break;
492 
493             case nw::anim::ResAnimData::LOOP_MODE_LOOP:
494                 m_AnimFrameController.SetPlayPolicy(nw::anim::PlayPolicy_Loop);
495                 break;
496 
497             default:
498                 NW_ASSERT(false);
499         }
500 
501         m_IsCacheDirty = true;
502     }
503 };
504 
505 } // namespace gfx
506 } // namespace nw
507 
508 #endif // NW_GFX_BASEANIMEVALUATOR_H_
509