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