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