/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_SceneNode.h Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 25986 $ *---------------------------------------------------------------------------*/ #ifndef NW_GFX_SCENENODE_H_ #define NW_GFX_SCENENODE_H_ #include #include #include #include #include #include namespace nw { namespace gfx { class ISceneVisitor; class SceneContext; class WorldMatrixUpdater; //--------------------------------------------------------------------------- //! @brief シーンツリーを構成するための節となるクラスです。 //! 複数の子を持つことができます。 //--------------------------------------------------------------------------- class SceneNode : public SceneObject { private: NW_DISALLOW_COPY_AND_ASSIGN(SceneNode); NW_CHILD_DECLARE_PARENT(SceneNode); static const size_t CHILDREN_MEMORY_ALIGNMENT = os::IAllocator::CACHE_LINE_ALIGNMENT; public: NW_UT_RUNTIME_TYPEINFO; //! @brief トラバースの結果を表すビットフラグの定義です。 enum TraversalResults { //! @details :private FLAG_IS_VISIBLE_SHIFT = 0, //! @details :private FLAG_IS_DIRTY_SHIFT = 1, //! 描画を行うのであれば、1になります。 FLAG_IS_VISIBLE = 0x1 << FLAG_IS_VISIBLE_SHIFT, //! ワールドマトリクスの計算処理を行うのであれば、1になります。 FLAG_IS_DIRTY = 0x1 << FLAG_IS_DIRTY_SHIFT, //! トラバースの結果の初期値です。 FLAG_DEFAULT = FLAG_IS_VISIBLE | FLAG_IS_DIRTY }; //! @brief 更新時に呼ばれるコールバック用シグナルの定義です。 //! //! @sa PreUpdateSignal typedef ut::Signal2 UpdateSignal; //! @brief 更新時に呼ばれるコールバック用スロットの定義です。 typedef UpdateSignal::SlotType UpdateSlot; //! @brief 設定内容です。 struct Description { bool isFixedSizeMemory; //!< 最初に固定サイズのメモリを確保するフラグです。 s32 maxCallbacks; //!< 管理できるコールバックの最大数です。 s32 maxChildren; //!< 子の最大数です。 s32 maxAnimObjectsPerGroup; //!< AnimBindingが持つ、AnimGroupごとのAnimObjectの最大数です。 bool isAnimationEnabled; //!< アニメーション可能かのフラグです。 //! @brief コンストラクタです。 Description() : isFixedSizeMemory(true), maxCallbacks(DEFAULT_MAX_CALLBACKS), maxChildren(DEFAULT_MAX_CHILDREN), maxAnimObjectsPerGroup(DEFAULT_MAX_ANIMOBJECTS), isAnimationEnabled(true) {} }; //---------------------------------------- //! @name 作成/破棄 //@{ //! @brief シーンノードを動的に構築するためのクラスです。 //! //! IsFixedSizeMemory の初期値は true です。false に変更すると、各種最大数の設定は一部を除いて無視されます。 class DynamicBuilder { public: //! コンストラクタです。 DynamicBuilder() {} //! デストラクタです。 ~DynamicBuilder() {} //! @brief 生成時以外にもメモリを確保するかどうかのフラグを設定します。 //! //! true を指定すると、生成時のみ固定サイズのメモリ確保を行います。 //! //! false を指定すると、生成時以外にも必要に応じて動的にメモリ確保が行われます。 DynamicBuilder& IsFixedSizeMemory(bool isFixedSizeMemory) { m_Description.isFixedSizeMemory = isFixedSizeMemory; return *this; } //! 子の最大数を設定します。 DynamicBuilder& MaxChildren(int maxChildren) { m_Description.maxChildren = maxChildren; return *this; } //! 管理できるコールバックの最大数を設定します。 DynamicBuilder& MaxCallbacks(int maxCallbacks) { m_Description.maxCallbacks = maxCallbacks; return *this; } //! @brief AnimBindingが持てるAnimGroupごとのAnimObjectの最大数を設定します。 //! //! IsFixedSizeMemory に false を指定しても、この最大数は有効です。 DynamicBuilder& MaxAnimObjectsPerGroup(s32 maxAnimObjects) { m_Description.maxAnimObjectsPerGroup = maxAnimObjects; return *this; } //! @brief アニメーション可能かを設定します。 //! //! false を指定すると AnimBinding の生成を行いません。 //! そのためアニメーションのバインドができなくなりますが、 //! アニメーション更新の処理が行われなくなるため、 //! シーンアップデートのパフォーマンスが向上します。 DynamicBuilder& IsAnimationEnabled(bool isAnimationEnabled) { m_Description.isAnimationEnabled = isAnimationEnabled; return *this; } //! @brief シーンノードを生成します。 //! //! @param[in] allocator アロケータです。 //! //! @return 生成したシーンノードを返します。 //! SceneNode* Create(os::IAllocator* allocator); private: SceneNode::Description m_Description; }; //! @brief シーンノードを生成します。 //! //! @param[in] parent 親のノードです。 //! @param[in] resource リソースです。 //! @param[in] description 設定内容です。 //! @param[in] allocator アロケータです。 //! //! @return 生成されたシーンノードです。 //! static SceneNode* Create( SceneNode* parent, ResSceneObject resource, const SceneNode::Description& description, os::IAllocator* allocator); //! @brief 自ノードを含むブランチ以下のノードを全て破棄します。 void DestroyBranch() { SceneNodeChildren::iterator end = this->m_Children.end(); for (SceneNodeChildren::iterator child = this->m_Children.begin(); child != end; ++child) { if (*child) { (*child)->SetParent(NULL); (*child)->DestroyBranch(); *child = NULL; } } this->Destroy(); } //! @brief 生成時に必要なデバイスメモリサイズを取得します。 //! //! @param[in] resource リソースです。 //! @param[in] description 設定内容です。 //! //! @details :private static size_t GetDeviceMemorySize( ResSceneObject, Description, size_t alignment = os::IAllocator::DEFAULT_ALIGNMENT) { NW_UNUSED_VARIABLE(alignment); return 0; } //@} //---------------------------------------- //! @name リソース //@{ //! @brief シーンノードのリソースを取得します。 ResSceneNode GetResSceneNode() { return ResStaticCast( this->GetResSceneObject() ); } //! @brief シーンノードのリソースを取得します。 const ResSceneNode GetResSceneNode() const { return ResStaticCast( this->GetResSceneObject() ); } //@} //---------------------------------------- //! @name トランスフォーム //@{ //! @brief 変換情報に関する更新を行います。 //! //! @param[in] worldMatrixUpdater ワールドマトリクス更新クラスです。 //! @param[in] sceneContext シーンコンテキストです。 //! virtual void UpdateTransform( WorldMatrixUpdater* worldMatrixUpdater, SceneContext* sceneContext) { NW_UNUSED_VARIABLE(worldMatrixUpdater); NW_UNUSED_VARIABLE(sceneContext); } //@} //---------------------------------------- //! @name シーンツリー //@{ //! @brief 子を取り付けます。 //! //! @param[in] child 子となるシーンノードです。 //! //! @return 追加できなかった場合は、false が返ります。 //! bool AttachChild(SceneNode* child) { if (IsCircularReference(child)) { return false; } return m_Children.Attach(child); } //! @brief 子を取り外します。 //! //! @param[in] child 子となるシーンノードです。 //! void DetachChild(SceneNode* child) { NW_NULL_ASSERT(child); NW_ASSERT(child->GetParent() == this); m_Children.Detach(child); } //! @brief 子の先頭を取得します。 SceneNodeChildren::iterator GetChildBegin() { return m_Children.begin(); } //! @brief 子の先頭を取得します。 SceneNodeChildren::const_iterator GetChildBegin() const { return m_Children.begin(); } //! @brief 子の末尾を取得します。 SceneNodeChildren::iterator GetChildEnd() { return m_Children.end(); } //! @brief 子の末尾を取得します。 SceneNodeChildren::const_iterator GetChildEnd() const { return m_Children.end(); } //! @brief 全ての子を取り外します。 void DetachAllChildren() { m_Children.clear(); } //! @brief ビジターを受け付けます。 //! //! @param[in] visitor ビジターです。 //! virtual void Accept(ISceneVisitor* visitor); //! @brief 親ノードをたどりワールドマトリクスを取得します。 virtual const math::MTX34& TrackbackWorldMatrix() const { const SceneNode* parent = this->GetParent(); if (parent == NULL) { return nw::math::MTX34::Identity(); } return parent->TrackbackWorldMatrix(); } //! @brief 親ノードをたどりワールドトランスフォームを取得します。 virtual const CalculatedTransform& TrackbackWorldTransform() const { const SceneNode* parent = this->GetParent(); if (parent == NULL) { return CalculatedTransform::Identity(); } return parent->TrackbackWorldTransform(); } //! @brief 親ノードをたどりローカルトランスフォームを取得します。 virtual const CalculatedTransform& TrackbackLocalTransform() const { const SceneNode* parent = this->GetParent(); if (parent == NULL) { return CalculatedTransform::Identity(); } return parent->TrackbackLocalTransform(); } //@} //---------------------------------------- //! @name コールバック //@{ //! @brief シーンノード更新前のシグナルを取得します。 //! //! TransformNode クラスまたはそのサブクラスでは、ノードのワールドマトリクス計算前の //! シグナルとなります。 //! //! @sa UpdateSignal UpdateSignal& PreUpdateSignal() { return *m_PreUpdateSignal; } //! @brief シーンノード更新前のシグナルを取得します。 //! //! TransformNode クラスまたはそのサブクラスでは、ノードのワールドマトリクス計算前の //! シグナルとなります。 //! //! @sa UpdateSignal const UpdateSignal& PreUpdateSignal() const { return *m_PreUpdateSignal; } //@} //---------------------------------------- //! @name アニメーション //@{ //! @brief アニメーションバインディングを取得します。 const AnimBinding* GetAnimBinding() const { return m_AnimBinding.Get(); } //! @brief アニメーションバインディングを取得します。 AnimBinding* GetAnimBinding() { return m_AnimBinding.Get(); } //! @brief アニメーションバインディングを設定します。 void SetAnimBinding(AnimBinding* animBinding) { m_AnimBinding = GfxPtr(animBinding); } //! @brief 設定されたすべてのアニメーションオブジェクトのフレームを更新します。 void UpdateFrame() { AnimBinding* animBinding = GetAnimBinding(); if (animBinding != NULL) { animBinding->UpdateFrame(); } } //@} //---------------------------------------- //! @name トラバース //@{ //! @brief トラバースの結果を取得します。 bit32 GetTraversalResults() const { return this->m_TraversalResults; } //! @brief トラバースの結果を設定します。 void SetTraversalResults(bit32 results) { this->m_TraversalResults = results; } //! @brief 任意のトラバースの結果が有効であるか取得します。 bool IsEnabledResults(bit32 results) const { return ut::CheckFlag(m_TraversalResults, results); } //! @brief トラバースの結果を有効にします。 void EnableTraversalResults(bit32 results) { this->m_TraversalResults = ut::EnableFlag(this->m_TraversalResults, results); } //! @brief トラバースの結果を無効にします。 void DisableTraversalResults(bit32 results) { this->m_TraversalResults = ut::DisableFlag(this->m_TraversalResults, results); } //! @brief ノードからトラバースの結果をコピーします。 void ResetTraversalResults() { this->m_TraversalResults = FLAG_DEFAULT; } //! @brief ノードからトラバースの結果をコピーします。 void CopyTraversalResults(const SceneNode* node) { if (node != NULL) { this->m_TraversalResults = node->GetTraversalResults(); } } //! @brief 親ノードからの変換情報を継承します。 NW_INLINE virtual void InheritTraversalResults(); //! @brief ノード以下で描画を行うためのフラグを設定します。 //! //! falseにするとノード以下の描画が無効になります。 //! //! @param[in] isBranchVisible ノード以下で描画を行うためのフラグです。 //! void SetBranchVisible(bool isBranchVisible) { m_BranchVisible = isBranchVisible; } //! @brief ノード以下で描画を行うためのフラグを取得します。 //! //! @return ノード以下で描画を行うためのフラグです。 //! bool IsBranchVisible() const { return m_BranchVisible; } //@} //! @brief Initialize() の実行に必要なメモリサイズを取得します。 //! //! @details :private static void GetMemorySizeForInitialize( os::MemorySizeCalculator* pSize, ResSceneNode resSceneNode, Description description) { NW_ASSERT(description.isFixedSizeMemory); NW_ASSERT(resSceneNode.IsValid()); // SceneNode::Initialize os::MemorySizeCalculator& size = *pSize; // SceneNode::CreateChildren size.Add(sizeof(SceneNode*) * description.maxChildren, CHILDREN_MEMORY_ALIGNMENT); // SceneNode::CreateCallbacks if (description.maxCallbacks == 0) { UpdateSignal::GetMemorySizeForInvalidateSignalInternal(pSize); } else { UpdateSignal::GetMemorySizeForFixedSizedSignalInternal(pSize, description.maxCallbacks); } // SceneNode::CreateAnimBinding if (description.isAnimationEnabled) { const int animGroupCount = resSceneNode.GetAnimGroupsCount(); if (animGroupCount) { AnimBinding::Builder() .MaxAnimGroups(animGroupCount) .MaxAnimObjectsPerGroup(description.maxAnimObjectsPerGroup) .GetMemorySizeInternal(pSize); } } } protected: //---------------------------------------- //! @name コンストラクタ/デストラクタ //@{ //! @brief コンストラクタです。 SceneNode( os::IAllocator* allocator, ResSceneNode resObj, const SceneNode::Description& description) : SceneObject(allocator, resObj), m_PreUpdateSignal(NULL), m_TraversalResults(FLAG_DEFAULT), m_Description(description), m_BranchVisible(true) { this->SetParent(NULL); if (resObj.IsValid()) { m_BranchVisible = resObj.IsBranchVisible(); } } //! @brief デストラクタです。 virtual ~SceneNode() { SceneNode* parent = this->GetParent(); if (parent) { parent->DetachChild(this); } SafeDestroy(m_PreUpdateSignal); } //@} //! @brief 循環参照ならば true を返します。 //! //! @param[in] child 対象となる子です。 //! bool IsCircularReference(const SceneNode* child) const { const SceneNode* parent = this->GetParent(); if (parent == 0) { return false; } if (parent != child) { return parent->IsCircularReference(child); } return true; } //! @brief 全ての子にビジターを受け付けさせます。 //! //! @param[in] visitor ビジターです。 //! void AcceptChildren(ISceneVisitor* visitor) { NW_FOREACH(SceneNode* child, m_Children) { child->Accept(visitor); } } //! @brief メンバのメモリ確保と初期化を行います。 //! //! コンストラクタではメモリの確保を行わず、こちらでメモリ確保を行います。 //! メモリ確保に失敗した場合は Result でエラーコードが返りますので、Destroy を呼び出してクラスを削除します。 virtual Result Initialize(os::IAllocator* allocator); //! @brief 子のノードです。 SceneNodeChildren m_Children; //! @brief アニメーションとの関連付けです。 GfxPtr m_AnimBinding; private: //! 子を作成します。 Result CreateChildren(os::IAllocator* allocator); //! コールバック関数を生成します。 Result CreateCallbacks(os::IAllocator* allocator); //! アニメーションバインディングを生成します。 Result CreateAnimBinding(os::IAllocator* allocator); //!< 標準のAnimBindingの持つGroupに対するAnimObjectsの最大数です。 static const int DEFAULT_MAX_ANIMOBJECTS = 1; UpdateSignal* m_PreUpdateSignal; bit32 m_TraversalResults; Description m_Description; bool m_BranchVisible; }; //--------------------------------------------------------------------------- //! @brief ブランチ以下を削除後に0を設定するためのインライン関数です。 //! //! @tparam TNode 削除するオブジェクトの型です。 //! //! @param[in] node 削除するオブジェクトです。 //--------------------------------------------------------------------------- template NW_INLINE void SafeDestroyBranch( TNode*& node ) { if (node == NULL) { return; } node->DestroyBranch(); node = NULL; } //--------------------------------------------------------------------------- //! @brief SafeDestroyBranch でオブジェクトを破棄するための関数オブジェクトです。 //! //! @tparam TObject 削除するオブジェクトの型です。 //--------------------------------------------------------------------------- template struct SafeBranchDestroyer : public std::unary_function { //! @brief オブジェクトを破棄します。 void operator()(TNode& node) const { SafeDestroyBranch(node); } }; //--------------------------------------------------------------------------- //! @brief SafeBranchDestroy でコンテナ要素の全てのオブジェクトを破棄するための関数です。 //! //! @tparam TArray 削除するオブジェクトの型です。 //! //! @param[in] nodes 削除するオブジェクトの配列です。 //--------------------------------------------------------------------------- template NW_INLINE void SafeDestroyBranchAll( TArray& nodes ) { std::for_each(nodes.begin(), nodes.end(), SafeBranchDestroyer()); nodes.clear(); } //---------------------------------------- NW_INLINE void SceneNode::InheritTraversalResults() { bit32 results = this->GetTraversalResults(); SceneNode* parent = this->GetParent(); bool isVisible = this->IsBranchVisible(); if (parent != NULL && !(parent->IsEnabledResults(SceneNode::FLAG_IS_VISIBLE))) { isVisible = false; } if (isVisible) { results = ut::EnableFlag(results, SceneNode::FLAG_IS_VISIBLE); } else { results = ut::DisableFlag(results, SceneNode::FLAG_IS_VISIBLE); } this->SetTraversalResults(results); } } // namespace gfx } // namespace nw #endif // NW_GFX_SCENENODE_H_