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