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