1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     gfx_SkeletonUpdater.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: 25010 $
14  *---------------------------------------------------------------------------*/
15 
16 #include "precompiled.h"
17 
18 #include <nw/gfx/gfx_SkeletonUpdater.h>
19 #include <nw/gfx/gfx_WorldMatrixUpdater.h>
20 #include <nw/gfx/gfx_BillboardUpdater.h>
21 #include <nw/gfx/gfx_Camera.h>
22 #include <nw/gfx/gfx_SkeletalModel.h>
23 #include <nw/gfx/gfx_Skeleton.h>
24 #include <nw/gfx/res/gfx_ResSkeleton.h>
25 #include <nw/ut/ut_Foreach.h>
26 #include <nw/dev.h>
27 
28 namespace nw
29 {
30 namespace gfx
31 {
32 
33 //----------------------------------------
34 SkeletonUpdater*
Create(os::IAllocator * allocator)35 SkeletonUpdater::Builder::Create(
36     os::IAllocator* allocator
37 )
38 {
39     NW_NULL_ASSERT(allocator);
40 
41     void* updaterMemory = allocator->Alloc(sizeof(SkeletonUpdater));
42     NW_NULL_ASSERT(updaterMemory);
43 
44     return new(updaterMemory) SkeletonUpdater(allocator);
45 }
46 
47 //----------------------------------------
SkeletonUpdater(os::IAllocator * allocator)48 SkeletonUpdater::SkeletonUpdater(
49     os::IAllocator* allocator
50 )
51 : GfxObject(allocator)
52 {
53 }
54 
55 //----------------------------------------
~SkeletonUpdater()56 SkeletonUpdater::~SkeletonUpdater()
57 {
58 }
59 
60 //----------------------------------------
61 void
UpdateWorld(Skeleton * skeleton,const WorldMatrixUpdater & worldMatrixUpdater) const62 SkeletonUpdater::UpdateWorld(
63     Skeleton* skeleton,
64     const WorldMatrixUpdater& worldMatrixUpdater
65 ) const
66 {
67     NW_NULL_ASSERT(skeleton);
68 
69     ResSkeleton resSkeleton = skeleton->GetResSkeleton();
70     NW_ASSERT(resSkeleton.IsValid());
71     bool isModelCoordinate = ut::CheckFlag(resSkeleton.GetFlags(), ResSkeletonData::FLAG_MODEL_COORDINATE);
72     Skeleton::TransformPose& pose = skeleton->LocalTransformPose();
73     Skeleton::TransformPose& worldPose = skeleton->WorldTransformPose();
74 
75     int index = 0;
76     Skeleton::MatrixPose::MatrixRange range = skeleton->WorldMatrixPose().GetAllMatrices();
77     WorldMatrixUpdater::ScalingRule scalingRule = static_cast<WorldMatrixUpdater::ScalingRule>(resSkeleton.GetScalingRule());
78 
79     if (scalingRule == WorldMatrixUpdater::SCALING_RULE_MAYA)
80     {
81         for (Skeleton::MatrixPose::MatrixArray::iterator matrix = range.first; matrix != range.second; ++matrix, ++index)
82         {
83             skeleton->PreCalculateMatrixSignal()(skeleton, index);
84 
85             // CalculatedTransform::FLAG_IS_WORLDMATRIX_CALCULATION_ENABLED を 0 にしたときでもコールバックは呼ばれます。
86             if (pose.GetTransform(index)->IsEnabledFlags(CalculatedTransform::FLAG_IS_WORLDMATRIX_CALCULATION_ENABLED) )
87             {
88                 ResBone bone = resSkeleton.GetBones(index);
89                 int parentBoneIndex = bone.GetParentBoneIndex();
90                 const CalculatedTransform* parentWorldTransform;
91                 const CalculatedTransform* parentLocalTransform;
92 
93                 if (parentBoneIndex == -1)
94                 {
95                     if (isModelCoordinate)
96                     {
97                         parentWorldTransform = &CalculatedTransform::Identity();
98                         parentLocalTransform = &CalculatedTransform::Identity();
99                     }
100                     else
101                     {
102                         parentWorldTransform = &(skeleton->GetOwnerSkeletalModel()->WorldTransform());
103                         parentLocalTransform = &(skeleton->GetOwnerSkeletalModel()->Transform());
104                     }
105                 }
106                 else
107                 {
108                     parentLocalTransform = pose.GetTransform(parentBoneIndex);
109                     parentWorldTransform = worldPose.GetTransform(parentBoneIndex);
110                 }
111 
112                 worldMatrixUpdater.UpdateMaya(
113                     matrix,
114                     worldPose.GetTransform(index),
115                     *pose.GetTransform(index),
116                     *parentWorldTransform,
117                     *parentLocalTransform,
118                     ut::CheckFlag(bone.GetFlags(), ResBoneData::FLAG_IS_SEGMENTSCALE_COMPENSATE));
119             }
120 
121             skeleton->PostCalculateMatrixSignal()(skeleton, index);
122         }
123     }
124     else if(scalingRule == WorldMatrixUpdater::SCALING_RULE_STANDARD)
125     {
126         for (Skeleton::MatrixPose::MatrixArray::iterator matrix = range.first; matrix != range.second; ++matrix, ++index)
127         {
128             skeleton->PreCalculateMatrixSignal()(skeleton, index);
129 
130             // CalculatedTransform::FLAG_IS_WORLDMATRIX_CALCULATION_ENABLED を 0 にしたときでもコールバックは呼ばれます。
131             if ( pose.GetTransform(index)->IsEnabledFlags(CalculatedTransform::FLAG_IS_WORLDMATRIX_CALCULATION_ENABLED) )
132             {
133                 ResBone bone = resSkeleton.GetBones(index);
134                 int parentBoneIndex = bone.GetParentBoneIndex();
135                 const CalculatedTransform* parentWorldTransform;
136                 const CalculatedTransform* parentLocalTransform;
137 
138                 if (parentBoneIndex == -1)
139                 {
140                     if (isModelCoordinate)
141                     {
142                         parentWorldTransform = &CalculatedTransform::Identity();
143                         parentLocalTransform = &CalculatedTransform::Identity();
144                     }
145                     else
146                     {
147                         parentWorldTransform = &(skeleton->GetOwnerSkeletalModel()->WorldTransform());
148                         parentLocalTransform = &(skeleton->GetOwnerSkeletalModel()->Transform());
149                     }
150                 }
151                 else
152                 {
153                     parentLocalTransform = pose.GetTransform(parentBoneIndex);
154                     parentWorldTransform = worldPose.GetTransform(parentBoneIndex);
155                 }
156 
157                 worldMatrixUpdater.UpdateBasic(
158                     matrix,
159                     worldPose.GetTransform(index),
160                     *pose.GetTransform(index),
161                     *parentWorldTransform,
162                     *parentLocalTransform);
163             }
164 
165             skeleton->PostCalculateMatrixSignal()(skeleton, index);
166         }
167     }
168     else // Softimage
169     {
170         for (Skeleton::MatrixPose::MatrixArray::iterator matrix = range.first; matrix != range.second; ++matrix, ++index)
171         {
172             skeleton->PreCalculateMatrixSignal()(skeleton, index);
173 
174             // CalculatedTransform::FLAG_IS_WORLDMATRIX_CALCULATION_ENABLED を 0 にしたときでもコールバックは呼ばれます。
175             if ( pose.GetTransform(index)->IsEnabledFlags(CalculatedTransform::FLAG_IS_WORLDMATRIX_CALCULATION_ENABLED) )
176             {
177                 ResBone bone = resSkeleton.GetBones(index);
178                 int parentBoneIndex = bone.GetParentBoneIndex();
179                 const CalculatedTransform* parentWorldTransform;
180                 const CalculatedTransform* parentLocalTransform;
181 
182                 if (parentBoneIndex == -1)
183                 {
184                     if (isModelCoordinate)
185                     {
186                         parentWorldTransform = &CalculatedTransform::Identity();
187                         parentLocalTransform = &CalculatedTransform::Identity();
188                     }
189                     else
190                     {
191                         parentWorldTransform = &(skeleton->GetOwnerSkeletalModel()->WorldTransform());
192                         parentLocalTransform = &(skeleton->GetOwnerSkeletalModel()->Transform());
193                     }
194                 }
195                 else
196                 {
197                     parentLocalTransform = pose.GetTransform(parentBoneIndex);
198                     parentWorldTransform = worldPose.GetTransform(parentBoneIndex);
199                 }
200 
201                 worldMatrixUpdater.UpdateXsi(
202                     matrix,
203                     worldPose.GetTransform(index),
204                     *pose.GetTransform(index),
205                     *parentWorldTransform,
206                     *parentLocalTransform);
207             }
208 
209             skeleton->PostCalculateMatrixSignal()(skeleton, index);
210         }
211     }
212 }
213 
214 //----------------------------------------
215 void
UpdateView(Skeleton * skeleton,const BillboardUpdater & billboardUpdater,const Camera & camera) const216 SkeletonUpdater::UpdateView(
217     Skeleton* skeleton,
218     const BillboardUpdater& billboardUpdater,
219     const Camera& camera
220 ) const
221 {
222     NW_NULL_ASSERT(skeleton);
223 
224     ResSkeleton resSkeleton = skeleton->GetResSkeleton();
225     NW_ASSERT(resSkeleton.IsValid());
226     Skeleton::TransformPose& pose = skeleton->LocalTransformPose();
227     Skeleton::TransformPose& worldPose = skeleton->WorldTransformPose();
228     SkeletalModel* model = skeleton->GetOwnerSkeletalModel();
229 
230     math::VEC3 cameraPos = camera.WorldMatrix().GetColumn(3);
231 
232     int index = 0;
233     Skeleton::MatrixPose::MatrixRange range = skeleton->WorldMatrixPose().GetAllMatrices();
234     for (Skeleton::MatrixPose::MatrixArray::iterator matrix = range.first; matrix != range.second; ++matrix, ++index)
235     {
236         if ( !pose.GetTransform(index)->IsEnabledFlagsOr(
237             CalculatedTransform::FLAG_IS_WORLDMATRIX_CALCULATION_ENABLED |
238             CalculatedTransform::FLAG_FORCE_VIEW_CALCULATION_ENABLED))
239         {
240             continue;
241         }
242 
243         ResBone bone = resSkeleton.GetBones(index);
244 #if defined(NW_GFX_BILLBOARD_UPDATE_ENABLED)
245         ResBone::BillboardMode billboardMode = bone.GetBillboardMode();
246         if ( billboardMode != ResBone::BILLBOARD_MODE_OFF)
247         {
248             NW_ASSERTMSG(
249                 !ut::CheckFlag(resSkeleton.GetFlags(), ResSkeletonData::FLAG_MODEL_COORDINATE),
250                 "The bone for billboard needs to be world-coordinate.");
251 
252             CalculatedTransform* worldTransform = worldPose.GetTransform(index);
253             CalculatedTransform* localTransform = pose.GetTransform(index);
254 
255             billboardUpdater.Update(
256                 &(*matrix),
257                 camera.ViewMatrix(),
258                 camera.InverseViewMatrix(),
259                 cameraPos,
260                 *worldTransform,
261                 *localTransform,
262                 billboardMode);
263 
264             bool isScaleOne = worldTransform->IsEnabledFlags(CalculatedTransform::FLAG_IS_SCALE_ONE);
265 
266             if (!isScaleOne)
267             {
268                 math::MTX34 worldMatrix;
269                 math::VEC3 rotateScale;
270                 math::MTX34MultScale(&worldMatrix, &worldTransform->TransformMatrix(), &localTransform->Scale());
271                 math::MTX34DecomposeToColumnScale(&rotateScale, &worldMatrix);
272 
273                 math::MTX33Mult(
274                     &(*matrix),
275                     *matrix,
276                     math::MTX34().SetupScale(rotateScale));
277             }
278         }
279 #else
280         NW_UNUSED_VARIABLE(billboardUpdater);
281 #endif
282 
283         // スキニング用にベースマトリクスと掛ける
284         bool isCalcSkiningMatrix =
285             ut::CheckFlag(bone.GetFlags(), ResBoneData::FLAG_HAS_SKINNING_MATRIX | ResBoneData::FLAG_IS_NEED_RENDERING);
286 
287         if (isCalcSkiningMatrix)
288         {
289             math::MTX34Mult(
290                 skeleton->SkiningMatrixPose().GetMatrix(index),
291                 matrix,
292                 &bone.GetInverseBaseMatrix());
293         }
294     }
295 }
296 
297 } // namespace gfx
298 } // namespace nw
299