1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     gfx_TransformAnimEvaluator.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: 31311 $
16  *---------------------------------------------------------------------------*/
17 
18 #ifndef NW_GFX_TRANSFORMANIMEVALUATOR_H_
19 #define NW_GFX_TRANSFORMANIMEVALUATOR_H_
20 
21 #include <nw/gfx/gfx_BaseAnimEvaluator.h>
22 
23 namespace nw {
24 namespace gfx {
25 
26 //---------------------------------------------------------------------------
27 //! @brief トランスフォームアニメーションを評価するクラスです。
28 //!
29 //! アニメーションデータを保持し、ファンクションカーブの評価を行います。
30 //---------------------------------------------------------------------------
31 class TransformAnimEvaluator : public BaseAnimEvaluator
32 {
33 public:
34     NW_UT_RUNTIME_TYPEINFO;
35 
36     //----------------------------------------
37     //! @name 作成
38     //@{
39 
40     //! トランスフォームアニメーション評価を構築するクラスです。
41     class Builder
42     {
43     public:
44         //! コンストラクタです。
Builder()45         Builder()
46         : m_AnimData(NULL),
47           m_MaxMembers(64),
48           m_MaxAnimMembers(64),
49           m_AllocCache(false) {}
50 
51         //! アニメーションデータを設定します。
AnimData(const anim::ResAnim & animData)52         Builder& AnimData(const anim::ResAnim& animData) { m_AnimData = animData; return *this; }
53 
54         //! @brief アニメーション対象メンバの最大数を設定します。
55         //!
56         //! TransformAnimEvaluator::Bind に渡す AnimGroup の AnimGroup::GetMemberCount の値を設定してください。
57         //! 複数の AnimGroup に Bind する場合は、最大値を設定してください。
MaxMembers(int maxMembers)58         Builder& MaxMembers(int maxMembers)
59         {
60             NW_ASSERT(maxMembers > 0);
61             m_MaxMembers = maxMembers;
62             return *this;
63         }
64 
65         //! @brief 実際にアニメーションするメンバの最大数を設定します。
66         //!
67         //! AnimData() に渡す anim::res::ResAnim の anim::res::ResAnim::GetMemberAnimSetCount の値を設定してください。
68         //! TransformAnimEvaluator::ChangeAnim で複数の ResAnim を切り替える場合は、最大値を設定してください。
MaxAnimMembers(int maxAnimMembers)69         Builder& MaxAnimMembers(int maxAnimMembers)
70         {
71             NW_ASSERT(maxAnimMembers > 0);
72             m_MaxAnimMembers = maxAnimMembers;
73             return *this;
74         }
75 
76         //! キャッシュバッファを確保してキャッシュを有効にするかどうかを設定します。
AllocCache(bool allocCache)77         Builder& AllocCache(bool allocCache) { m_AllocCache = allocCache; return *this; }
78 
79         //! @brief 生成時に必要なメモリサイズを取得します。
80         //!
81         //! メモリサイズは Builder の設定によって変化します。
82         //! すべての設定が終わった後にこの関数を呼び出してください。
83         //!
84         //! @param[in] alignment 計算に用いるアライメントです。2 のべき乗である必要があります。
85         size_t GetMemorySize(size_t alignment = os::IAllocator::DEFAULT_ALIGNMENT) const
86         {
87             os::MemorySizeCalculator size(alignment);
88 
89             GetMemorySizeInternal(&size);
90 
91             return size.GetSizeWithPadding(alignment);
92         }
93 
94         //! @details :private
GetMemorySizeInternal(os::MemorySizeCalculator * pSize)95         void GetMemorySizeInternal(os::MemorySizeCalculator* pSize) const
96         {
97             os::MemorySizeCalculator& size = *pSize;
98 
99             size += sizeof(TransformAnimEvaluator);
100             BaseAnimEvaluator::GetMemorySizeForInitialize(pSize, m_MaxMembers, m_MaxAnimMembers);
101 
102             if (m_AllocCache)
103             {
104                 size += sizeof(CalculatedTransform) * m_MaxAnimMembers;
105             }
106         }
107 
108         //! @brief トランスフォームアニメーション評価を生成します。
109         //!
110         //! @param[in] allocator アロケータです。
111         //!
112         //! @return 生成されたトランスフォームアニメーション評価です。
113         //!
Create(os::IAllocator * allocator)114         TransformAnimEvaluator* Create(os::IAllocator* allocator)
115         {
116             void* buf = allocator->Alloc(sizeof(TransformAnimEvaluator));
117 
118             if (buf == NULL)
119             {
120                 return NULL;
121             }
122 
123             TransformAnimEvaluator* evaluator = new(buf) TransformAnimEvaluator(allocator);
124 
125             Result result = evaluator->Initialize(m_AnimData, m_MaxMembers, m_MaxAnimMembers, m_AllocCache);
126             NW_ASSERT(result.IsSuccess());
127 
128             return evaluator;
129         }
130 
131     private:
132         anim::ResAnim m_AnimData;
133         int m_MaxMembers;
134         int m_MaxAnimMembers;
135         bool m_AllocCache;
136     };
137 
138     //@}
139 
140     //----------------------------------------
141     //! @name 基本操作
142     //@{
143 
144     //! @brief  アニメーションを関連付けます。
145     //!
146     //!         Bind() よりも詳細なバインド結果を得ることができます。
147     //!
148     //!         アニメーションデータがあるボーンの、アニメーションカーブがないSRT要素に、
149     //!         この時点でバインドポーズをコピーします。
150     //!         そのため、この関数を呼び出した後からアニメーション評価を行うまでの間は
151     //!         スケルトンの姿勢が正しくない状態になりますのでご注意ください。
152     //!
153     //! @param[in] animGroup アニメーショングループです。
154     //!
155     //! @return バインドの結果を返します。
156     //!
157     //! @sa Bind
158     //! @sa BindResult
159     virtual Result TryBind(AnimGroup* animGroup);
160 
161     //! @brief アニメーションを変更します。
162     //!
163     //! 内部で Bind() を呼び出すため、スケルトンの姿勢が変化します。
164     //!
165     //! GetCacheBufferSizeNeeded() の値が変化しますので、
166     //! SetCacheBuffer() している場合はバッファの再設定が必要です。
167     //!
168     //! 内部で確保したキャッシュバッファは、自動的にサイズ変更されます。
169     //!
170     //! @sa Bind
171     //! @sa BaseAnimEvaluator::ChangeAnim
172     //! @param animData アニメーションデータです。
ChangeAnim(const nw::anim::ResAnim animData)173     virtual void ChangeAnim(const nw::anim::ResAnim animData)
174     {
175         // BaseAnimEvaluator::ChangeAnim()内でBind()が呼ばれ、
176         // そこでキャッシュを書き換えるので、キャッシュのResizeを先に行う
177         if (!m_IsCacheExternal && !m_CacheTransforms.Empty())
178         {
179             m_CacheTransforms.Resize(animData.GetMemberAnimSetCount());
180         }
181 
182         BaseAnimEvaluator::ChangeAnim(animData);
183     }
184 
185     //@}
186 
187     //----------------------------------------
188     //! @name 評価
189     //@{
190 
191     //! @brief メンバ単位でアニメーション結果を取得します。
192     //!
193     //! @param[out] target アニメーション結果を書き込む対象です。
194     //! @param[in] memberIdx メンバインデックスです。
195     //!
196     //! @return アニメーション結果を適用した場合は NULL でない値を返します。
197     //!
198     virtual const anim::AnimResult* GetResult(
199         void* target,
200         int memberIdx) const;
201 
202     //@}
203 
204     //----------------------------------------
205     //! @name 取得/設定
206     //@{
207 
208     //! スケールアニメを無効化しているかを取得します。
GetIsScaleDisabled()209     bool GetIsScaleDisabled() const { return m_IsScaleDisabled; }
210 
211     //! スケールアニメを無効化しているかを設定します。
SetIsScaleDisabled(bool isDisabled)212     void SetIsScaleDisabled(bool isDisabled) { m_IsScaleDisabled = isDisabled; }
213 
214     //! 回転アニメを無効化しているかを取得します。
GetIsRotateDisabled()215     bool GetIsRotateDisabled() const { return m_IsRotateDisabled; }
216 
217     //! 回転アニメを無効化しているかを設定します。
SetIsRotateDisabled(bool isDisabled)218     void SetIsRotateDisabled(bool isDisabled) { m_IsRotateDisabled = isDisabled; }
219 
220     //! 移動アニメを無効化しているかを取得します。
GetIsTranslateDisabled()221     bool GetIsTranslateDisabled() const { return m_IsTranslateDisabled; }
222 
223     //! 移動アニメを無効化しているかを設定します。
SetIsTranslateDisabled(bool isDisabled)224     void SetIsTranslateDisabled(bool isDisabled) { m_IsTranslateDisabled = isDisabled; }
225 
226     //! @brief メンバに関連付けられたアニメーションが存在するかどうかを取得します。
227     //!
228     //! @param[in] memberIdx メンバインデックスです。
229     //!
230     //! @return アニメーションが存在すれば true を返します。
231     //!
HasMemberAnim(int memberIdx)232     virtual bool HasMemberAnim(int memberIdx) const
233     {
234         NW_MINMAXLT_ASSERT(memberIdx, 0, m_BindIndexTable.Size());
235         if (m_AnimData.ptr() == NULL)
236         {
237             return (0 <= memberIdx && memberIdx < m_AnimGroup->GetMemberCount());
238         }
239         else
240         {
241             return m_BindIndexTable[memberIdx] != NotFoundIndex;
242         }
243     }
244 
245     //@}
246 
247     //----------------------------------------
248     //! @name キャッシュ
249     //@{
250 
251     //----------------------------------------------------------
252     //! :private
UpdateCacheNonVirtual()253     void UpdateCacheNonVirtual()
254     {
255         if (!m_CacheTransforms.Empty() && m_IsCacheDirty)
256         {
257             if (m_AnimData.ptr() != NULL)
258             {
259                 for (int memberIdx = 0; memberIdx < m_AnimGroup->GetMemberCount(); ++memberIdx)
260                 {
261                     const int animIdx = m_BindIndexTable[memberIdx];
262                     if (animIdx != NotFoundIndex)
263                     {
264                         GetResult(&m_CacheTransforms[animIdx], memberIdx);
265                     }
266                 }
267             }
268             m_IsCacheDirty = false;
269         }
270     }
271 
272     //! アニメーション評価結果の内部キャッシュが古ければ更新します。
UpdateCache()273     virtual void UpdateCache() { this->UpdateCacheNonVirtual(); }
274 
275     //! キャッシュバッファに必要なサイズ(バイト数)を取得します。
GetCacheBufferSizeNeeded()276     virtual int GetCacheBufferSizeNeeded() const
277     {
278         return m_AnimData.GetMemberAnimSetCount() * sizeof(CalculatedTransform);
279     }
280 
281     //! キャッシュバッファを取得します。
GetCacheBuffer()282     virtual const void* GetCacheBuffer() const { return m_CacheTransforms.Elements(); }
283 
284     //! @brief キャッシュバッファを設定します。
285     //! この関数で指定したキャッシュバッファはデストラクタで開放されません。
286     //!
287     //! @param[in] buf キャッシュバッファ用のメモリの先頭アドレスです。
288     //! NULL の場合、キャッシュが無効になります。
289     //! @param[in] size キャッシュバッファのサイズ(バイト数)です。
290     //!
SetCacheBuffer(void * buf,int size)291     virtual void SetCacheBuffer(void* buf, int size)
292     {
293         if (buf != NULL)
294         {
295             NW_ASSERT(size >= GetCacheBufferSizeNeeded());
296             const int maxCalculatedTransforms = size / sizeof(CalculatedTransform);
297             m_CacheTransforms = ut::MoveArray<CalculatedTransform>(buf, maxCalculatedTransforms);
298             m_CacheTransforms.Resize(maxCalculatedTransforms);
299             m_IsCacheDirty = true;
300             m_IsCacheExternal = true;
301         }
302         else
303         {
304             m_CacheTransforms = ut::MoveArray<CalculatedTransform>();
305         }
306     }
307 
308     //! @brief 必要があれば SRT ごとの重みを無効化します。
309     //!
310     //! @details
311     //! 引数 animObj が TransformAnimEvaluator のインスタンスの場合に、
312     //! アニメーション無効化の設定を重みに反映します。
313     //!
314     //! @param[in] weights SRT ごとの重みです。
315     //! @param[in] animObj アニメーションオブジェクトです。
316     //!
317     //! @sa GetIsScaleDisabled
318     //! @sa GetIsRotateDisabled
319     //! @sa GetIsTranslateDisabled
320     //!
321     //! :private
DisableSRTWeightsIfNeeded(float * weights,const AnimObject * animObj)322     static void DisableSRTWeightsIfNeeded(float* weights, const AnimObject* animObj)
323     {
324         const TransformAnimEvaluator* evaluator =
325             ut::DynamicCast<const TransformAnimEvaluator*>(animObj);
326         if (evaluator != NULL)
327         {
328             if (evaluator->GetIsScaleDisabled())
329             {
330                 weights[0] = TransformAnimBlendOp::WeightDiscard;
331             }
332 
333             if (evaluator->GetIsRotateDisabled())
334             {
335                 weights[1] = TransformAnimBlendOp::WeightDiscard;
336             }
337 
338             if (evaluator->GetIsTranslateDisabled())
339             {
340                 weights[2] = TransformAnimBlendOp::WeightDiscard;
341             }
342         }
343     }
344 
345     //! @brief SRT ごとの重みがすべてゼロかどうか判定します。
346     //!
347     //! @param[in] weights SRT ごとの重みです。
348     //!
349     //! @return 重みがすべてゼロなら true を返します。
350     //!
351     //! :private
CheckWeightsNearlyZero(const float * weights)352     static bool CheckWeightsNearlyZero(const float* weights)
353     {
354         NW_NULL_ASSERT(weights);
355 
356         return
357             AnimWeightNearlyEqualZero(weights[0]) &&
358             AnimWeightNearlyEqualZero(weights[1]) &&
359             AnimWeightNearlyEqualZero(weights[2]);
360     }
361 
362     //@}
363 
364 protected:
365     //----------------------------------------
366     //! @name コンストラクタ/デストラクタ
367     //@{
368 
369     //! コンストラクタです。
370     //!
371     //! :private
TransformAnimEvaluator(os::IAllocator * allocator)372     TransformAnimEvaluator(
373         os::IAllocator* allocator)
374     : BaseAnimEvaluator(allocator, ANIMTYPE_TRANSFORM_SIMPLE),
375       m_IsScaleDisabled(false),
376       m_IsRotateDisabled(false),
377       m_IsTranslateDisabled(false)
378     {
379     }
380 
381     //! デストラクタです。
382     //!
383     //! :private
~TransformAnimEvaluator()384     virtual ~TransformAnimEvaluator() {}
385 
386     //@}
387 
388     //! @details :private
Initialize(const anim::ResAnim & animData,const int maxMembers,const int maxAnimMembers,bool allocCache)389     Result Initialize(
390         const anim::ResAnim& animData,
391         const int maxMembers,
392         const int maxAnimMembers,
393         bool allocCache)
394     {
395         Result result = BaseAnimEvaluator::Initialize(animData, maxMembers, maxAnimMembers);
396         NW_ENSURE_AND_RETURN(result);
397 
398         if (allocCache)
399         {
400             void* memory = GetAllocator().Alloc(sizeof(CalculatedTransform) * maxAnimMembers);
401             if (memory == NULL)
402             {
403                 result |= Result::MASK_FAIL_BIT;
404             }
405             NW_ENSURE_AND_RETURN(result);
406 
407             m_CacheTransforms = ut::MoveArray<CalculatedTransform>(memory, maxAnimMembers, &GetAllocator());
408             m_CacheTransforms.Resize(animData.GetMemberAnimSetCount());
409         }
410 
411         return result;
412     }
413 
414     bool m_IsScaleDisabled; //!< @details :private
415     bool m_IsRotateDisabled; //!< @details :private
416     bool m_IsTranslateDisabled; //!< @details :private
417 
418     ut::MoveArray<CalculatedTransform> m_CacheTransforms; //!< @details :private
419 
420 private:
421     // アニメーションカーブが存在しないメンバを、OriginalValueで初期化します。
422     void ResetNoAnimMember(AnimGroup* animGroup, anim::ResAnim animData);
423 
424     const anim::AnimResult* GetResultFast(void* target, int memberIdx) const;
425     const anim::AnimResult* GetResultCommon(void* target, int memberIdx, bool writeNoAnimMember) const;
426 
427     // メンバアニメーションを評価します。
428     // @param[in] writeNoAnimMember trueなら、アニメーションのない要素にOriginalValueを上書きします。
429     void EvaluateMemberAnim(
430         CalculatedTransform* result,
431         anim::ResTransformAnim transformAnim,
432         float frame,
433         const math::Transform3* originalTransform,
434         bool writeNoAnimMember) const;
435 
436     // ベイク済みのメンバアニメーションを評価します。
437     // @param[in] writeNoAnimMember trueなら、アニメーションのない要素にOriginalValueを上書きします。
438     void EvaluateMemberBakedAnim(
439         CalculatedTransform* result,
440         anim::ResBakedTransformAnim transformAnim,
441         float frame,
442         const math::Transform3* originalTransform,
443         bool writeNoAnimMember) const;
444 
445     void UpdateFlagsCommon(CalculatedTransform* transform) const;
446     void UpdateFlags(CalculatedTransform* transform) const;
447 
448     // CalculatedTransformのフラグを、Bakeされた情報をもとにして更新します。
449     void ApplyBakedFlags(CalculatedTransform* transform, bit32 flags) const;
450 
451     friend class AnimBinding;
452 };
453 
454 } // namespace gfx
455 } // namespace nw
456 
457 #endif // NW_GFX_TRANSFORMANIMEVALUATOR_H_
458