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