1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     gfx_AnimEvaluator.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: 31563 $
16  *---------------------------------------------------------------------------*/
17 
18 #ifndef NW_GFX_ANIMEVALUATOR_H_
19 #define NW_GFX_ANIMEVALUATOR_H_
20 
21 #include <nw/gfx/gfx_BaseAnimEvaluator.h>
22 #include <nw/gfx/gfx_SharedAnimCache.h>
23 #include <nw/ut/ut_MoveArray.h>
24 #include <nw/ut/ut_RuntimeTypeInfo.h>
25 
26 namespace nw {
27 namespace gfx {
28 
29 //---------------------------------------------------------------------------
30 //! @brief 汎用アニメーションを評価するクラスです。
31 //!
32 //! アニメーションデータを保持し、ファンクションカーブの評価を行います。
33 //---------------------------------------------------------------------------
34 class AnimEvaluator : public BaseAnimEvaluator
35 {
36 public:
37     NW_UT_RUNTIME_TYPEINFO;
38 
39     //----------------------------------------
40     //! @name 作成
41     //@{
42 
43     //! 汎用アニメーション評価を構築するクラスです。
44     class Builder
45     {
46     public:
47         //! コンストラクタです。
Builder()48         Builder()
49         : m_AnimData(NULL),
50           m_MaxMembers(64),
51           m_MaxAnimMembers(64),
52           m_AllocCache(false) {}
53 
54         //! アニメーションデータを設定します。
AnimData(const nw::anim::ResAnim & animData)55         Builder& AnimData(const nw::anim::ResAnim& animData) { m_AnimData = animData; return *this; }
56 
57         //! @brief アニメーション対象メンバの最大数を設定します。
58         //!
59         //! AnimEvaluator::Bind に渡す AnimGroup の AnimGroup::GetMemberCount の値を設定してください。
60         //! 複数の AnimGroup に Bind する場合は、最大値を設定してください。
MaxMembers(int maxMembers)61         Builder& MaxMembers(int maxMembers)
62         {
63             NW_ASSERT(maxMembers > 0);
64             m_MaxMembers = maxMembers;
65             return *this;
66         }
67 
68         //! @brief 実際にアニメーションするメンバの最大数を設定します。
69         //!
70         //! AnimData() に渡す anim::res::ResAnim の anim::res::ResAnim::GetMemberAnimSetCount の値を設定してください。
71         //! AnimEvaluator::ChangeAnim で複数の ResAnim を切り替える場合は、最大値を設定してください。
MaxAnimMembers(int maxAnimMembers)72         Builder& MaxAnimMembers(int maxAnimMembers)
73         {
74             NW_ASSERT(maxAnimMembers > 0);
75             m_MaxAnimMembers = maxAnimMembers;
76             return *this;
77         }
78 
79         //! キャッシュバッファを確保してキャッシュを有効にするかどうかを設定します。
AllocCache(bool allocCache)80         Builder& AllocCache(bool allocCache) { m_AllocCache = allocCache; return *this; }
81 
82         //! @brief 生成時に必要なメモリサイズを取得します。
83         //!
84         //! メモリサイズは Builder の設定によって変化します。
85         //! すべての設定が終わった後にこの関数を呼び出してください。
86         //!
87         //! @param[in] alignment 計算に用いるアライメントです。2 のべき乗である必要があります。
88         size_t GetMemorySize(size_t alignment = os::IAllocator::DEFAULT_ALIGNMENT) const
89         {
90             os::MemorySizeCalculator size(alignment);
91 
92             GetMemorySizeInternal(&size);
93 
94             return size.GetSizeWithPadding(alignment);
95         }
96 
97         //! @details :private
GetMemorySizeInternal(os::MemorySizeCalculator * pSize)98         void GetMemorySizeInternal(os::MemorySizeCalculator* pSize) const
99         {
100             os::MemorySizeCalculator& size = *pSize;
101 
102             size += sizeof(AnimEvaluator);
103             AnimEvaluator::GetMemorySizeForInitialize(pSize, m_AnimData, m_MaxMembers, m_MaxAnimMembers, m_AllocCache);
104         }
105 
106         //! @brief 汎用アニメーション評価を生成します。
107         //!
108         //! @param[in] allocator アロケータです。
109         //!
110         //! @return 生成された汎用アニメーション評価です。
111         //!
Create(os::IAllocator * allocator)112         AnimEvaluator* Create(os::IAllocator* allocator)
113         {
114             void* buf = allocator->Alloc(sizeof(AnimEvaluator));
115 
116             if (buf == NULL)
117             {
118                 return NULL;
119             }
120 
121             AnimEvaluator* animEvaluator = new(buf) AnimEvaluator(allocator);
122 
123             Result result = animEvaluator->Initialize(m_AnimData, m_MaxMembers, m_MaxAnimMembers, m_AllocCache);
124             NW_ASSERT(result.IsSuccess());
125 
126             return animEvaluator;
127         }
128 
129     private:
130         nw::anim::ResAnim m_AnimData;
131         int m_MaxMembers;
132         int m_MaxAnimMembers;
133         bool m_AllocCache;
134     };
135 
136     //@}
137 
138     //----------------------------------------
139     //! @name 基本操作
140     //@{
141 
142     //! @brief アニメーションを変更します。
143     //!
144     //! GetCacheBufferSizeNeeded() の値が変化しますので、
145     //! SetCacheBuffer() や SetSharedCacheBuffer() を使用している場合はバッファの再設定が必要です。
146     //!
147     //! 内部で確保したキャッシュバッファは、自動的にサイズ変更されます。
148     //!
149     //! @param animData アニメーションデータです。
150     //! @sa BaseAnimEvaluator::ChangeAnim
ChangeAnim(const nw::anim::ResAnim animData)151     virtual void ChangeAnim(const nw::anim::ResAnim animData)
152     {
153         // キャッシュサイズを求めるのにm_AnimDataを参照するので、
154         // 先に更新する必要がある
155         BaseAnimEvaluator::ChangeAnim(animData);
156 
157         m_CachePtrs.Resize(animData.GetMemberAnimSetCount());
158 
159         if (!m_IsCacheExternal && m_CacheBuf != NULL)
160         {
161             os::SafeFree(m_CacheBuf, &GetAllocator());
162 
163             // サイズ0でのAllocを回避する
164             // CreateEmpty~Anim()などを使用した場合に起こりうる
165             if (animData.GetMemberAnimSetCount() != 0)
166             {
167                 m_CacheBuf = GetAllocator().Alloc(GetCacheBufferSizeNeeded());
168                 NW_NULL_ASSERT(m_CacheBuf);
169 
170                 SetCacheBufferPointers();
171             }
172         }
173     }
174 
175     //! @brief マテリアル名を置き換えてアニメーションを関連付けます。
176     //!
177     //! マテリアルアニメーションのバインド先を
178     //! 名前が materialName であるマテリアルに置き換えてバインドします。
179     //!
180     //! マテリアルアニメーション以外では使用できません。
181     //!
182     //! materialName の文字数が長すぎるとバインドに失敗することがあります。
183     //!
184     //! @param[in] animGroup アニメーショングループです。
185     //! @param[in] materialName バインドするマテリアル名です。文字数の制限があります。
186     //!
187     //! @return バインドの結果を返します。
188     //!
189     //! @sa TryBind
190     Result ForceBindMaterialAnim(AnimGroup* animGroup, const char* materialName);
191 
192     //@}
193 
194     //----------------------------------------
195     //! @name 評価
196     //@{
197 
198     //! @brief メンバ単位でアニメーション結果を取得します。
199     //!
200     //! @param[out] target アニメーション結果を書き込む対象です。
201     //! @param[in] memberIdx メンバインデックスです。
202     //!
203     //! @return アニメーション結果を返します。
204     //! ブレンドオペレーションを使用する場合は、返り値のアニメーション結果を使用してください。
205     //!
206     virtual const anim::AnimResult* GetResult(
207         void* target,
208         int memberIdx) const;
209 
210     //@}
211 
212     //----------------------------------------
213     //! @name 取得/設定
214     //@{
215 
216     //! @brief メンバに関連付けられたアニメーションが存在するかどうかを取得します。
217     //!
218     //! @param[in] memberIdx メンバインデックスです。
219     //!
220     //! @return アニメーションが存在すれば true を返します。
221     //!
HasMemberAnim(int memberIdx)222     virtual bool HasMemberAnim(int memberIdx) const
223     {
224         NW_MINMAXLT_ASSERT(memberIdx, 0, m_BindIndexTable.Size());
225         return m_BindIndexTable[memberIdx] != NotFoundIndex;
226     }
227 
228     //@}
229 
230     //----------------------------------------
231     //! @name キャッシュ
232     //@{
233 
234     //! アニメーション評価結果の内部キャッシュが古ければ更新します。
UpdateCache()235     virtual void UpdateCache() { this->UpdateCacheNonVirtual(); }
236 
237     //! :private
238     void UpdateCacheNonVirtual();
239 
240     //! :private
241     void UpdateCacheImpl();
242 
243     //! キャッシュバッファに必要なサイズ(バイト数)を取得します。
244     //! @return 必要なサイズ(バイト数)です。
245     virtual int GetCacheBufferSizeNeeded() const;
246 
247     //! キャッシュバッファを取得します。
GetCacheBuffer()248     virtual const void* GetCacheBuffer() const { return m_CacheBuf; }
249 
250     //! @brief キャッシュバッファを設定します。
251     //! この関数で指定したキャッシュバッファはデストラクタで解放されません。
252     //!
253     //! @param[in] buf キャッシュバッファ用のメモリの先頭アドレスです。
254     //! NULL の場合、キャッシュが無効になります。
255     //! @param[in] size キャッシュバッファのサイズ(バイト数)です。
256     //!
SetCacheBuffer(void * buf,int size)257     virtual void SetCacheBuffer(void* buf, int size)
258     {
259         m_CacheBuf = buf;
260         if (buf != NULL)
261         {
262             NW_ASSERT(size >= GetCacheBufferSizeNeeded());
263             (void)size;
264             m_IsCacheDirty = true;
265             m_IsCacheExternal = true;
266             SetCacheBufferPointers();
267         }
268     }
269 
270     //! @brief 他のAnimEvaluatorと共有可能なキャッシュを使用するように設定します。
SetSharedCacheBuffer(SharedAnimCache * cache)271     void SetSharedCacheBuffer(SharedAnimCache* cache)
272     {
273         // 他の形式のキャッシュが指定されているならアサートで止める
274         NW_ASSERT(m_CacheBuf == NULL);
275         NW_ASSERT(!m_IsCacheExternal);
276 
277         m_SharedCache = cache;
278         if (cache != NULL)
279         {
280             m_IsCacheDirty = true;
281             m_IsCacheExternal = true;
282             m_UseSharedCache = true;
283             m_CacheBuf = cache->GetCacheBuffer();
284             SetCacheBufferPointers();
285         }
286         else
287         {
288             m_CacheBuf = NULL;
289         }
290     }
291 
292     //@}
293 
294 protected:
295     //----------------------------------------
296     //! @name コンストラクタ/デストラクタ
297     //@{
298 
299     //! コンストラクタです。
300     //!
301     //! :private
302     AnimEvaluator(
303         os::IAllocator* allocator);
304 
305     //! デストラクタです。
306     //!
307     //! :private
~AnimEvaluator()308     virtual ~AnimEvaluator()
309     {
310         if (!m_IsCacheExternal && m_CacheBuf != NULL)
311         {
312             GetAllocator().Free(m_CacheBuf);
313         }
314     }
315 
316     //@}
317 
318     //! Initialize() の実行に必要なメモリサイズを取得します。
319     //!
320     //! :private
321     static void GetMemorySizeForInitialize(
322         os::MemorySizeCalculator* pSize,
323         const nw::anim::ResAnim& animData,
324         const int maxMembers,
325         const int maxAnimMembers,
326         bool allocCache);
327 
328     //! @details :private
329     Result Initialize(
330         const nw::anim::ResAnim& animData,
331         const int maxMembers,
332         const int maxAnimMembers,
333         bool allocCache);
334 
335     //! キャッシュバッファに必要なサイズ(バイト数)を取得します。
336     //!
337     //! :private
338     static int GetCacheBufferSizeNeeded(const anim::ResAnim& animData);
339 
340     //! 各メンバアニメのキャッシュバッファへのポインタを設定します。
341     //!
342     //! :private
343     void SetCacheBufferPointers();
344 
345     //! AnimResult を実サイズで詰めたバッファ
346     //!
347     //! :private
348     void* m_CacheBuf;
349 
350     //! 共有するキャッシュのポインタ
351     SharedAnimCache* m_SharedCache;
352 
353     //! 各メンバアニメの m_CacheBuf 中のポインタ
354     //!
355     //! :private
356     ut::MoveArray<anim::AnimResult*> m_CachePtrs;
357 
358 private:
359     //! マテリアル名を置き換えてメンバインデックスを引く関数オブジェクトです。
360     //!
361     //! :private
362     class ReplaceMaterialNameIndexGetterFunctor
363     {
364     private:
365         static const int MAX_BUFFER_LENGTH = 256;
366 
367     public:
ReplaceMaterialNameIndexGetterFunctor(const char * materialName)368         ReplaceMaterialNameIndexGetterFunctor(const char* materialName)
369             : m_MaterialName(materialName),
370               m_LenMaterialName(0)
371         {
372             NW_NULL_ASSERT(materialName);
373             m_LenMaterialName = std::strlen(m_MaterialName);
374         }
375 
operator()376         int operator() (AnimGroup* animGroup, anim::ResMemberAnim member)
377         {
378             const char MATERIAL_PREFIX[] = "Materials[\"";
379             const size_t MATERIAL_PREFIX_LEN = sizeof(MATERIAL_PREFIX) - 1;
380 
381             const char* path = member.GetPath();
382             size_t lenPath = std::strlen(path);
383 
384             NW_ASSERTMSG(std::strncmp(path, MATERIAL_PREFIX, MATERIAL_PREFIX_LEN) == 0, "Not material animation member.");
385 
386             NW_FAILSAFE_IF (lenPath <= MATERIAL_PREFIX_LEN)
387             {
388                 // 書き換え対象のパスの長さが異常なので
389                 // リソースが壊れている可能性がある。
390 
391                 // -1 は辞書引きで見つからない場合の戻り値
392                 return -1;
393             }
394 
395             // "Materials[\"" は確定しているので、その次の文字列までポインタを進める。
396             path += MATERIAL_PREFIX_LEN;
397             lenPath -=  MATERIAL_PREFIX_LEN;
398 
399             // 次の " を探す。
400             while ((*path != 0) && (*path != '\"'))
401             {
402                 ++path;
403                 --lenPath;
404             }
405 
406             // この時点で path は '\"' からはじまるか、NULL 終端を指している。
407 
408             NW_FAILSAFE_IF (MATERIAL_PREFIX_LEN + m_LenMaterialName + lenPath + 1 > MAX_BUFFER_LENGTH)
409             {
410                 // 書き換えたパスがバッファに収まらない。
411 
412                 // -1 は辞書引きで見つからない場合の戻り値
413                 return -1;
414             }
415 
416             // パスを作成する。
417             char replacedPath[MAX_BUFFER_LENGTH];
418             ut::strncpy(replacedPath, MAX_BUFFER_LENGTH, MATERIAL_PREFIX, MATERIAL_PREFIX_LEN);
419             ut::strncat(replacedPath, MAX_BUFFER_LENGTH, m_MaterialName, m_LenMaterialName);
420             ut::strncat(replacedPath, MAX_BUFFER_LENGTH, path, lenPath);
421 
422             return animGroup->GetResAnimGroupMemberIndex(replacedPath);
423         }
424 
425     private:
426         const char* m_MaterialName;
427         size_t m_LenMaterialName;
428     };
429 };
430 
431 } // namespace gfx
432 } // namespace nw
433 
434 #endif // NW_GFX_ANIMEVALUATOR_H_
435