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: 25986 $
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         //!        現在の実装では0か1を指定する必要があります。
102         //!
NW_DEPRECATED_FUNCTION(Builder & MaterialBufferCount (int bufferCount))103         NW_DEPRECATED_FUNCTION(Builder& MaterialBufferCount(int bufferCount))
104         {
105             NW_UNUSED_VARIABLE(bufferCount);
106             return *this;
107         }
108 
109         //! @brief バッファの生成オプションを設定します。
110         //!
111         //!
112         //! @param bufferOption Model::BufferOptionで定義されるフラグの組み合わせです。
113         //!        SharedMaterialModel が指定された場合、BufferOption は指定することができません。
114         //!        BufferOption が指定された場合は動作を停止します。
115         //!
116         //! @param[in] bufferOption Model::BufferOptionで定義されるフラグの組み合わせです。
117         //!
BufferOption(bit32 bufferOption)118         Builder& BufferOption(bit32 bufferOption)
119         {
120             m_Description.bufferOption = bufferOption;
121             return *this;
122         }
123 
124         //! @brief マテリアルを共有する場合に共有元のモデルを設定します。
125         //!
126         //!        所有権の移動を行いません。
127         //!        マテリアルを最初に生成したモデルがマテリアルの所有権を持ちます。
128         //!        共有元のモデルが先に破棄された場合は未定義の動作となります。
129         //!
130         //! @param[in] model 共有するマテリアルを持つモデルです。
131         //!
SharedMaterialModel(Model * model)132         Builder& SharedMaterialModel(Model* model)
133         {
134             m_Description.sharedMaterialModel = model;
135             return *this;
136         }
137 
138         //! @brief 共有するスケルトンを設定します。
139         //!
140         //!        所有権の移動を行いません。
141         //!        スケルトンを最初に生成したモデルがスケルトンの所有権を持ちます。
142         //!        共有元のモデルが先に破棄された場合は未定義の動作となります。
143         //!
144         //! @param[in]  sharedSkeleton 共有するスケルトンです。
SharedSkeleton(Skeleton * sharedSkeleton)145         Builder& SharedSkeleton(Skeleton* sharedSkeleton)
146         {
147             m_Description.sharedSkeleton = sharedSkeleton;
148             return *this;
149         }
150 
151         //! @brief AnimGroup ごとの AnimObject の最大数を指定します。
152         //!
153         //!        IsFixedSizeMemory に false を指定しても、この最大数は有効です。
MaxAnimObjectsPerGroup(s32 maxAnimObjectCount)154         Builder& MaxAnimObjectsPerGroup(s32 maxAnimObjectCount)
155         {
156             m_Description.maxAnimObjectsPerGroup = maxAnimObjectCount;
157             return *this;
158         }
159 
160         //! @brief アニメーション可能かを設定します。
161         //!
162         //!        false を指定すると AnimBinding の生成を行いません。
163         //!        そのためアニメーションのバインドができなくなりますが、
164         //!        アニメーション更新の処理が行われなくなるため、
165         //!        シーンアップデートのパフォーマンスが向上します。
IsAnimationEnabled(bool isAnimationEnabled)166         Builder& IsAnimationEnabled(bool isAnimationEnabled)
167         {
168             m_Description.isAnimationEnabled = isAnimationEnabled;
169             return *this;
170         }
171 
172         //! @brief          スケルタルモデルを生成します。
173         //!
174         //!                 parent に NULL を指定した場合は、親ノードへの取り付けを行いません。
175         //!
176         //! @param[in]    allocator アロケータです。
177         //! @param[in]    parent 親ノードです。
178         //! @param[in]    resource リソースです。
179         //!
180         //! @return       生成したスケルタルモデルを返します。
181         //!
182         SkeletalModel* Create(
183             SceneNode* parent,
184             ResSceneObject resource,
185             os::IAllocator* allocator);
186 
187         //! @brief        生成時に必要なメモリサイズを取得します。
188         //!
189         //! @param[in]    resource リソースです。
190         size_t GetMemorySize(
191             ResSkeletalModel resModel,
192             size_t alignment = os::IAllocator::DEFAULT_ALIGNMENT) const
193         {
194             os::MemorySizeCalculator size(alignment);
195 
196             GetMemorySizeInternal(&size, resModel);
197 
198             return size.GetSizeWithPadding(alignment);
199         }
200 
201         //! @details :private
GetMemorySizeInternal(os::MemorySizeCalculator * pSize,ResSkeletalModel resModel)202         void GetMemorySizeInternal(
203             os::MemorySizeCalculator* pSize,
204             ResSkeletalModel resModel) const
205         {
206             os::MemorySizeCalculator& size = *pSize;
207 
208             if (m_Description.sharedSkeleton == NULL)
209             {
210                 ResSkeleton resSkeleton = resModel.GetSkeleton();
211 
212 #ifdef NW_MOVE_ARRAY_CACHE_LINE_ALIGNMENT_ENABLED
213                 size.Add(
214                     sizeof(Skeleton::TransformPose::Transform) * resSkeleton.GetBonesCount(),
215                     Skeleton::TransformPose::TransformArray::MEMORY_ALIGNMENT);
216 #else
217                 size += sizeof(Skeleton::TransformPose::Transform) * resSkeleton.GetBonesCount();
218 #endif
219                 StandardSkeleton::GetMemorySizeInternal(
220                     pSize,
221                     resSkeleton,
222                     m_Description.maxCallbacks);
223             }
224 
225             // モデル作成
226             size += sizeof(SkeletalModel);
227             SkeletalModel::GetMemorySizeForInitialize(pSize, resModel, m_Description);
228         }
229 
230     private:
231         SkeletalModel::Description m_Description;
232     };
233 
234     //----------------------------------------
235     //! @name シーンツリー
236     //@{
237 
238     //! @brief        ビジターを受け付けます。
239     //!
240     //! @param[in]    visitor ビジターです。
241     //!
242     virtual void Accept(ISceneVisitor* visitor);
243 
244     //@}
245 
246     //----------------------------------------
247     //! @name リソース
248     //@{
249 
250     //! スケルタルモデルのリソースを取得します。
GetResSkeletalModel()251     ResSkeletalModel GetResSkeletalModel()
252     {
253         return ResDynamicCast<ResSkeletalModel>(this->GetResSceneObject());
254     }
255 
256     //! スケルタルモデルのリソースを取得します。
GetResSkeletalModel()257     const ResSkeletalModel GetResSkeletalModel() const
258     {
259         return ResDynamicCast<ResSkeletalModel>(this->GetResSceneObject());
260     }
261 
262     //@}
263 
264     //----------------------------------------
265     //! @name スケルトン
266     //@{
267 
268     //! スケルトンを取得します。
GetSkeleton()269     Skeleton* GetSkeleton() { return m_Skeleton.Get(); }
270 
271     //! スケルトンを取得します。
GetSkeleton()272     const Skeleton* GetSkeleton() const { return m_Skeleton.Get(); }
273 
274     //! 他モデルのスケルトンを共有している場合は true を返します。
IsSharingSkeleton()275     bool IsSharingSkeleton() const { return this->m_SharingSkeleton; }
276 
277     //@}
278 
279     //----------------------------------------
280     //! @name アニメーション
281     //@{
282 
283     //! スケルタルアニメーショングループを取得します。
GetSkeletalAnimGroup()284     AnimGroup* GetSkeletalAnimGroup() { return m_SkeletalAnimGroup; }
285 
286     //! スケルタルアニメーショングループを取得します。
GetSkeletalAnimGroup()287     const AnimGroup* GetSkeletalAnimGroup() const { return m_SkeletalAnimGroup; }
288 
289     //! スケルタルアニメーショングループのアニメーションバインディング中のインデックスを取得します。
GetSkeletalAnimBindingIndex()290     int GetSkeletalAnimBindingIndex() const { return m_SkeletalAnimBindingIndex; }
291 
292     //! スケルタルアニメーションオブジェクトを取得します。
293     const AnimObject* GetSkeletalAnimObject(int objectIndex = 0) const
294     {
295         NW_NULL_ASSERT(m_AnimBinding);
296         return m_AnimBinding->GetAnimObject(m_SkeletalAnimBindingIndex, objectIndex);
297     }
298 
299     //! スケルタルアニメーションオブジェクトを取得します。
300     AnimObject* GetSkeletalAnimObject(int objectIndex = 0)
301     {
302         NW_NULL_ASSERT(m_AnimBinding);
303         return m_AnimBinding->GetAnimObject(m_SkeletalAnimBindingIndex, objectIndex);
304     }
305 
306     //! スケルタルアニメーションオブジェクトを設定します。
307     void SetSkeletalAnimObject(AnimObject* animObject, int objectIndex = 0)
308     {
309         NW_NULL_ASSERT(m_AnimBinding);
310         m_AnimBinding->SetAnimObject(m_SkeletalAnimBindingIndex, animObject, objectIndex);
311     }
312 
313     //@}
314 
315 protected:
316     virtual Result Initialize(os::IAllocator* allocator);
317 
318     //! @brief   Initialize() の実行に必要なメモリサイズを取得します。
319     //!
320     //! @details :private
GetMemorySizeForInitialize(os::MemorySizeCalculator * pSize,ResSkeletalModel resModel,Description description)321     static void GetMemorySizeForInitialize(
322         os::MemorySizeCalculator* pSize,
323         ResSkeletalModel resModel,
324         Description description)
325     {
326         // SkeletalModel::Initialize
327         os::MemorySizeCalculator& size = *pSize;
328 
329         Model::GetMemorySizeForInitialize(pSize, resModel, description);
330 
331         // SkeletalModel::CreateSkeletalAnimGroup
332         if (description.isAnimationEnabled)
333         {
334             const int animGroupCount = resModel.GetAnimGroupsCount();
335             for (int animGroupIdx = 0; animGroupIdx < animGroupCount; ++animGroupIdx)
336             {
337                 anim::ResAnimGroup resAnimGroup = resModel.GetAnimGroups(animGroupIdx);
338                 const int targetType = resAnimGroup.GetTargetType();
339                 const bool transformFlag =
340                     (resAnimGroup.GetFlags() & anim::ResAnimGroup::FLAG_IS_CALCULATED_TRANSFORM) != 0;
341                 if (transformFlag &&
342                     targetType == anim::ResGraphicsAnimGroup::TARGET_TYPE_BONE)
343                 {
344                     AnimGroup::Builder()
345                         .ResAnimGroup(resAnimGroup)
346                         .UseOriginalValue(true)
347                         .GetMemorySizeInternal(pSize);
348                     break; // 他にスケルタルモデル専用のアニメーショングループがないので
349                 }
350             }
351         }
352     }
353 
354     //----------------------------------------
355     //! @name コンストラクタ/デストラクタ
356     //@{
357 
358     //! コンストラクタです。
SkeletalModel(os::IAllocator * allocator,ResSkeletalModel resource,GfxPtr<Skeleton> & skeleton,bool isSharingSkeleton,const SkeletalModel::Description & description)359     SkeletalModel(
360         os::IAllocator* allocator,
361         ResSkeletalModel resource,
362         GfxPtr<Skeleton>& skeleton,
363         bool isSharingSkeleton,
364         const SkeletalModel::Description& description)
365     : Model(
366         allocator,
367         resource,
368         description),
369       m_Skeleton(skeleton),
370       m_SkeletalAnimGroup(NULL),
371       m_SkeletalAnimBindingIndex(-1),
372       m_SharingSkeleton(isSharingSkeleton)
373     {
374         if (!isSharingSkeleton)
375         {
376             m_Skeleton->SetOwnerSkeletalModel(this);
377         }
378     }
379 
380     //! デストラクタです。
~SkeletalModel()381     virtual ~SkeletalModel()
382     {
383         SafeDestroy(m_SkeletalAnimGroup);
384     }
385 
386     //@}
387 
388     //! スケルタルアニメーション用のアニメーショングループを生成します。
389     Result CreateSkeletalAnimGroup(os::IAllocator* allocator);
390 
391     //! スケルタルアニメーションに登録するモデルデータのポインタを取得します。
392     void* GetAnimTargetObject(const anim::ResAnimGroupMember& anim);
393 
394 private:
395     GfxPtr<Skeleton> m_Skeleton;
396     AnimGroup* m_SkeletalAnimGroup;
397     int m_SkeletalAnimBindingIndex;
398     bool m_SharingSkeleton;
399 };
400 
401 } // namespace gfx
402 } // namespace nw
403 
404 #endif // NW_GFX_SKELETALMODEL_H_
405