1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     gfx_SkeletalModel.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_SKELETALMODEL_H_
19 #define NW_GFX_SKELETALMODEL_H_
20 
21 #include <nw/gfx/gfx_Model.h>
22 #include <nw/gfx/gfx_Skeleton.h>
23 #include <nw/gfx/gfx_StandardSkeleton.h>
24 #include <nw/gfx/gfx_AnimObject.h>
25 
26 namespace nw
27 {
28 namespace anim
29 {
30 namespace res
31 {
32 class ResAnimGroupMember;
33 }
34 }
35 }
36 
37 namespace nw
38 {
39 namespace gfx
40 {
41 
42 //---------------------------------------------------------------------------
43 //! @brief        内部に軽量な階層構造を持てるモデルクラスです。
44 //---------------------------------------------------------------------------
45 class SkeletalModel  : public Model
46 {
47 private:
48     NW_DISALLOW_COPY_AND_ASSIGN(SkeletalModel);
49 
50 public:
51     NW_UT_RUNTIME_TYPEINFO;
52 
53     //! @brief 設定内容です。
54     struct Description : public Model::Description
55     {
56         Skeleton* sharedSkeleton; //!< 共有するスケルトンです。
57 
58         //! @brief コンストラクタです。
DescriptionDescription59         Description()
60             : sharedSkeleton(NULL)
61         {}
62     };
63 
64     //! @brief スケルタルモデルを構築するためのクラスです。
65     //!
66     //! IsFixedSizeMemory の初期値は true です。false に変更すると、各種最大数の設定は一部を除いて無視されます。
67     class Builder
68     {
69     public:
70         //! コンストラクタです。
Builder()71         Builder() {}
72 
73         //! デストラクタです。
~Builder()74         ~Builder() {}
75 
76         //! @brief 生成時以外にもメモリを確保するかどうかのフラグを設定します。
77         //!
78         //!        true を指定すると、生成時のみ固定サイズのメモリ確保を行います。
79         //!
80         //!        false を指定すると、生成時以外にも必要に応じて動的にメモリ確保が行われます。
IsFixedSizeMemory(bool isFixedSizeMemory)81         Builder& IsFixedSizeMemory(bool isFixedSizeMemory)
82         {
83             m_Description.isFixedSizeMemory = isFixedSizeMemory;
84             return *this;
85         }
86 
87         //! 子の最大数を設定します。
MaxChildren(int maxChildren)88         Builder& MaxChildren(int maxChildren)
89         {
90             m_Description.maxChildren = maxChildren;
91             return *this;
92         }
93 
94         //! 管理できるコールバックの最大数を設定します。
MaxCallbacks(int maxCallbacks)95         Builder& MaxCallbacks(int maxCallbacks)
96         {
97             m_Description.maxCallbacks = maxCallbacks;
98             return *this;
99         }
100 
101         //! @brief バッファの生成オプションを設定します。
102         //!
103         //!
104         //! @param bufferOption Model::BufferOptionで定義されるフラグの組み合わせです。
105         //!        SharedMaterialModel が指定された場合、BufferOption は指定することができません。
106         //!        BufferOption が指定された場合は動作を停止します。
107         //!
108         //! @param[in] bufferOption Model::BufferOptionで定義されるフラグの組み合わせです。
109         //!
BufferOption(bit32 bufferOption)110         Builder& BufferOption(bit32 bufferOption)
111         {
112             m_Description.bufferOption = bufferOption;
113             return *this;
114         }
115 
116         //! @brief マテリアルを共有する場合に共有元のモデルを設定します。
117         //!
118         //!        所有権の移動を行いません。
119         //!        マテリアルを最初に生成したモデルがマテリアルの所有権を持ちます。
120         //!        共有元のモデルが先に破棄された場合は未定義の動作となります。
121         //!
122         //! @param[in] model 共有するマテリアルを持つモデルです。
123         //!
SharedMaterialModel(Model * model)124         Builder& SharedMaterialModel(Model* model)
125         {
126             m_Description.sharedMaterialModel = model;
127             return *this;
128         }
129 
130         //! @brief 共有するスケルトンを設定します。
131         //!
132         //!        作成時には所有権の移動を行いません。
133         //!        スケルトンを最初に生成したモデルがスケルトンの所有権を持ちます。
134         //!        共有元のモデルが先に破棄された場合は未定義の動作となります。
135         //!        描画前には所有権を持つ SkeletalModel は必ず SubmitView を行う必要があります。
136         //!        所有権を持つ SkeletalModel の SubmitView を行わないと、スケルトンを共有している他の SkeletalModel の更新が行われません。
137         //!
138         //! @param[in]  sharedSkeleton 共有するスケルトンです。
139         //!
140         //! @sa SwapSkeleton
SharedSkeleton(Skeleton * sharedSkeleton)141         Builder& SharedSkeleton(Skeleton* sharedSkeleton)
142         {
143             m_Description.sharedSkeleton = sharedSkeleton;
144             return *this;
145         }
146 
147         //! @brief AnimGroup ごとの AnimObject の最大数を指定します。
148         //!
149         //!        MaxAnimObjectsPerGroup の指定は IsFixedSizeMemory が false でも有効になります。
MaxAnimObjectsPerGroup(s32 maxAnimObjectCount)150         Builder& MaxAnimObjectsPerGroup(s32 maxAnimObjectCount)
151         {
152             m_Description.maxAnimObjectsPerGroup = maxAnimObjectCount;
153             return *this;
154         }
155 
156         //! @brief アニメーション可能かを設定します。
157         //!
158         //!        false を指定すると AnimBinding の生成を行いません。
159         //!        そのためアニメーションのバインドができなくなりますが、
160         //!        アニメーション更新の処理が行われなくなるため、
161         //!        シーンアップデートのパフォーマンスが向上します。
IsAnimationEnabled(bool isAnimationEnabled)162         Builder& IsAnimationEnabled(bool isAnimationEnabled)
163         {
164             m_Description.isAnimationEnabled = isAnimationEnabled;
165             return *this;
166         }
167 
168         //! @brief          スケルタルモデルを生成します。
169         //!
170         //!                 parent に NULL を指定した場合は、親ノードへの取り付けを行いません。
171         //!
172         //! @param[in]    allocator アロケータです。
173         //! @param[in]    parent 親ノードです。
174         //! @param[in]    resource リソースです。
175         //!
176         //! @return       生成したスケルタルモデルを返します。
177         //!
178         SkeletalModel* Create(
179             SceneNode* parent,
180             ResSceneObject resource,
181             os::IAllocator* allocator);
182 
183         //! @brief        生成時に必要なメモリサイズを取得します。
184         //!
185         //! @param[in]    resModel リソースです。
186         //! @param[in]    alignment 計算に用いるアライメントです。2 のべき乗である必要があります。
187         size_t GetMemorySize(
188             ResSkeletalModel resModel,
189             size_t alignment = os::IAllocator::DEFAULT_ALIGNMENT) const
190         {
191             os::MemorySizeCalculator size(alignment);
192 
193             GetMemorySizeInternal(&size, resModel);
194 
195             return size.GetSizeWithPadding(alignment);
196         }
197 
198         //! @brief        生成時に必要なデバイスメモリサイズを取得します。
199         //!
200         //! @param[in]    alignment 計算に用いるアライメントです。2 のべき乗である必要があります。
201         size_t GetDeviceMemorySize(
202             ResSkeletalModel,
203             size_t alignment = os::IAllocator::DEFAULT_ALIGNMENT) const
204         {
205             NW_UNUSED_VARIABLE(alignment);
206 
207             return 0;
208         }
209 
210         //! @details :private
GetMemorySizeInternal(os::MemorySizeCalculator * pSize,ResSkeletalModel resModel)211         void GetMemorySizeInternal(
212             os::MemorySizeCalculator* pSize,
213             ResSkeletalModel resModel) const
214         {
215             os::MemorySizeCalculator& size = *pSize;
216 
217             if (m_Description.sharedSkeleton == NULL)
218             {
219                 ResSkeleton resSkeleton = resModel.GetSkeleton();
220 
221 #ifdef NW_MOVE_ARRAY_CACHE_LINE_ALIGNMENT_ENABLED
222                 size.Add(
223                     sizeof(Skeleton::TransformPose::Transform) * resSkeleton.GetBonesCount(),
224                     Skeleton::TransformPose::TransformArray::MEMORY_ALIGNMENT);
225 #else
226                 size += sizeof(Skeleton::TransformPose::Transform) * resSkeleton.GetBonesCount();
227 #endif
228                 StandardSkeleton::GetMemorySizeInternal(
229                     pSize,
230                     resSkeleton,
231                     m_Description.maxCallbacks);
232             }
233 
234             // モデル作成
235             size += sizeof(SkeletalModel);
236             SkeletalModel::GetMemorySizeForInitialize(pSize, resModel, m_Description);
237         }
238 
239         //! @details :private
GetDeviceMemorySizeInternal(os::MemorySizeCalculator *,ResSkeletalModel)240         void GetDeviceMemorySizeInternal(
241             os::MemorySizeCalculator*,
242             ResSkeletalModel) const
243         {
244         }
245 
246     private:
247         SkeletalModel::Description m_Description;
248     };
249 
250     //----------------------------------------
251     //! @name シーンツリー
252     //@{
253 
254     //! @brief        ビジターを受け付けます。
255     //!
256     //! @param[in]    visitor ビジターです。
257     //!
258     virtual void Accept(ISceneVisitor* visitor);
259 
260     //@}
261 
262     //----------------------------------------
263     //! @name リソース
264     //@{
265 
266     //! スケルタルモデルのリソースを取得します。
GetResSkeletalModel()267     ResSkeletalModel GetResSkeletalModel()
268     {
269         return ResDynamicCast<ResSkeletalModel>(this->GetResSceneObject());
270     }
271 
272     //! スケルタルモデルのリソースを取得します。
GetResSkeletalModel()273     const ResSkeletalModel GetResSkeletalModel() const
274     {
275         return ResDynamicCast<ResSkeletalModel>(this->GetResSceneObject());
276     }
277 
278     //@}
279 
280     //----------------------------------------
281     //! @name スケルトン
282     //@{
283 
284     //! スケルトンを取得します。
GetSkeleton()285     Skeleton* GetSkeleton() { return m_Skeleton.Get(); }
286 
287     //! スケルトンを取得します。
GetSkeleton()288     const Skeleton* GetSkeleton() const { return m_Skeleton.Get(); }
289 
290     //! 他モデルのスケルトンを共有している場合は true を返します。
IsSharingSkeleton()291     bool IsSharingSkeleton() const { return this->m_SharingSkeleton; }
292 
293     //! @brief スケルトンの所有権を交換します。
294     //!
295     //! 保有するスケルトンの所有権を交換します。
296     //! 共有スケルトンのオーナーとなる SkeletalModel を変更する場合などに用いることができます。
297     //!
298     //! @param[in] skeletalModel スケルトンを交換するスケルタルモデルです。
299     //!
SwapSkeleton(SkeletalModel * skeletalModel)300     void SwapSkeleton( SkeletalModel* skeletalModel )
301     {
302         this->m_Skeleton.Swap(skeletalModel->m_Skeleton);
303         bool sharingSkeleton = this->m_SharingSkeleton;
304         this->m_SharingSkeleton = skeletalModel->m_SharingSkeleton;
305         skeletalModel->m_SharingSkeleton = sharingSkeleton;
306     }
307 
308     //@}
309 
310     //----------------------------------------
311     //! @name アニメーション
312     //@{
313 
314     //! スケルタルアニメーショングループを取得します。
GetSkeletalAnimGroup()315     AnimGroup* GetSkeletalAnimGroup() { return m_SkeletalAnimGroup; }
316 
317     //! スケルタルアニメーショングループを取得します。
GetSkeletalAnimGroup()318     const AnimGroup* GetSkeletalAnimGroup() const { return m_SkeletalAnimGroup; }
319 
320     //! スケルタルアニメーショングループのアニメーションバインディング中のインデックスを取得します。
GetSkeletalAnimBindingIndex()321     int GetSkeletalAnimBindingIndex() const { return m_SkeletalAnimBindingIndex; }
322 
323     //! スケルタルアニメーションオブジェクトを取得します。
324     const AnimObject* GetSkeletalAnimObject(int objectIndex = 0) const
325     {
326         NW_NULL_ASSERT(m_AnimBinding);
327         return m_AnimBinding->GetAnimObject(m_SkeletalAnimBindingIndex, objectIndex);
328     }
329 
330     //! スケルタルアニメーションオブジェクトを取得します。
331     AnimObject* GetSkeletalAnimObject(int objectIndex = 0)
332     {
333         NW_NULL_ASSERT(m_AnimBinding);
334         return m_AnimBinding->GetAnimObject(m_SkeletalAnimBindingIndex, objectIndex);
335     }
336 
337     //! @brief スケルタルアニメーションオブジェクトを設定します。
338     //!
339     //! @param[in] animObject 設定するアニメーションオブジェクトです。NULL を指定するとアニメーションを解除します。
340     void SetSkeletalAnimObject(AnimObject* animObject, int objectIndex = 0)
341     {
342         NW_NULL_ASSERT(m_AnimBinding);
343         m_AnimBinding->SetAnimObject(m_SkeletalAnimBindingIndex, animObject, objectIndex);
344     }
345 
346     //! フルベイクアニメーションを使用するかを取得します。
GetFullBakedAnimEnabled()347     bool GetFullBakedAnimEnabled() const
348     {
349         return m_FullBakedAnimEnabled;
350     }
351 
352     //! @brief フルベイクアニメーションを使用するかを設定します。
353     //!
354     //! true を指定すると、スケルトンの更新処理が省略されます。
355     //! また、 ResSkeletonData::FLAG_MODEL_COORDINATE フラグが有効に設定されます。
356     //! true を指定した状態では、通常のアニメーションは正常に再生できませんので注意してください。
357     //!
358     //! false を指定すると、 ResSkeletonData::FLAG_MODEL_COORDINATE フラグが無効に設定されます。
359     //!
360     //! 共有スケルトンを使用している場合は、
361     //! 共有元のスケルトンの上記フラグが変更されますので注意してください。
362     void SetFullBakedAnimEnabled(bool enable);
363 
364     //@}
365 
366 protected:
367     virtual Result Initialize(os::IAllocator* allocator);
368 
369     //! @brief   Initialize() の実行に必要なメモリサイズを取得します。
370     //!
371     //! @details :private
GetMemorySizeForInitialize(os::MemorySizeCalculator * pSize,ResSkeletalModel resModel,Description description)372     static void GetMemorySizeForInitialize(
373         os::MemorySizeCalculator* pSize,
374         ResSkeletalModel resModel,
375         Description description)
376     {
377         // SkeletalModel::Initialize
378         os::MemorySizeCalculator& size = *pSize;
379 
380         Model::GetMemorySizeForInitialize(pSize, resModel, description);
381 
382         // SkeletalModel::CreateSkeletalAnimGroup
383         if (description.isAnimationEnabled)
384         {
385             const int animGroupCount = resModel.GetAnimGroupsCount();
386             for (int animGroupIdx = 0; animGroupIdx < animGroupCount; ++animGroupIdx)
387             {
388                 anim::ResAnimGroup resAnimGroup = resModel.GetAnimGroups(animGroupIdx);
389                 const int targetType = resAnimGroup.GetTargetType();
390                 const bool transformFlag =
391                     (resAnimGroup.GetFlags() & anim::ResAnimGroup::FLAG_IS_CALCULATED_TRANSFORM) != 0;
392                 if (transformFlag &&
393                     targetType == anim::ResGraphicsAnimGroup::TARGET_TYPE_BONE)
394                 {
395                     AnimGroup::Builder()
396                         .ResAnimGroup(resAnimGroup)
397                         .UseOriginalValue(true)
398                         .GetMemorySizeInternal(pSize);
399                     break; // 他にスケルタルモデル専用のアニメーショングループがないので
400                 }
401             }
402         }
403     }
404 
405     //----------------------------------------
406     //! @name コンストラクタ/デストラクタ
407     //@{
408 
409     //! コンストラクタです。
SkeletalModel(os::IAllocator * allocator,ResSkeletalModel resource,GfxPtr<Skeleton> & skeleton,bool isSharingSkeleton,const SkeletalModel::Description & description)410     SkeletalModel(
411         os::IAllocator* allocator,
412         ResSkeletalModel resource,
413         GfxPtr<Skeleton>& skeleton,
414         bool isSharingSkeleton,
415         const SkeletalModel::Description& description)
416     : Model(
417         allocator,
418         resource,
419         description),
420       m_Skeleton(skeleton),
421       m_SkeletalAnimGroup(NULL),
422       m_SkeletalAnimBindingIndex(-1),
423       m_SharingSkeleton(isSharingSkeleton),
424       m_FullBakedAnimEnabled(false)
425     {
426         if (!isSharingSkeleton)
427         {
428             m_Skeleton->SetOwnerSkeletalModel(this);
429         }
430     }
431 
432     //! デストラクタです。
~SkeletalModel()433     virtual ~SkeletalModel()
434     {
435         SafeDestroy(m_SkeletalAnimGroup);
436     }
437 
438     //@}
439 
440     //! スケルタルアニメーション用のアニメーショングループを生成します。
441     Result CreateSkeletalAnimGroup(os::IAllocator* allocator);
442 
443     //! スケルタルアニメーションに登録するモデルデータのポインタを取得します。
444     void* GetAnimTargetObject(const anim::ResAnimGroupMember& anim);
445 
446 private:
447     void SetupAnimGroup(AnimGroup* animGroup, bool fullBakedAnimEnabled) const;
448 
449     GfxPtr<Skeleton> m_Skeleton;
450     AnimGroup* m_SkeletalAnimGroup;
451     int m_SkeletalAnimBindingIndex;
452     bool m_SharingSkeleton;
453     bool m_FullBakedAnimEnabled;
454 };
455 
456 } // namespace gfx
457 } // namespace nw
458 
459 #endif // NW_GFX_SKELETALMODEL_H_
460