1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     gfx_TransformNode.h
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: 30175 $
14  *---------------------------------------------------------------------------*/
15 
16 #ifndef NW_GFX_TRANSFORMNODE_H_
17 #define NW_GFX_TRANSFORMNODE_H_
18 
19 #include <nw/gfx/gfx_SceneNode.h>
20 
21 #include <nw/gfx/gfx_CalculatedTransform.h>
22 
23 namespace nw
24 {
25 namespace gfx
26 {
27 
28 //---------------------------------------------------------------------------
29 //! @brief        変換情報を持つシーンノードを表すクラスです。
30 //---------------------------------------------------------------------------
31 class TransformNode : public SceneNode
32 {
33 private:
34     NW_DISALLOW_COPY_AND_ASSIGN(TransformNode);
35 
36 public:
37     NW_UT_RUNTIME_TYPEINFO;
38 
39     //! @brief マトリクス計算時に呼ばれるコールバック用シグナルの定義です。
40     //!
41     //! @sa PostCalculateWorldMatrixSignal
42     typedef ut::Signal2<void, TransformNode*, SceneContext*> CalculateMatrixSignal;
43 
44     //! @brief マトリクス計算時に呼ばれるコールバック用スロットの定義です。
45     typedef CalculateMatrixSignal::SlotType CalculateMatrixSlot;
46 
47     //! @brief 設定内容です。
48     struct Description : public SceneNode::Description
49     {
50         //! @brief コンストラクタです。
DescriptionDescription51         Description(){}
52     };
53 
54     //----------------------------------------
55     //! @name 作成/破棄
56     //@{
57 
58     //! @brief トランスフォームノードを動的に構築するためのクラスです。
59     //!
60     //! IsFixedSizeMemory の初期値は true です。false に変更すると、各種最大数の設定は無視されます。
61     class DynamicBuilder
62     {
63     public:
64         //! @brief コンストラクタです。
DynamicBuilder()65         DynamicBuilder() {}
66         //! @brief デストラクタです。
~DynamicBuilder()67         ~DynamicBuilder() {}
68 
69         //! @brief 生成時以外にもメモリを確保するかどうかのフラグを設定します。
70         //!
71         //!        true を指定すると、生成時のみ固定サイズのメモリ確保を行います。
72         //!
73         //!        false を指定すると、生成時以外にも必要に応じて動的にメモリ確保が行われます。
IsFixedSizeMemory(bool isFixedSizeMemory)74         DynamicBuilder& IsFixedSizeMemory(bool isFixedSizeMemory)
75         {
76             m_Description.isFixedSizeMemory = isFixedSizeMemory;
77             return *this;
78         }
79 
80         //! @brief 子の最大数を設定します。
MaxChildren(int maxChildren)81         DynamicBuilder& MaxChildren(int maxChildren)
82         {
83             m_Description.maxChildren = maxChildren;
84             return *this;
85         }
86 
87         //! @brief 管理できるコールバックの最大数を設定します。
MaxCallbacks(int maxCallbacks)88         DynamicBuilder& MaxCallbacks(int maxCallbacks)
89         {
90             m_Description.maxCallbacks = maxCallbacks;
91             return *this;
92         }
93 
94         //! @brief        トランスフォームノードを生成します。
95         //!
96         //! @param[in]    allocator アロケータです。
97         //!
98         //! @return       生成したトランスフォームノードを返します。
99         //!
100         TransformNode* Create(os::IAllocator* allocator);
101 
102         //! @brief 生成時に必要なメモリサイズを取得します。
103         //!
104         //! メモリサイズは Builder の設定によって変化します。
105         //! すべての設定が終わった後にこの関数を呼び出してください。
106         //!
107         //! @param[in] alignment 計算に用いるアライメントです。2 のべき乗である必要があります。
108         size_t GetMemorySize(size_t alignment = os::IAllocator::DEFAULT_ALIGNMENT) const
109         {
110             os::MemorySizeCalculator size(alignment);
111 
112             size += sizeof(TransformNode);
113             GetMemorySizeForInitialize(&size, ResTransformNode(), m_Description);
114 
115             return size.GetSizeWithPadding(alignment);
116         }
117 
118     private:
119         TransformNode::Description m_Description;
120     };
121 
122     //! @brief        リソースからトランスフォームノードを生成します。
123     //!
124     //! @param[in]    parent 親のノードです。
125     //! @param[in]    resource リソースです。
126     //! @param[in]    description 設定内容です。
127     //! @param[in]    allocator アロケータです。
128     //!
129     //! @return       生成されたトランスフォームノードです。
130     //!
131     static TransformNode* Create(
132         SceneNode* parent,
133         ResSceneObject resource,
134         const TransformNode::Description& description,
135         os::IAllocator* allocator);
136 
137     //! @brief        生成時に必要なメモリサイズを取得します。
138     //!
139     //! @param[in]    resTransformNode リソースです。
140     //! @param[in]    description 設定内容です。
141     //! @param[in] alignment 計算に用いるアライメントです。2 のべき乗である必要があります。
142     static size_t GetMemorySize(
143         ResTransformNode resTransformNode,
144         Description description,
145         size_t alignment = os::IAllocator::DEFAULT_ALIGNMENT)
146     {
147         os::MemorySizeCalculator size(alignment);
148 
149         GetMemorySizeInternal(&size, resTransformNode, description);
150 
151         return size.GetSizeWithPadding(alignment);
152     }
153 
154     //! @details :private
GetMemorySizeInternal(os::MemorySizeCalculator * pSize,ResTransformNode resTransformNode,Description description)155     static void GetMemorySizeInternal(
156         os::MemorySizeCalculator* pSize,
157         ResTransformNode resTransformNode,
158         Description description)
159     {
160         os::MemorySizeCalculator& size = *pSize;
161 
162         size += sizeof(TransformNode);
163         GetMemorySizeForInitialize(pSize, resTransformNode, description);
164     }
165 
166     //@}
167 
168     //----------------------------------------
169     //! @name リソース
170     //@{
171 
172     //! @brief トランスフォームノードのリソースを取得します。
GetResTransformNode()173     ResTransformNode GetResTransformNode()
174     {
175         return ResStaticCast<ResTransformNode>( this->GetResSceneObject() );
176     }
177 
178     //! @brief トランスフォームノードのリソースを取得します。
GetResTransformNode()179     const ResTransformNode GetResTransformNode() const
180     {
181         return ResStaticCast<ResTransformNode>( this->GetResSceneObject() );
182     }
183 
184     //@}
185 
186     //----------------------------------------
187     //! @name トランスフォーム
188     //@{
189 
190     //! @brief 変換情報を取得します。
Transform()191     CalculatedTransform& Transform() { return m_Transform; }
192 
193     //! @brief 変換情報を取得します。
Transform()194     const CalculatedTransform& Transform() const { return m_Transform; }
195 
196     //! @brief ワールドマトリクスを取得します。
WorldMatrix()197     math::MTX34& WorldMatrix() { return m_WorldMatrix; }
198 
199     //! @brief ワールドマトリクスを取得します。
WorldMatrix()200     const math::MTX34& WorldMatrix() const { return m_WorldMatrix; }
201 
202     //! @brief WorldMatrix 計算後の詳細な変換情報を取得します。
203     //!
WorldTransform()204     CalculatedTransform& WorldTransform() { return m_CalculatedTransform; }
205 
206     //! @brief WorldMatrix 計算後の詳細な変換情報を取得します。
207     //!        TransformMatrix にはこのノードの Rotate と Translate に親ノードまでの Scale が掛かっています。
208     //!        Scale は親ノードまでの累積のスケールとなります。
WorldTransform()209     const CalculatedTransform& WorldTransform() const { return m_CalculatedTransform; }
210 
211     //! @brief ワールド行列の逆行列を取得します。2回目以降は、キャッシュを利用します。
212     const math::MTX34& InverseWorldMatrix() const;
213 
214     //! @brief ワールド行列の逆行列のキャッシュを無効化します。(ワールド行列を更新する際に呼び出します。)
215     void InvalidateInverseWorldMatrix();
216 
217     //! @brief 方向情報を更新します。
UpdateDirection()218     virtual void UpdateDirection() {}
219 
220     //! @brief        変換情報に関する更新を行います。
221     //!
222     //! @param[in]    worldMatrixUpdater ワールドマトリクス更新クラスです。
223     //! @param[in]    sceneContext シーンコンテキストです。
224     //!
225     virtual void UpdateTransform(
226         WorldMatrixUpdater* worldMatrixUpdater,
227         SceneContext* sceneContext);
228 
229     //! @brief        リソースの持つトランスフォーム情報を基準にトランスフォームを設定します。
230     //!
231     //!               リソースの持つトランスフォーム情報を表す変換行列に、引数で与えられた変換行列を乗じ、
232     //!               その結果をノードに設定します。
233     //!               transformMatrix はスケール成分を含むことができません。
234     //!
235     //!               この関数は実行毎にリソースのトランスフォーム情報から行列を生成するので
236     //!               パフォーマンスに注意してください。
237     //!
238     //! @param[in]    transformMatrix 合成する変換行列です。
239     //! @param[in]    scale スケールです。
240     void SetResourceBasedTransform(
241         const math::MTX34& transformMatrix,
242         const math::VEC3& scale = math::VEC3(1.0f, 1.0f, 1.0f)
243         )
244     {
245         math::Transform3& resTransform = GetResTransformNode().GetTransform();
246 
247         math::MTX34 resultMatrix;
248         nw::math::MTX34RotXYZRad(&resultMatrix,
249             resTransform.rotate.x,
250             resTransform.rotate.y,
251             resTransform.rotate.z);
252         resultMatrix.f._03 = resTransform.translate.x;
253         resultMatrix.f._13 = resTransform.translate.y;
254         resultMatrix.f._23 = resTransform.translate.z;
255 
256         math::MTX34Mult(&resultMatrix, &transformMatrix, &resultMatrix);
257 
258         math::VEC3 resultScale;
259         math::VEC3Mult(&resultScale, &scale, &resTransform.scale);
260 
261         m_Transform.SetTransformMatrix(resultMatrix);
262         m_Transform.SetScale(resultScale);
263         m_Transform.UpdateFlagsStrictly();
264     }
265 
266     //! @brief        リソースの持つトランスフォーム情報を基準にトランスフォームを設定します。
267     //!
268     //!               リソースの持つトランスフォーム情報を表す変換行列に、引数で与えられた変換行列を乗じ、
269     //!               その結果をノードに設定します。
270     //!
271     //!               SetResourceBasedTransform() と異なり、行列にスケールを含むことができますが、
272     //!               内部でスケールの分離処理を行なうのでパフォーマンスに注意してください。
273     //!
274     //! @param[in]    transformMatrix 合成する変換行列です。
SetResourceScaledTransform(const math::MTX34 & transformMatrix)275     void SetResourceScaledTransform(const math::MTX34& transformMatrix)
276     {
277         math::VEC3 scale;
278         math::MTX34 matrix;
279         nw::math::MTX34DecomposeToColumnScale(&scale, &transformMatrix);
280 
281         NW_ASSERT(scale.x > 0 && scale.y > 0 && scale.z > 0);
282 
283         matrix.f._00 = transformMatrix.f._00 / scale.x;
284         matrix.f._10 = transformMatrix.f._10 / scale.x;
285         matrix.f._20 = transformMatrix.f._20 / scale.x;
286         matrix.f._01 = transformMatrix.f._01 / scale.y;
287         matrix.f._11 = transformMatrix.f._11 / scale.y;
288         matrix.f._21 = transformMatrix.f._21 / scale.y;
289         matrix.f._02 = transformMatrix.f._02 / scale.z;
290         matrix.f._12 = transformMatrix.f._12 / scale.z;
291         matrix.f._22 = transformMatrix.f._22 / scale.z;
292         matrix.f._03 = transformMatrix.f._03;
293         matrix.f._13 = transformMatrix.f._13;
294         matrix.f._23 = transformMatrix.f._23;
295 
296         SetResourceBasedTransform(matrix, scale);
297     }
298 
299     //@}
300 
301     //----------------------------------------
302     //! @name シーンツリー
303     //@{
304 
305     //! @brief        ビジターを受け付けます。
306     //!
307     //! @param[in]    visitor ビジターです。
308     //!
309     virtual void Accept(ISceneVisitor* visitor);
310 
311     //! @brief 親ノードをたどりワールドマトリクスを取得します。
TrackbackWorldMatrix()312     virtual const math::MTX34& TrackbackWorldMatrix() const
313     {
314         return this->WorldMatrix();
315     }
316 
317     //! @brief 親ノードをたどりワールドトランスフォームを取得します。
TrackbackWorldTransform()318     virtual const CalculatedTransform& TrackbackWorldTransform() const
319     {
320         return this->WorldTransform();
321     }
322 
323     //! @brief 親ノードをたどりローカルトランスフォームを取得します。
TrackbackLocalTransform()324     virtual const CalculatedTransform& TrackbackLocalTransform() const
325     {
326         return this->Transform();
327     }
328 
329     //! @brief 親ノードから変換情報を継承します。
330     NW_INLINE virtual void InheritTraversalResults();
331 
332     //@}
333 
334     //----------------------------------------
335     //! @name コールバック
336     //@{
337 
338     //! @brief ワールドマトリクス計算後のシグナルを取得します。
339     //!
340     //! @sa CalculateMatrixSignal
PostCalculateWorldMatrixSignal()341     CalculateMatrixSignal& PostCalculateWorldMatrixSignal()
342     {
343         return *m_PostCalculateWorldMatrixSignal;
344     }
345 
346     //! @brief ワールドマトリクス計算後のシグナルを取得します。
347     //!
348     //! @sa CalculateMatrixSignal
PostCalculateWorldMatrixSignal()349     const CalculateMatrixSignal& PostCalculateWorldMatrixSignal() const
350     {
351         return *m_PostCalculateWorldMatrixSignal;
352     }
353 
354     //@}
355 
356 protected:
357     //----------------------------------------
358     //! @name コンストラクタ/デストラクタ
359     //@{
360 
361     //! @brief コンストラクタです。
362     TransformNode(
363         os::IAllocator* allocator,
364         ResTransformNode resObj,
365         const TransformNode::Description& description);
366 
367     //! @brief デストラクタです。
~TransformNode()368     virtual ~TransformNode()
369     {
370         SafeDestroy(m_PostCalculateWorldMatrixSignal);
371     }
372 
373     virtual Result Initialize(os::IAllocator* allocator);
374 
375     //@}
376 
377     //! @brief 親ノードの変換情報を継承した方向を計算します。
CalcInheritingDiretion(math::VEC3 & inheritingDirection,const math::VEC3 & direction)378     void CalcInheritingDiretion(
379         math::VEC3& inheritingDirection,
380         const math::VEC3 &direction) const
381     {
382         if (this->GetParent() == NULL)
383         {
384             inheritingDirection = direction;
385         }
386         else
387         {
388             const math::MTX34& parentWorldMatrix =  this->TrackbackWorldMatrix();
389             inheritingDirection.x =
390                 parentWorldMatrix.f._00 * direction.x +
391                 parentWorldMatrix.f._01 * direction.y +
392                 parentWorldMatrix.f._02 * direction.z;
393 
394             inheritingDirection.y =
395                 parentWorldMatrix.f._10 * direction.x +
396                 parentWorldMatrix.f._11 * direction.y +
397                 parentWorldMatrix.f._12 * direction.z;
398 
399             inheritingDirection.z =
400                 parentWorldMatrix.f._20 * direction.x +
401                 parentWorldMatrix.f._21 * direction.y +
402                 parentWorldMatrix.f._22 * direction.z;
403         }
404     }
405 
406 
407     //! @brief   Initialize() の実行に必要なメモリサイズを取得します。
408     //!
409     //! @details :private
GetMemorySizeForInitialize(os::MemorySizeCalculator * pSize,ResTransformNode resTransformNode,Description description)410     static void GetMemorySizeForInitialize(
411         os::MemorySizeCalculator* pSize,
412         ResTransformNode resTransformNode,
413         Description description)
414     {
415         NW_ASSERT(description.isFixedSizeMemory);
416 
417         SceneNode::GetMemorySizeForInitialize(pSize, resTransformNode, description);
418 
419         // TransformNode::CreateCallbacks
420         if (description.maxCallbacks == 0)
421         {
422             CalculateMatrixSignal::GetMemorySizeForInvalidateSignalInternal(pSize);
423         }
424         else
425         {
426             CalculateMatrixSignal::GetMemorySizeForFixedSizedSignalInternal(pSize, description.maxCallbacks);
427         }
428     }
429 
430 private:
431     //! @brief コールバック関数を生成します。
432     Result CreateCallbacks(os::IAllocator* allocator);
433 
434     CalculatedTransform m_Transform;
435     math::MTX34 m_WorldMatrix;
436     CalculatedTransform m_CalculatedTransform;
437 
438     mutable math::MTX34 m_InverseWorldMatrix;
439     mutable bool m_IsInverseWorldMatrixValid;
440 
441     CalculateMatrixSignal* m_PostCalculateWorldMatrixSignal;
442     bool m_IsBranchWorldMatrixCalculationEnabled;
443     Description m_Description;
444 };
445 
446 //----------------------------------------
447 NW_INLINE void
InheritTraversalResults()448 TransformNode::InheritTraversalResults()
449 {
450     SceneNode::InheritTraversalResults();
451 
452     SceneNode* parent = this->GetParent();
453     bit32 results = this->GetTraversalResults();
454 
455     if (this->Transform().IsEnabledFlags(CalculatedTransform::FLAG_IS_DIRTY))
456     {
457         results = ut::EnableFlag(results, SceneNode::FLAG_IS_DIRTY);
458     }
459     else if (parent== NULL)
460     {
461         results = ut::DisableFlag(results, SceneNode::FLAG_IS_DIRTY);
462     }
463     else if (parent->IsEnabledResults(SceneNode::FLAG_IS_DIRTY))
464     {
465         results = ut::EnableFlag(results, SceneNode::FLAG_IS_DIRTY);
466     }
467     else
468     {
469         results = ut::DisableFlag(results, SceneNode::FLAG_IS_DIRTY);
470     }
471 
472     this->SetTraversalResults(results);
473 }
474 
475 } // namespace gfx
476 } // namespace nw
477 
478 #endif // NW_GFX_TRANSFORMNODE_H_
479