1 /*---------------------------------------------------------------------------*
2 Project: NintendoWare
3 File: gfx_SceneNode.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: 25986 $
14 *---------------------------------------------------------------------------*/
15
16 #ifndef NW_GFX_SCENENODE_H_
17 #define NW_GFX_SCENENODE_H_
18
19 #include <nw/gfx/gfx_SceneObject.h>
20 #include <nw/gfx/gfx_AnimObject.h>
21 #include <nw/gfx/gfx_CalculatedTransform.h>
22
23 #include <nw/ut/ut_Children.h>
24 #include <nw/ut/ut_Foreach.h>
25 #include <nw/ut/ut_Signal.h>
26
27 namespace nw
28 {
29 namespace gfx
30 {
31
32 class ISceneVisitor;
33 class SceneContext;
34 class WorldMatrixUpdater;
35
36 //---------------------------------------------------------------------------
37 //! @brief シーンツリーを構成するための節となるクラスです。
38 //! 複数の子を持つことができます。
39 //---------------------------------------------------------------------------
40 class SceneNode : public SceneObject
41 {
42 private:
43 NW_DISALLOW_COPY_AND_ASSIGN(SceneNode);
44 NW_CHILD_DECLARE_PARENT(SceneNode);
45
46 static const size_t CHILDREN_MEMORY_ALIGNMENT = os::IAllocator::CACHE_LINE_ALIGNMENT;
47
48 public:
49 NW_UT_RUNTIME_TYPEINFO;
50
51 //! @brief トラバースの結果を表すビットフラグの定義です。
52 enum TraversalResults
53 {
54 //! @details :private
55 FLAG_IS_VISIBLE_SHIFT = 0,
56 //! @details :private
57 FLAG_IS_DIRTY_SHIFT = 1,
58
59 //! 描画を行うのであれば、1になります。
60 FLAG_IS_VISIBLE = 0x1 << FLAG_IS_VISIBLE_SHIFT,
61
62 //! ワールドマトリクスの計算処理を行うのであれば、1になります。
63 FLAG_IS_DIRTY = 0x1 << FLAG_IS_DIRTY_SHIFT,
64
65 //! トラバースの結果の初期値です。
66 FLAG_DEFAULT = FLAG_IS_VISIBLE | FLAG_IS_DIRTY
67 };
68
69 //! @brief 更新時に呼ばれるコールバック用シグナルの定義です。
70 //!
71 //! @sa PreUpdateSignal
72 typedef ut::Signal2<void, SceneNode*, SceneContext*> UpdateSignal;
73
74 //! @brief 更新時に呼ばれるコールバック用スロットの定義です。
75 typedef UpdateSignal::SlotType UpdateSlot;
76
77 //! @brief 設定内容です。
78 struct Description
79 {
80 bool isFixedSizeMemory; //!< 最初に固定サイズのメモリを確保するフラグです。
81 s32 maxCallbacks; //!< 管理できるコールバックの最大数です。
82 s32 maxChildren; //!< 子の最大数です。
83 s32 maxAnimObjectsPerGroup; //!< AnimBindingが持つ、AnimGroupごとのAnimObjectの最大数です。
84 bool isAnimationEnabled; //!< アニメーション可能かのフラグです。
85
86 //! @brief コンストラクタです。
DescriptionDescription87 Description()
88 : isFixedSizeMemory(true),
89 maxCallbacks(DEFAULT_MAX_CALLBACKS),
90 maxChildren(DEFAULT_MAX_CHILDREN),
91 maxAnimObjectsPerGroup(DEFAULT_MAX_ANIMOBJECTS),
92 isAnimationEnabled(true)
93 {}
94 };
95
96 //----------------------------------------
97 //! @name 作成/破棄
98 //@{
99
100 //! @brief シーンノードを動的に構築するためのクラスです。
101 //!
102 //! IsFixedSizeMemory の初期値は true です。false に変更すると、各種最大数の設定は一部を除いて無視されます。
103 class DynamicBuilder
104 {
105 public:
106 //! コンストラクタです。
DynamicBuilder()107 DynamicBuilder() {}
108 //! デストラクタです。
~DynamicBuilder()109 ~DynamicBuilder() {}
110
111 //! @brief 生成時以外にもメモリを確保するかどうかのフラグを設定します。
112 //!
113 //! true を指定すると、生成時のみ固定サイズのメモリ確保を行います。
114 //!
115 //! false を指定すると、生成時以外にも必要に応じて動的にメモリ確保が行われます。
IsFixedSizeMemory(bool isFixedSizeMemory)116 DynamicBuilder& IsFixedSizeMemory(bool isFixedSizeMemory)
117 {
118 m_Description.isFixedSizeMemory = isFixedSizeMemory;
119 return *this;
120 }
121
122 //! 子の最大数を設定します。
MaxChildren(int maxChildren)123 DynamicBuilder& MaxChildren(int maxChildren)
124 {
125 m_Description.maxChildren = maxChildren;
126 return *this;
127 }
128
129 //! 管理できるコールバックの最大数を設定します。
MaxCallbacks(int maxCallbacks)130 DynamicBuilder& MaxCallbacks(int maxCallbacks)
131 {
132 m_Description.maxCallbacks = maxCallbacks;
133 return *this;
134 }
135
136 //! @brief AnimBindingが持てるAnimGroupごとのAnimObjectの最大数を設定します。
137 //!
138 //! IsFixedSizeMemory に false を指定しても、この最大数は有効です。
MaxAnimObjectsPerGroup(s32 maxAnimObjects)139 DynamicBuilder& MaxAnimObjectsPerGroup(s32 maxAnimObjects)
140 {
141 m_Description.maxAnimObjectsPerGroup = maxAnimObjects;
142 return *this;
143 }
144
145 //! @brief アニメーション可能かを設定します。
146 //!
147 //! false を指定すると AnimBinding の生成を行いません。
148 //! そのためアニメーションのバインドができなくなりますが、
149 //! アニメーション更新の処理が行われなくなるため、
150 //! シーンアップデートのパフォーマンスが向上します。
IsAnimationEnabled(bool isAnimationEnabled)151 DynamicBuilder& IsAnimationEnabled(bool isAnimationEnabled)
152 {
153 m_Description.isAnimationEnabled = isAnimationEnabled;
154 return *this;
155 }
156
157 //! @brief シーンノードを生成します。
158 //!
159 //! @param[in] allocator アロケータです。
160 //!
161 //! @return 生成したシーンノードを返します。
162 //!
163 SceneNode* Create(os::IAllocator* allocator);
164
165 private:
166 SceneNode::Description m_Description;
167 };
168
169 //! @brief シーンノードを生成します。
170 //!
171 //! @param[in] parent 親のノードです。
172 //! @param[in] resource リソースです。
173 //! @param[in] description 設定内容です。
174 //! @param[in] allocator アロケータです。
175 //!
176 //! @return 生成されたシーンノードです。
177 //!
178 static SceneNode* Create(
179 SceneNode* parent,
180 ResSceneObject resource,
181 const SceneNode::Description& description,
182 os::IAllocator* allocator);
183
184 //! @brief 自ノードを含むブランチ以下のノードを全て破棄します。
DestroyBranch()185 void DestroyBranch()
186 {
187 SceneNodeChildren::iterator end = this->m_Children.end();
188 for (SceneNodeChildren::iterator child = this->m_Children.begin(); child != end; ++child)
189 {
190 if (*child)
191 {
192 (*child)->SetParent(NULL);
193 (*child)->DestroyBranch();
194 *child = NULL;
195 }
196 }
197 this->Destroy();
198 }
199
200 //! @brief 生成時に必要なデバイスメモリサイズを取得します。
201 //!
202 //! @param[in] resource リソースです。
203 //! @param[in] description 設定内容です。
204 //!
205 //! @details :private
206 static size_t GetDeviceMemorySize(
207 ResSceneObject,
208 Description,
209 size_t alignment = os::IAllocator::DEFAULT_ALIGNMENT)
210 {
211 NW_UNUSED_VARIABLE(alignment);
212
213 return 0;
214 }
215
216 //@}
217
218 //----------------------------------------
219 //! @name リソース
220 //@{
221
222 //! @brief シーンノードのリソースを取得します。
GetResSceneNode()223 ResSceneNode GetResSceneNode()
224 {
225 return ResStaticCast<ResSceneNode>( this->GetResSceneObject() );
226 }
227
228 //! @brief シーンノードのリソースを取得します。
GetResSceneNode()229 const ResSceneNode GetResSceneNode() const
230 {
231 return ResStaticCast<ResSceneNode>( this->GetResSceneObject() );
232 }
233
234 //@}
235
236 //----------------------------------------
237 //! @name トランスフォーム
238 //@{
239
240 //! @brief 変換情報に関する更新を行います。
241 //!
242 //! @param[in] worldMatrixUpdater ワールドマトリクス更新クラスです。
243 //! @param[in] sceneContext シーンコンテキストです。
244 //!
UpdateTransform(WorldMatrixUpdater * worldMatrixUpdater,SceneContext * sceneContext)245 virtual void UpdateTransform(
246 WorldMatrixUpdater* worldMatrixUpdater,
247 SceneContext* sceneContext)
248 {
249 NW_UNUSED_VARIABLE(worldMatrixUpdater);
250 NW_UNUSED_VARIABLE(sceneContext);
251 }
252
253 //@}
254
255 //----------------------------------------
256 //! @name シーンツリー
257 //@{
258
259 //! @brief 子を取り付けます。
260 //!
261 //! @param[in] child 子となるシーンノードです。
262 //!
263 //! @return 追加できなかった場合は、false が返ります。
264 //!
AttachChild(SceneNode * child)265 bool AttachChild(SceneNode* child)
266 {
267 if (IsCircularReference(child))
268 {
269 return false;
270 }
271
272 return m_Children.Attach(child);
273 }
274
275 //! @brief 子を取り外します。
276 //!
277 //! @param[in] child 子となるシーンノードです。
278 //!
DetachChild(SceneNode * child)279 void DetachChild(SceneNode* child)
280 {
281 NW_NULL_ASSERT(child);
282 NW_ASSERT(child->GetParent() == this);
283
284 m_Children.Detach(child);
285 }
286
287 //! @brief 子の先頭を取得します。
GetChildBegin()288 SceneNodeChildren::iterator GetChildBegin() { return m_Children.begin(); }
289
290 //! @brief 子の先頭を取得します。
GetChildBegin()291 SceneNodeChildren::const_iterator GetChildBegin() const { return m_Children.begin(); }
292
293 //! @brief 子の末尾を取得します。
GetChildEnd()294 SceneNodeChildren::iterator GetChildEnd() { return m_Children.end(); }
295
296 //! @brief 子の末尾を取得します。
GetChildEnd()297 SceneNodeChildren::const_iterator GetChildEnd() const { return m_Children.end(); }
298
299 //! @brief 全ての子を取り外します。
DetachAllChildren()300 void DetachAllChildren() { m_Children.clear(); }
301
302 //! @brief ビジターを受け付けます。
303 //!
304 //! @param[in] visitor ビジターです。
305 //!
306 virtual void Accept(ISceneVisitor* visitor);
307
308 //! @brief 親ノードをたどりワールドマトリクスを取得します。
TrackbackWorldMatrix()309 virtual const math::MTX34& TrackbackWorldMatrix() const
310 {
311 const SceneNode* parent = this->GetParent();
312 if (parent == NULL)
313 {
314 return nw::math::MTX34::Identity();
315 }
316
317 return parent->TrackbackWorldMatrix();
318 }
319
320 //! @brief 親ノードをたどりワールドトランスフォームを取得します。
TrackbackWorldTransform()321 virtual const CalculatedTransform& TrackbackWorldTransform() const
322 {
323 const SceneNode* parent = this->GetParent();
324 if (parent == NULL)
325 {
326 return CalculatedTransform::Identity();
327 }
328
329 return parent->TrackbackWorldTransform();
330 }
331
332 //! @brief 親ノードをたどりローカルトランスフォームを取得します。
TrackbackLocalTransform()333 virtual const CalculatedTransform& TrackbackLocalTransform() const
334 {
335 const SceneNode* parent = this->GetParent();
336 if (parent == NULL)
337 {
338 return CalculatedTransform::Identity();
339 }
340
341 return parent->TrackbackLocalTransform();
342 }
343
344 //@}
345
346 //----------------------------------------
347 //! @name コールバック
348 //@{
349
350 //! @brief シーンノード更新前のシグナルを取得します。
351 //!
352 //! TransformNode クラスまたはそのサブクラスでは、ノードのワールドマトリクス計算前の
353 //! シグナルとなります。
354 //!
355 //! @sa UpdateSignal
PreUpdateSignal()356 UpdateSignal& PreUpdateSignal() { return *m_PreUpdateSignal; }
357
358 //! @brief シーンノード更新前のシグナルを取得します。
359 //!
360 //! TransformNode クラスまたはそのサブクラスでは、ノードのワールドマトリクス計算前の
361 //! シグナルとなります。
362 //!
363 //! @sa UpdateSignal
PreUpdateSignal()364 const UpdateSignal& PreUpdateSignal() const { return *m_PreUpdateSignal; }
365
366 //@}
367
368 //----------------------------------------
369 //! @name アニメーション
370 //@{
371
372 //! @brief アニメーションバインディングを取得します。
GetAnimBinding()373 const AnimBinding* GetAnimBinding() const { return m_AnimBinding.Get(); }
374
375 //! @brief アニメーションバインディングを取得します。
GetAnimBinding()376 AnimBinding* GetAnimBinding() { return m_AnimBinding.Get(); }
377
378 //! @brief アニメーションバインディングを設定します。
SetAnimBinding(AnimBinding * animBinding)379 void SetAnimBinding(AnimBinding* animBinding) { m_AnimBinding = GfxPtr<AnimBinding>(animBinding); }
380
381 //! @brief 設定されたすべてのアニメーションオブジェクトのフレームを更新します。
UpdateFrame()382 void UpdateFrame()
383 {
384 AnimBinding* animBinding = GetAnimBinding();
385 if (animBinding != NULL)
386 {
387 animBinding->UpdateFrame();
388 }
389 }
390
391 //@}
392
393 //----------------------------------------
394 //! @name トラバース
395 //@{
396
397 //! @brief トラバースの結果を取得します。
GetTraversalResults()398 bit32 GetTraversalResults() const
399 {
400 return this->m_TraversalResults;
401 }
402
403 //! @brief トラバースの結果を設定します。
SetTraversalResults(bit32 results)404 void SetTraversalResults(bit32 results)
405 {
406 this->m_TraversalResults = results;
407 }
408
409 //! @brief 任意のトラバースの結果が有効であるか取得します。
IsEnabledResults(bit32 results)410 bool IsEnabledResults(bit32 results) const
411 {
412 return ut::CheckFlag(m_TraversalResults, results);
413 }
414
415 //! @brief トラバースの結果を有効にします。
EnableTraversalResults(bit32 results)416 void EnableTraversalResults(bit32 results)
417 {
418 this->m_TraversalResults = ut::EnableFlag(this->m_TraversalResults, results);
419 }
420
421 //! @brief トラバースの結果を無効にします。
DisableTraversalResults(bit32 results)422 void DisableTraversalResults(bit32 results)
423 {
424 this->m_TraversalResults = ut::DisableFlag(this->m_TraversalResults, results);
425 }
426
427 //! @brief ノードからトラバースの結果をコピーします。
ResetTraversalResults()428 void ResetTraversalResults()
429 {
430 this->m_TraversalResults = FLAG_DEFAULT;
431 }
432
433 //! @brief ノードからトラバースの結果をコピーします。
CopyTraversalResults(const SceneNode * node)434 void CopyTraversalResults(const SceneNode* node)
435 {
436 if (node != NULL)
437 {
438 this->m_TraversalResults = node->GetTraversalResults();
439 }
440 }
441
442 //! @brief 親ノードからの変換情報を継承します。
443 NW_INLINE virtual void InheritTraversalResults();
444
445 //! @brief ノード以下で描画を行うためのフラグを設定します。
446 //!
447 //! falseにするとノード以下の描画が無効になります。
448 //!
449 //! @param[in] isBranchVisible ノード以下で描画を行うためのフラグです。
450 //!
SetBranchVisible(bool isBranchVisible)451 void SetBranchVisible(bool isBranchVisible)
452 {
453 m_BranchVisible = isBranchVisible;
454 }
455
456 //! @brief ノード以下で描画を行うためのフラグを取得します。
457 //!
458 //! @return ノード以下で描画を行うためのフラグです。
459 //!
IsBranchVisible()460 bool IsBranchVisible() const
461 {
462 return m_BranchVisible;
463 }
464 //@}
465
466 //! @brief Initialize() の実行に必要なメモリサイズを取得します。
467 //!
468 //! @details :private
GetMemorySizeForInitialize(os::MemorySizeCalculator * pSize,ResSceneNode resSceneNode,Description description)469 static void GetMemorySizeForInitialize(
470 os::MemorySizeCalculator* pSize,
471 ResSceneNode resSceneNode,
472 Description description)
473 {
474 NW_ASSERT(description.isFixedSizeMemory);
475
476 NW_ASSERT(resSceneNode.IsValid());
477
478 // SceneNode::Initialize
479 os::MemorySizeCalculator& size = *pSize;
480
481 // SceneNode::CreateChildren
482 size.Add(sizeof(SceneNode*) * description.maxChildren, CHILDREN_MEMORY_ALIGNMENT);
483
484 // SceneNode::CreateCallbacks
485 if (description.maxCallbacks == 0)
486 {
487 UpdateSignal::GetMemorySizeForInvalidateSignalInternal(pSize);
488 }
489 else
490 {
491 UpdateSignal::GetMemorySizeForFixedSizedSignalInternal(pSize, description.maxCallbacks);
492 }
493
494 // SceneNode::CreateAnimBinding
495 if (description.isAnimationEnabled)
496 {
497 const int animGroupCount = resSceneNode.GetAnimGroupsCount();
498 if (animGroupCount)
499 {
500 AnimBinding::Builder()
501 .MaxAnimGroups(animGroupCount)
502 .MaxAnimObjectsPerGroup(description.maxAnimObjectsPerGroup)
503 .GetMemorySizeInternal(pSize);
504 }
505 }
506 }
507
508 protected:
509 //----------------------------------------
510 //! @name コンストラクタ/デストラクタ
511 //@{
512
513 //! @brief コンストラクタです。
SceneNode(os::IAllocator * allocator,ResSceneNode resObj,const SceneNode::Description & description)514 SceneNode(
515 os::IAllocator* allocator,
516 ResSceneNode resObj,
517 const SceneNode::Description& description)
518 : SceneObject(allocator, resObj),
519 m_PreUpdateSignal(NULL),
520 m_TraversalResults(FLAG_DEFAULT),
521 m_Description(description),
522 m_BranchVisible(true)
523 {
524 this->SetParent(NULL);
525 if (resObj.IsValid())
526 {
527 m_BranchVisible = resObj.IsBranchVisible();
528 }
529 }
530
531 //! @brief デストラクタです。
~SceneNode()532 virtual ~SceneNode()
533 {
534 SceneNode* parent = this->GetParent();
535 if (parent)
536 {
537 parent->DetachChild(this);
538 }
539
540 SafeDestroy(m_PreUpdateSignal);
541 }
542
543 //@}
544
545 //! @brief 循環参照ならば true を返します。
546 //!
547 //! @param[in] child 対象となる子です。
548 //!
IsCircularReference(const SceneNode * child)549 bool IsCircularReference(const SceneNode* child) const
550 {
551 const SceneNode* parent = this->GetParent();
552 if (parent == 0)
553 {
554 return false;
555 }
556
557 if (parent != child)
558 {
559 return parent->IsCircularReference(child);
560 }
561
562 return true;
563 }
564
565 //! @brief 全ての子にビジターを受け付けさせます。
566 //!
567 //! @param[in] visitor ビジターです。
568 //!
AcceptChildren(ISceneVisitor * visitor)569 void AcceptChildren(ISceneVisitor* visitor)
570 {
571 NW_FOREACH(SceneNode* child, m_Children)
572 {
573 child->Accept(visitor);
574 }
575 }
576
577 //! @brief メンバのメモリ確保と初期化を行います。
578 //!
579 //! コンストラクタではメモリの確保を行わず、こちらでメモリ確保を行います。
580 //! メモリ確保に失敗した場合は Result でエラーコードが返りますので、Destroy を呼び出してクラスを削除します。
581 virtual Result Initialize(os::IAllocator* allocator);
582
583 //! @brief 子のノードです。
584 SceneNodeChildren m_Children;
585 //! @brief アニメーションとの関連付けです。
586 GfxPtr<AnimBinding> m_AnimBinding;
587
588 private:
589 //! 子を作成します。
590 Result CreateChildren(os::IAllocator* allocator);
591
592 //! コールバック関数を生成します。
593 Result CreateCallbacks(os::IAllocator* allocator);
594
595 //! アニメーションバインディングを生成します。
596 Result CreateAnimBinding(os::IAllocator* allocator);
597
598 //!< 標準のAnimBindingの持つGroupに対するAnimObjectsの最大数です。
599 static const int DEFAULT_MAX_ANIMOBJECTS = 1;
600
601 UpdateSignal* m_PreUpdateSignal;
602 bit32 m_TraversalResults;
603 Description m_Description;
604 bool m_BranchVisible;
605 };
606
607 //---------------------------------------------------------------------------
608 //! @brief ブランチ以下を削除後に0を設定するためのインライン関数です。
609 //!
610 //! @tparam TNode 削除するオブジェクトの型です。
611 //!
612 //! @param[in] node 削除するオブジェクトです。
613 //---------------------------------------------------------------------------
614 template<typename TNode>
615 NW_INLINE void
SafeDestroyBranch(TNode * & node)616 SafeDestroyBranch(
617 TNode*& node
618 )
619 {
620 if (node == NULL) { return; }
621 node->DestroyBranch();
622 node = NULL;
623 }
624
625 //---------------------------------------------------------------------------
626 //! @brief SafeDestroyBranch でオブジェクトを破棄するための関数オブジェクトです。
627 //!
628 //! @tparam TObject 削除するオブジェクトの型です。
629 //---------------------------------------------------------------------------
630 template<typename TNode>
631 struct SafeBranchDestroyer : public std::unary_function<TNode&, void>
632 {
633 //! @brief オブジェクトを破棄します。
operatorSafeBranchDestroyer634 void operator()(TNode& node) const
635 {
636 SafeDestroyBranch(node);
637 }
638 };
639
640 //---------------------------------------------------------------------------
641 //! @brief SafeBranchDestroy でコンテナ要素の全てのオブジェクトを破棄するための関数です。
642 //!
643 //! @tparam TArray 削除するオブジェクトの型です。
644 //!
645 //! @param[in] nodes 削除するオブジェクトの配列です。
646 //---------------------------------------------------------------------------
647 template<typename TArray>
648 NW_INLINE void
SafeDestroyBranchAll(TArray & nodes)649 SafeDestroyBranchAll(
650 TArray& nodes
651 )
652 {
653 std::for_each(nodes.begin(), nodes.end(), SafeBranchDestroyer<typename TArray::value_type>());
654 nodes.clear();
655 }
656
657 //----------------------------------------
658 NW_INLINE void
InheritTraversalResults()659 SceneNode::InheritTraversalResults()
660 {
661 bit32 results = this->GetTraversalResults();
662 SceneNode* parent = this->GetParent();
663
664 bool isVisible = this->IsBranchVisible();
665
666 if (parent != NULL && !(parent->IsEnabledResults(SceneNode::FLAG_IS_VISIBLE)))
667 {
668 isVisible = false;
669 }
670
671 if (isVisible)
672 {
673 results = ut::EnableFlag(results, SceneNode::FLAG_IS_VISIBLE);
674 }
675 else
676 {
677 results = ut::DisableFlag(results, SceneNode::FLAG_IS_VISIBLE);
678 }
679
680 this->SetTraversalResults(results);
681 }
682
683
684 } // namespace gfx
685 } // namespace nw
686
687 #endif // NW_GFX_SCENENODE_H_
688