1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     gfx_BillboardUpdater.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: 22912 $
14  *---------------------------------------------------------------------------*/
15 
16 #include "precompiled.h"
17 
18 #include <nw/gfx/gfx_BillboardUpdater.h>
19 #include <nw/gfx/gfx_CalculatedTransform.h>
20 #include <nw/gfx/res/gfx_ResSkeleton.h>
21 
22 namespace nw
23 {
24 namespace gfx
25 {
26 
27 //----------------------------------------
28 BillboardUpdater*
Create(os::IAllocator * allocator)29 BillboardUpdater::Create(
30     os::IAllocator* allocator
31 )
32 {
33     NW_NULL_ASSERT(allocator);
34 
35     void* updaterMemory = allocator->Alloc(sizeof(BillboardUpdater));
36     NW_NULL_ASSERT(updaterMemory);
37 
38     return new(updaterMemory) BillboardUpdater(allocator);
39 }
40 
41 //----------------------------------------
BillboardUpdater(os::IAllocator * allocator)42 BillboardUpdater::BillboardUpdater(
43     os::IAllocator* allocator
44 )
45 : GfxObject(allocator)
46 {
47     NW_NULL_ASSERT(allocator);
48 }
49 
50 //----------------------------------------
~BillboardUpdater()51 BillboardUpdater::~BillboardUpdater()
52 {
53 }
54 
55 //----------------------------------------
56 void
Update(math::MTX34 * worldMatrix,const math::MTX34 & viewMatrix,const math::MTX34 & inverseViewMatrix,const math::VEC3 & cameraPosition,const CalculatedTransform & worldTransform,const CalculatedTransform & localTransform,ResBone::BillboardMode billboardMode) const57 BillboardUpdater::Update(
58     math::MTX34* worldMatrix,
59     const math::MTX34& viewMatrix,
60     const math::MTX34& inverseViewMatrix,
61     const math::VEC3& cameraPosition,
62     const CalculatedTransform& worldTransform,
63     const CalculatedTransform& localTransform,
64     ResBone::BillboardMode billboardMode
65 ) const
66 {
67     NW_ASSERT( ResBone::BILLBOARD_MODE_OFF < billboardMode && billboardMode <= ResBone::BILLBOARD_MODE_Y_AXIAL_VIEWPOINT );
68 
69     switch (billboardMode)
70     {
71         case ResBone::BILLBOARD_MODE_WORLD:
72             {
73                 this->CalculateLocalMatrix(
74                     worldMatrix,
75                     worldTransform,
76                     inverseViewMatrix.GetColumn(2)
77                     );
78             }
79 
80             break;
81         case ResBone::BILLBOARD_MODE_WORLD_VIEWPOINT:
82             {
83                 this->CalculateLocalMatrix(
84                     worldMatrix,
85                     worldTransform,
86                     cameraPosition - worldTransform.GetTranslate());
87             }
88 
89             break;
90         case ResBone::BILLBOARD_MODE_SCREEN:
91             {
92                 // localTransformにinverseViewMatrixを掛けるだけでは
93                 // アニメーション以外の回転成分も適用されてしまうので
94                 // ジョイントにビルボード設定を施したときなどにおかしな挙動になります。
95                 // そこでlocalTransformのY軸成分とZ軸方向(0,0,1)から
96                 // z軸中心の回転を考慮しつつ正面を向くようなlocalMatrixを生成します。
97                 math::VEC3 zAxis(0.0f ,0.0f, 1.0f);
98                 this->CalculateScreenLocalMatrix(
99                     worldMatrix,
100                     worldTransform,
101                     inverseViewMatrix,
102                     localTransform.TransformMatrix().GetColumn(1),
103                     zAxis);
104             }
105 
106             break;
107         case ResBone::BILLBOARD_MODE_SCREEN_VIEWPOINT:
108             {
109                 // ワールド座標系における位置です
110                 math::VEC3 wPos(worldTransform.GetTranslate());
111 
112                 // ビュー座標系における位置です。
113                 math::VEC3 viewPos;
114                 const f32 (*m)[4] = viewMatrix.m;
115                 viewPos.x = - (m[0][0] * wPos.x + m[0][1] * wPos.y + m[0][2] * wPos.z + m[0][3]);
116                 viewPos.y = - (m[1][0] * wPos.x + m[1][1] * wPos.y + m[1][2] * wPos.z + m[1][3]);
117                 viewPos.z = - (m[2][0] * wPos.x + m[2][1] * wPos.y + m[2][2] * wPos.z + m[2][3]);
118                 viewPos.SafeNormalize(math::VEC3(0.0f ,0.0f, 1.0f));
119 
120                 // localTransformのY軸成分とカメラ方向のベクトルを用いて
121                 // localMatrixを計算します。
122                 this->CalculateScreenLocalMatrix(
123                     worldMatrix,
124                     worldTransform,
125                     inverseViewMatrix,
126                     localTransform.TransformMatrix().GetColumn(1),
127                     viewPos);
128             }
129 
130             break;
131         case ResBone::BILLBOARD_MODE_Y_AXIAL:
132             {
133                 // Y軸の代わりにZ軸を再計算します。それによって
134                 // localMatrixのY軸成分は常にworldTransformのY軸成分になります。
135                 this->CalculateLocalMatrix(
136                     worldMatrix,
137                     worldTransform,
138                     inverseViewMatrix.GetColumn(2),
139                     false);
140             }
141 
142             break;
143         case ResBone::BILLBOARD_MODE_Y_AXIAL_VIEWPOINT:
144             {
145                 // Y軸の代わりにZ軸を再計算します。それによって
146                 // localMatrixのY軸成分は常にworldTransformのY軸成分になります。
147                 this->CalculateLocalMatrix(
148                     worldMatrix,
149                     worldTransform,
150                     cameraPosition - worldTransform.GetTranslate(),
151                     false);
152             }
153 
154             break;
155         default:
156             {
157                 NW_FATAL_ERROR("Unsupported billboard type.");
158             }
159 
160             break;
161     }
162 }
163 
164 //----------------------------------------
165 void
CalculateLocalMatrix(math::MTX34 * localMatrix,const CalculatedTransform & transform,math::VEC3 zAxis,bool recalculateYAxis) const166 BillboardUpdater::CalculateLocalMatrix(
167                                        math::MTX34* localMatrix,
168                                        const CalculatedTransform& transform,
169                                        math::VEC3 zAxis,
170                                        bool recalculateYAxis) const
171 {
172     const math::MTX34& matrix = transform.TransformMatrix();
173     math::VEC3 &rz = zAxis;
174     rz.Normalize();
175 
176     f32 (*const m)[4] = localMatrix->m;
177 
178     if (transform.IsEnabledFlags(CalculatedTransform::FLAG_IS_IDENTITY))
179     {
180         math::VEC3 ry(0.0f, 1.0f, 0.0f);
181 
182         math::VEC3 rx;
183         rx.Cross(ry, rz).SafeNormalize(math::VEC3(1.0f, 0.0f, 0.0f));
184         if (recalculateYAxis)
185         {
186           ry.Cross(rz,rx);
187         }
188         else
189         {
190           rz.Cross(rx,ry);
191         }
192 
193         m[0][0] = rx.x;
194         m[0][1] = ry.x;
195         m[0][2] = rz.x;
196         m[0][3] = 0.0f;
197 
198         m[1][0] = rx.y;
199         m[1][1] = ry.y;
200         m[1][2] = rz.y;
201         m[1][3] = 0.0f;
202 
203         m[2][0] = rx.z;
204         m[2][1] = ry.z;
205         m[2][2] = rz.z;
206         m[2][3] = 0.0f;
207     }
208     else
209     {
210         math::VEC3 ry(matrix.GetColumn(1));
211 
212         ry.SafeNormalize(math::VEC3(1.0f, 0.0f, 0.0f));
213 
214         math::VEC3 rx;
215         rx.Cross(ry,rz).SafeNormalize(matrix.GetColumn(2));
216 
217         if (recalculateYAxis)
218         {
219           ry.Cross(rz,rx);
220         }
221         else
222         {
223           rz.Cross(rx,ry);
224         }
225 
226         m[0][0] = rx.x;
227         m[0][1] = ry.x;
228         m[0][2] = rz.x;
229         m[0][3] = matrix.m[0][3];
230 
231         m[1][0] = rx.y;
232         m[1][1] = ry.y;
233         m[1][2] = rz.y;
234         m[1][3] = matrix.m[1][3];
235 
236         m[2][0] = rx.z;
237         m[2][1] = ry.z;
238         m[2][2] = rz.z;
239         m[2][3] = matrix.m[2][3];
240     }
241 }
242 
243 //----------------------------------------
244 void
CalculateScreenLocalMatrix(math::MTX34 * localMatrix,const CalculatedTransform & transform,const math::MTX34 & inverseViewMatrix,math::VEC3 yAxis,math::VEC3 & zAxis) const245 BillboardUpdater::CalculateScreenLocalMatrix(
246                                        math::MTX34* localMatrix,
247                                        const CalculatedTransform& transform,
248                                        const math::MTX34& inverseViewMatrix,
249                                        math::VEC3 yAxis,
250                                        math::VEC3& zAxis) const
251 {
252     const math::MTX34& matrix = transform.TransformMatrix();
253     math::VEC3 &rz = zAxis;
254     f32 (*const m)[4] = localMatrix->m;
255 
256     if ( transform.IsEnabledFlags(CalculatedTransform::FLAG_IS_IDENTITY) )
257     {
258         math::VEC3 ry(0.0f,1.0f,0.0f);
259 
260         math::VEC3 rx;
261         rx.Cross(ry, rz).Normalize();
262         ry.Cross(rz, rx);
263 
264         m[0][0] = rx.x;
265         m[0][1] = ry.x;
266         m[0][2] = rz.x;
267         m[0][3] = 0.0f;
268 
269         m[1][0] = rx.y;
270         m[1][1] = ry.y;
271         m[1][2] = rz.y;
272         m[1][3] = 0.0f;
273 
274         m[2][0] = rx.z;
275         m[2][1] = ry.z;
276         m[2][2] = rz.z;
277         m[2][3] = 0.0f;
278     }
279     else
280     {
281         math::VEC3 &ry = yAxis;
282 
283         math::VEC3Normalize(&ry, &ry);
284 
285         math::VEC3 rx;
286         rx.Cross(ry,rz).Normalize();
287         ry.Cross(rz,rx);
288 
289         m[0][0] = rx.x;
290         m[0][1] = ry.x;
291         m[0][2] = rz.x;
292         m[0][3] = matrix.m[0][3];
293 
294         m[1][0] = rx.y;
295         m[1][1] = ry.y;
296         m[1][2] = rz.y;
297         m[1][3] = matrix.m[1][3];
298 
299         m[2][0] = rx.z;
300         m[2][1] = ry.z;
301         m[2][2] = rz.z;
302         m[2][3] = matrix.m[2][3];
303     }
304 
305     math::MTX33Mult(localMatrix, &inverseViewMatrix, localMatrix);
306 
307     // この時点では平行移動成分はlocalTransformのものが用いられているので
308     // worldTransformの平行移動成分をセットします。
309     localMatrix->SetColumn(3, transform.GetTranslate());
310 }
311 
312 } // namespace gfx
313 } // namespace nw
314