1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     gfx_SkeletalModel.cpp
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 #include "precompiled.h"
19 
20 #include <nw/gfx/gfx_SkeletalModel.h>
21 #include <nw/gfx/gfx_ISceneVisitor.h>
22 #include <nw/gfx/gfx_AnimObject.h>
23 
24 #include <nw/anim/res/anim_ResAnimGroup.h>
25 
26 #include <nw/ut/ut_ResUtil.h>
27 #include <nw/ut/ut_ResDictionary.h>
28 #include <nw/ut/ut_Foreach.h>
29 
30 namespace nw
31 {
32 namespace gfx
33 {
34 
35 NW_UT_RUNTIME_TYPEINFO_DEFINITION( SkeletalModel, Model );
36 
37 //----------------------------------------
38 SkeletalModel*
Create(SceneNode * parent,ResSceneObject resource,os::IAllocator * allocator)39 SkeletalModel::Builder::Create(
40             SceneNode* parent,
41             ResSceneObject resource,
42             os::IAllocator* allocator)
43 {
44     NW_NULL_ASSERT(allocator);
45 
46     ResSkeletalModel resModel = ResStaticCast<ResSkeletalModel>(resource);
47 
48     GfxPtr<Skeleton> skeleton;
49     bool isSharedSkeleton = true;
50     if (m_Description.sharedSkeleton != NULL)
51     {
52         // 所有権の移動を行いません。
53         skeleton.Reset(m_Description.sharedSkeleton, false);
54         isSharedSkeleton = true;
55     }
56     else
57     {
58         // スケルトン作成
59         ResSkeleton resSkeleton = resModel.GetSkeleton();
60         NW_ASSERT(resSkeleton.IsValid());
61         Skeleton::TransformPose::TransformArray poseTransforms(resSkeleton.GetBonesCount(), allocator);
62         ResBoneArray bones = resSkeleton.GetBones();
63         ResBoneArray::iterator bonesEnd = bones.end();
64         for (ResBoneArray::iterator bone = bones.begin(); bone != bonesEnd; ++bone)
65         {
66             // リソースから計算済みトランスフォームへデータをコピーします。
67             poseTransforms.PushBackFast(*bone);
68         }
69         skeleton.Reset(StandardSkeleton::Create(
70             resSkeleton,
71             m_Description.maxCallbacks,
72             m_Description.isFixedSizeMemory,
73             poseTransforms,
74             allocator));
75 
76         isSharedSkeleton = false;
77     }
78 
79     // モデル作成
80     void* memory = allocator->Alloc(sizeof(SkeletalModel));
81     NW_NULL_ASSERT(memory);
82 
83     SkeletalModel* model = new(memory) SkeletalModel(
84         allocator,
85         resModel,
86         skeleton,
87         isSharedSkeleton,
88         m_Description);
89 
90     Result result = model->Initialize(allocator);
91     NW_ASSERT(result.IsSuccess());
92 
93     if (parent)
94     {
95         bool isAttached = parent->AttachChild(model);
96         NW_ASSERT(isAttached);
97     }
98 
99     return model;
100 }
101 
102 //----------------------------------------
103 void
Accept(ISceneVisitor * visitor)104 SkeletalModel::Accept(
105     ISceneVisitor* visitor
106 )
107 {
108     visitor->VisitSkeletalModel(this);
109     AcceptChildren(visitor);
110 }
111 
112 //----------------------------------------
113 void
SetFullBakedAnimEnabled(bool enable)114 SkeletalModel::SetFullBakedAnimEnabled(bool enable)
115 {
116     m_FullBakedAnimEnabled = enable;
117 
118     // フルベイクアニメを使用する場合は、モデル座標系で描画する
119     if (enable)
120     {
121         m_Skeleton->GetResSkeleton().EnableFlags(ResSkeletonData::FLAG_MODEL_COORDINATE);
122     }
123     else
124     {
125         m_Skeleton->GetResSkeleton().DisableFlags(ResSkeletonData::FLAG_MODEL_COORDINATE);
126     }
127 
128     NW_NULL_ASSERT(m_SkeletalAnimGroup);
129     SetupAnimGroup(m_SkeletalAnimGroup, enable);
130 }
131 
132 //----------------------------------------
133 Result
CreateSkeletalAnimGroup(os::IAllocator * allocator)134 SkeletalModel::CreateSkeletalAnimGroup(os::IAllocator* allocator)
135 {
136     Result result = INITIALIZE_RESULT_OK;
137 
138     AnimBinding* animBinding = GetAnimBinding();
139     if (animBinding == NULL)
140     {
141         return result;
142     }
143 
144     ResSceneObject resSceneObject = GetResSceneObject();
145     ResSkeletalModel resModel = *reinterpret_cast<ResSkeletalModel*>(&resSceneObject);
146     NW_ASSERT(resModel.IsValid());
147 
148     ResSkeleton resSkeleton = resModel.GetSkeleton();
149     Skeleton::TransformPose& pose = m_Skeleton->LocalTransformPose();
150     Skeleton::OriginalPose& originalPose = m_Skeleton->LocalOriginalPose();
151 
152     const int animGroupCount = resModel.GetAnimGroupsCount();
153     for (int animGroupIdx = 0; animGroupIdx < animGroupCount; ++animGroupIdx)
154     {
155         anim::ResAnimGroup resAnimGroup = resModel.GetAnimGroups(animGroupIdx);
156         const int targetType = resAnimGroup.GetTargetType();
157         const bool transformFlag =
158             (resAnimGroup.GetFlags() & anim::ResAnimGroup::FLAG_IS_CALCULATED_TRANSFORM) != 0;
159         if (transformFlag &&
160             targetType == anim::ResGraphicsAnimGroup::TARGET_TYPE_BONE)
161         {
162             AnimGroup* animGroup = AnimGroup::Builder()
163                 .ResAnimGroup(resAnimGroup)
164                 .SetSceneNode(this)
165                 .UseOriginalValue(true)
166                 .Create(allocator);
167 
168             if (animGroup == NULL)
169             {
170                 result |= Result::MASK_FAIL_BIT;
171             }
172 
173             NW_ENSURE_AND_RETURN(result);
174 
175             const int animMemberCount = animGroup->GetMemberCount();
176             for (int memberIdx = 0; memberIdx < animMemberCount; ++memberIdx)
177             {
178                 anim::ResAnimGroupMember resAnimGroupMember =
179                     animGroup->GetResAnimGroupMember(memberIdx);
180 
181                 // TODO: ResBoneMemberDataの情報を利用する
182                 ResBone bone = resSkeleton.GetBones(resAnimGroupMember.GetPath());
183                 NW_ASSERT(bone.IsValid());
184                 const int boneIdx = bone.GetIndex();
185                 animGroup->SetTargetObjectIndex(memberIdx, boneIdx);
186                 animGroup->SetOriginalValue(memberIdx, originalPose.GetTransform(boneIdx));
187 
188                 void* object = GetAnimTargetObject(resAnimGroupMember);
189                 animGroup->SetTargetObject(memberIdx, object);
190 
191                 // TargetPtrは、SetupAnimGroup()で設定する
192             }
193             animBinding->SetAnimGroup(animGroupIdx, animGroup);
194             m_SkeletalAnimGroup = animGroup;
195             m_SkeletalAnimBindingIndex = animGroupIdx;
196 
197             SetupAnimGroup(animGroup, m_FullBakedAnimEnabled);
198 
199             break; // 他にスケルタルモデル専用のアニメーショングループがないので
200         }
201     }
202 
203     return result;
204 }
205 
206 //----------------------------------------
207 void*
GetAnimTargetObject(const anim::ResAnimGroupMember & anim)208 SkeletalModel::GetAnimTargetObject(const anim::ResAnimGroupMember& anim)
209 {
210     switch(anim.GetObjectType())
211     {
212     case nw::anim::ResAnimGroupMember::OBJECT_TYPE_BONE:
213         {
214             nw::anim::ResBoneMember member = ResStaticCast<nw::anim::ResBoneMember>(anim);
215             const char* boneName = member.GetBoneName();
216 
217             const int boneIndex =
218                 GetResSkeletalModel().GetSkeleton().GetBones(boneName).GetIndex();
219 
220             // 型が変更されたらコンパイルエラーが起きるよう、
221             // いったん目的の型に代入してからreturnで返す
222             CalculatedTransform* ptr =
223                 GetSkeleton()->LocalTransformPose().GetTransform(boneIndex);
224             return ptr;
225         }
226 
227     default:
228         NW_ASSERT(false);
229         return NULL;
230     }
231 }
232 
233 //----------------------------------------
234 Result
Initialize(os::IAllocator * allocator)235 SkeletalModel::Initialize(os::IAllocator* allocator)
236 {
237     Result result = INITIALIZE_RESULT_OK;
238 
239     result |= Model::Initialize(allocator);
240     NW_ENSURE_AND_RETURN(result);
241 
242     if (!m_SharingSkeleton)
243     {
244         result |= CreateSkeletalAnimGroup(allocator);
245         NW_ENSURE_AND_RETURN(result);
246     }
247 
248     return result;
249 }
250 
251 //----------------------------------------
252 void
SetupAnimGroup(AnimGroup * animGroup,bool fullBakedAnimEnabled) const253 SkeletalModel::SetupAnimGroup(AnimGroup* animGroup, bool fullBakedAnimEnabled) const
254 {
255     const int animMemberCount = animGroup->GetMemberCount();
256     for (int memberIdx = 0; memberIdx < animMemberCount; ++memberIdx)
257     {
258         anim::ResAnimGroupMember resAnimGroupMember =
259             animGroup->GetResAnimGroupMember(memberIdx);
260 
261         // TODO: ResBoneMemberDataの情報を利用する
262         ResBone bone = m_Skeleton->GetResSkeleton().GetBones(resAnimGroupMember.GetPath());
263         NW_ASSERT(bone.IsValid());
264         const int boneIdx = bone.GetIndex();
265 
266         if (fullBakedAnimEnabled)
267         {
268             // フルベイクアニメの場合は、ワールドマトリクスを直接更新する
269             void* target = m_Skeleton->WorldMatrixPose().GetMatrix(boneIdx);
270             animGroup->SetTargetPtr(memberIdx, target);
271         }
272         else
273         {
274             // 通常はボーンのローカルトランスフォームを更新する
275             void* target = m_Skeleton->LocalTransformPose().GetTransform(boneIdx);
276             animGroup->SetTargetPtr(memberIdx, target);
277         }
278     }
279 
280     m_SkeletalAnimGroup->SetFullBakedAnimEnabled(fullBakedAnimEnabled);
281 }
282 
283 } // namespace gfx
284 } // namespace nw
285