/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_RenderQueue.h Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo and/or its licensed developers and are protected by national and international copyright laws. 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. The content herein is highly confidential and should be handled accordingly. $Revision: 31311 $ *---------------------------------------------------------------------------*/ #ifndef NW_GFX_RENDERQUEUE_H_ #define NW_GFX_RENDERQUEUE_H_ #include #include #include #include #include #include #include #include namespace nw { namespace gfx { //--------------------------------------------------------------------------- //! @brief 描画要素を格納するキュークラスです。 //! //! @tparam TElement 描画単位となるクラスです。 //! @tparam TElementList 描画単位のクラスを格納するためのコンテナです。 //! @tparam TElementKeyFactory 描画単位をソートするためのキーを作成するクラスです。 //--------------------------------------------------------------------------- template< typename TElement, typename TElementList, typename TElementKeyFactory = BasicRenderKeyFactory > class BasicRenderQueue : public GfxObject { private: NW_DISALLOW_COPY_AND_ASSIGN(BasicRenderQueue); NW_STATIC_ASSERT(!ut::IsArray::value); public: NW_UT_RUNTIME_TYPEINFO; static const u8 PRIORITY_END = 0xff; //!< プライオリティの最後(最大値)を表す定義です。 typedef TElement ElementType; typedef TElementList ElementListType; typedef TElementKeyFactory ElementKeyFactoryType; typedef typename TElement::KeyType ElementKeyType; typedef typename TElementList::reference reference; typedef typename TElementList::difference_type difference_type; typedef typename TElementList::value_type value_type; typedef typename TElementList::iterator iterator; typedef typename TElementList::const_iterator const_iterator; #if defined(_MSC_VER) && _MSC_VER <= 1201 typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; #else typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; #endif //--------------------------------------------------------------------------- //! @brief 描画レイヤーが1(半透明用)のときのみ深度計算を行うための関数オブジェクトです。 //--------------------------------------------------------------------------- struct IsCalculatingOnlyLayer1Functor { IsCalculatingOnlyLayer1Functor(Model* model, ResMesh mesh) { isCalculating = model->GetRenderLayerId(mesh) == ResMaterial::TRANSLUCENCY_KIND_LAYER1; } IsCalculatingOnlyLayer1Functor(ResMaterial::TranslucencyKind renderLayer) { isCalculating = renderLayer == ResMaterial::TRANSLUCENCY_KIND_LAYER1; } bool operator ()() const { return isCalculating; } bool isCalculating; }; //--------------------------------------------------------------------------- //! @brief 常に深度計算を行うための関数オブジェクトです。 //--------------------------------------------------------------------------- struct AlwaysCalculatingFunctor { AlwaysCalculatingFunctor(Model* model, ResMesh mesh) { NW_UNUSED_VARIABLE(model); NW_UNUSED_VARIABLE(mesh); } AlwaysCalculatingFunctor(ResMaterial::TranslucencyKind renderLayer) { NW_UNUSED_VARIABLE(renderLayer); } bool operator ()() const { return true; } }; //--------------------------------------------------------------------------- //! @brief クリップ座標系での深度を計算する関数オブジェクトです。 //--------------------------------------------------------------------------- struct CalculateDepthFunctor { typedef Model ModelType; CalculateDepthFunctor(ModelType* model) : model(model) { NW_NULL_ASSERT(model); this->resModel = model->GetResModel(); NW_ASSERT(resModel.IsValid()); } template float operator ()(ResMesh mesh, const Camera& camera, IsCalculating isCalculating) const { if (isCalculating()) { ResShape shape = resModel.GetShapes(mesh.GetShapeIndex()); NW_ASSERT(shape.IsValid()); return SceneHelper::CalculateDepth( shape.GetCenterPosition(), model->WorldMatrix(), camera); } else { return 0.0f; } } ModelType* model; ResModel resModel; }; //--------------------------------------------------------------------------- //! @brief スケルタルモデル用のクリップ座標系での深度を計算する関数オブジェクトです。 //--------------------------------------------------------------------------- struct CalculateDepthOfSkeletalModelFunctor { typedef SkeletalModel ModelType; CalculateDepthOfSkeletalModelFunctor(ModelType* model) : model(model) { NW_NULL_ASSERT(model); this->resModel = model->GetResModel(); NW_ASSERT(resModel.IsValid()); } template float operator ()(ResMesh mesh, const Camera& camera, IsCalculating isCalculating) const { if (isCalculating()) { float depth; ResShape shape = resModel.GetShapes(mesh.GetShapeIndex()); NW_ASSERT(shape.IsValid()); // プリミティブが1つの場合はボーンのマトリクスを使用する if (shape.GetPrimitiveSetsCount() == 1) { ResPrimitiveSet primitiveSet = shape.GetPrimitiveSets(0); s32 boneIndex = primitiveSet.GetBoneIndexTable(0); Skeleton::MatrixPose& pose = this->model->GetSkeleton()->WorldMatrixPose(); depth = SceneHelper::CalculateDepth( shape.GetCenterPosition(), *pose.GetMatrix(boneIndex), camera); } else { depth = SceneHelper::CalculateDepth( shape.GetCenterPosition(), model->WorldMatrix(), camera); } return depth; } else { return 0.0f; } } ModelType* model; ResModel resModel; }; //--------------------------------------------------------------------------- //! @brief モデルをレンダーキューに追加するための関数オブジェクトです。 //--------------------------------------------------------------------------- template struct BasicEnqueueModelFunctor : public std::unary_function { BasicEnqueueModelFunctor( BasicRenderQueue* renderQueue, ModelType* model, u8 layerId, const Camera& camera) : calculateDepth(model), renderQueue(renderQueue), camera(camera), layerId(layerId) {} result_type operator()(argument_type mesh) { NW_ASSERT(mesh.IsValid()); if (calculateDepth.model->IsMeshVisible(mesh)) { float depth = calculateDepth(mesh, camera, IsCalculating(calculateDepth.model, mesh)); renderQueue->EnqueueMesh(mesh, calculateDepth.model, depth, layerId); } } CalculateDepth calculateDepth; BasicRenderQueue* renderQueue; const Camera& camera; u8 layerId; }; //! @brief モデルをレンダーキューに追加するための関数オブジェクトです。 typedef BasicEnqueueModelFunctor EnqueueModelFunctor; //! @brief スケルタルモデルをレンダーキューに追加するための関数オブジェクトです。 typedef BasicEnqueueModelFunctor EnqueueSkeletalModelFunctor; //! @brief モデルをレンダーキューに追加するための関数オブジェクトです。 //! 描画レイヤーが1(半透明用)のときのみ深度計算を行います。 typedef BasicEnqueueModelFunctor FastEnqueueModelFunctor; //! @brief スケルタルモデルをレンダーキューに追加するための関数オブジェクトです。 //! 描画レイヤーが1(半透明用)のときのみ深度計算を行います。 typedef BasicEnqueueModelFunctor FastEnqueueSkeletalModelFunctor; //--------------------------------------------------------------------------- //! @brief モデルをレンダーキューに追加するための関数オブジェクトです。 //! //! 半透明系のメッシュの深度をモデル単位にしてキーを作成します。 //--------------------------------------------------------------------------- template struct BasicEnqueueModelTranslucentModelBaseFunctor : public std::unary_function { BasicEnqueueModelTranslucentModelBaseFunctor( BasicRenderQueue* renderQueue, ModelType* model, u8 layerId, const Camera& camera) : calculateDepth(model), renderQueue(renderQueue), camera(camera), layerId(layerId) { NW_NULL_ASSERT(renderQueue); modelDepth = SceneHelper::CalculateDepth(model->WorldMatrix(), camera); } result_type operator()(argument_type mesh) { NW_ASSERT(mesh.IsValid()); if (calculateDepth.model->IsMeshVisible(mesh)) { ResMaterial::TranslucencyKind translucencyKind = calculateDepth.model->GetRenderLayerId(mesh); float depth = (translucencyKind == ResMaterial::TRANSLUCENCY_KIND_OPAQUE) ? calculateDepth(mesh, camera, IsCalculating(translucencyKind)) : modelDepth; renderQueue->EnqueueElement( ElementType(mesh, calculateDepth.model), translucencyKind, depth, layerId); } } CalculateDepth calculateDepth; BasicRenderQueue* renderQueue; const Camera& camera; float modelDepth; u8 layerId; }; //! @brief モデルをレンダーキューに追加するための関数オブジェクトです。 typedef BasicEnqueueModelTranslucentModelBaseFunctor EnqueueModelTranslucentModelBaseFunctor; //! @brief スケルタルモデルをレンダーキューに追加するための関数オブジェクトです。 typedef BasicEnqueueModelTranslucentModelBaseFunctor EnqueueSkeletalModelTranslucentModelBaseFunctor; //! @brief モデルをレンダーキューに追加するための関数オブジェクトです。 //! 描画レイヤーが1(半透明用)のときのみ深度計算を行います。 typedef BasicEnqueueModelTranslucentModelBaseFunctor FastEnqueueModelTranslucentModelBaseFunctor; //! @brief スケルタルモデルをレンダーキューに追加するための関数オブジェクトです。 //! 描画レイヤーが1(半透明用)のときのみ深度計算を行います。 typedef BasicEnqueueModelTranslucentModelBaseFunctor FastEnqueueSkeletalModelTranslucentModelBaseFunctor; //---------------------------------------- //! @name デバッグユーティリティ //@{ //! @brief レンダーキューの状態をログ出力にレポートするための関数オブジェクトです。 struct ReportFunctor : public std::unary_function { ReportFunctor() : count(0) {} //! @brief ログ出力にレポートします。 void operator() (reference element) { #ifdef NW_RELEASE NW_UNUSED_VARIABLE(element); #else if (element.IsCommand()) { NW_DEV_LOG("%3d : Command(%x)\n", count, reinterpret_cast(element.GetCommand())); } else { const ResMesh mesh = element.GetMesh(); const ResModel model = element.GetModel()->GetResModel(); const ResMaterial material = element.GetModel()->GetMaterial(mesh.GetMaterialIndex())->GetOriginal(); NW_DEV_LOG("%3d : Model(%s), Mesh(%s), Material(%s)\n", count, model.GetName(), mesh.GetName(), material.GetName()); } ++count; #endif } int count; }; //@} //---------------------------------------- //! @name 作成 //@{ //! @brief BasicRenderQueue を作成するためのクラスです。 //! //! IsFixedSizeMemory の初期値は true です。false に変更すると、各種最大数の設定は無視されます。 class Builder { public: //! @brief コンストラクタです。 Builder() : m_IsFixedSizeMemory(true), m_MaxRenderElements(64) {} //! @brief デストラクタです。 ~Builder() {} //! @brief 生成時以外にもメモリを確保するかどうかのフラグを設定します。 //! //! true を指定すると、生成時のみ固定サイズのメモリ確保を行います。 //! //! false を指定すると、生成時以外にも必要に応じて動的にメモリ確保が行われます。 Builder& IsFixedSizeMemory(bool isFixedSizeMemory) { m_IsFixedSizeMemory = isFixedSizeMemory; return *this; } //! @brief キューに積む描画要素の最大数を設定します。 Builder& MaxRenderElements(int max) { m_MaxRenderElements = max; return *this; } //! @brief レンダーキューを生成します。 //! //! @param[in] allocator アロケータです。 //! //! @return 生成したレンダーキューを返します。 //! BasicRenderQueue* Create(os::IAllocator* allocator) { void* queueMemory = allocator->Alloc(1); NW_NULL_ASSERT(queueMemory); BasicRenderQueue* queue; if (m_IsFixedSizeMemory) { queue = new(queueMemory) BasicRenderQueue(allocator, m_MaxRenderElements); } else { queue = new(queueMemory) BasicRenderQueue(allocator); } return queue; } private: bool m_IsFixedSizeMemory; int m_MaxRenderElements; }; //@} //---------------------------------------- //! @name キュー操作 //@{ //! @brief キューに描画要素を追加します。 //! //! @param[in] element 追加する描画要素です。 //! @param[in] translucencyKind 透明性の種類です。 //! @param[in] depth スクリーン座標系におけるメッシュの深度です。(0.0 ~ 1.0) //! @param[in] layerId 描画要素のソート時に最優先に区分される ID です。レイヤーやビューポートの描画を制御するのに用います。 //! //! @return 追加された描画要素を返します。追加できなかった場合は NULL が返ります。 //! ElementType* EnqueueElement( const ElementType& element, ResMaterial::TranslucencyKind translucencyKind, float depth, u8 layerId) { bool pushed = this->m_List.push_back(element); if (!pushed) { return NULL; } ElementType* back = &m_List.back(); if (this->m_KeyCachingState == UNUSE_CACHED_KEY || translucencyKind == ResMaterial::TRANSLUCENCY_KIND_LAYER1) { RenderKeyFactory* keyFactory = this->GetKeyFactory(translucencyKind); back->Key() = keyFactory->CreateRenderKey(*back, depth, layerId); } else { if (ut::CheckFlag(back->GetMesh().GetFlags(), ResMesh::FLAG_VALID_RENDER_KEY_CACHE)) { // キャッシュしたキーを利用します。 back->Key() = back->GetMesh().GetRenderKeyCache(); } else { // キーを作成して、キャッシュします。 RenderKeyFactory* keyFactory = this->GetKeyFactory(translucencyKind); back->Key() = keyFactory->CreateRenderKey(*back, depth, layerId); back->GetMesh().SetRenderKeyCache(back->Key()); back->GetMesh().SetFlags(ut::EnableFlag( back->GetMesh().GetFlags(), ResMesh::FLAG_VALID_RENDER_KEY_CACHE)); } } return back; } //! @brief キューにメッシュ要素を追加します。 //! //! @param[in] mesh メッシュです。 //! @param[in] model メッシュの所有者となるモデルです。 //! @param[in] depth スクリーン座標系におけるメッシュの深度です。(0.0 ~ 1.0) //! @param[in] layerId 描画要素のソート時に最優先に区分される ID です。レイヤーやビューポートの描画を制御するのに用います。 //! ElementType* EnqueueMesh( ResMesh mesh, Model* model, float depth, u8 layerId) { NW_ASSERT(mesh.IsValid()); NW_NULL_ASSERT(model); return EnqueueElement( ElementType(mesh, model), model->GetRenderLayerId(mesh), depth, layerId); } //! @brief キューにモデルに含まれるメッシュ要素を追加します。 //! //! @param[in] model メッシュの所有者となるモデルです。 //! @param[in] layerId 描画要素のソート時に最優先に区分される ID です。レイヤーやビューポートの描画を制御するのに用います。 //! @param[in] camera メッシュの深度を計算するのに用いるカメラです。 //! void EnqueueModel( Model* model, u8 layerId, const Camera& camera) { NW_NULL_ASSERT(model); gfx::ResMeshArray meshs = model->GetResMeshes(); std::for_each( meshs.begin(), meshs.end(), EnqueueModelFunctor(this, model, layerId, camera)); } //! @brief キューにスケルタルモデルに含まれるメッシュ要素を追加します。 //! //! @param[in] model メッシュの所有者となるモデルです。 //! @param[in] layerId 描画要素のソート時に最優先に区分される ID です。レイヤーやビューポートの描画を制御するのに用います。 //! @param[in] camera メッシュの深度を計算するのに用いるカメラです。 //! void EnqueueSkeletalModel( SkeletalModel* model, u8 layerId, const Camera& camera) { NW_NULL_ASSERT(model); gfx::ResMeshArray meshs = model->GetResMeshes(); std::for_each( meshs.begin(), meshs.end(), EnqueueSkeletalModelFunctor(this, model, layerId, camera)); } //! @brief キューにコマンド要素を追加します。 //! //! ここで設定するコマンドは描画時に呼び出されるので、 //! コマンドのインスタンスは描画時まで保持されるようにしてください。 //! ローカル変数等を使用して、描画前にデストラクトされると //! 不正な関数呼び出しが行われてしまいます。 //! //! @param[in] command 描画時に呼び出されるコマンドです。 //! @param[in] translucencyKind 透明性の種類です。 //! @param[in] priority メッシュの描画優先度です。 //! @param[in] layerId 描画要素のソート時に最優先に区分される ID です。レイヤーやビューポートの描画を制御するのに用います。 //! //! @return 追加されたコマンド要素を返します。追加できなかった場合は NULL が返ります。 //! ElementType* EnqueueCommand( RenderCommand* command, ResMaterial::TranslucencyKind translucencyKind, u8 priority, u8 layerId) { RenderKeyFactory* keyFactory = this->GetKeyFactory(translucencyKind); bool pushed = this->m_List.push_back( ElementType(keyFactory->CreateCommandRenderKey( command, translucencyKind, priority, layerId))); if (pushed) { return &this->m_List.back(); } else { return NULL; } } //! @brief キューの全ての要素を削除します。 //! //! @param[in] cacheEnabled true を指定するとキャッシュ機能を有効にします。 //! void Reset(bool cacheEnabled = false) { this->m_List.clear(); this->m_KeyCachingState = (cacheEnabled) ? USE_CACHED_KEY : UNUSE_CACHED_KEY; } //! @brief キューの全ての要素を削除して、キーファクトリの設定を行います。 //! //! 各キーファクトリに NULL を設定すると、ファクトリの変更を行いません。 //! //! @param[in] opacityKeyFactory 不透明メッシュのキーファクトリです。 //! @param[in] translucentKeyFactory 半透明メッシュのキーファクトリです。 //! @param[in] additiveKeyFactory 加算合成メッシュのキーファクトリです。 //! @param[in] subtractionKeyFactory 減算合成メッシュのキーファクトリです。 //! @param[in] cacheEnabled true を指定するとキャッシュ機能を有効にします。 //! //! @sa nw::gfx::CreatePriorMaterialRenderKeyFactory //! @sa nw::gfx::CreatePriorMaterialReverseDepthRenderKeyFactory //! @sa nw::gfx::CreatePriorMaterialAndZeroDepthRenderKeyFactory //! @sa nw::gfx::CreatePriorDepthRenderKeyFactory //! @sa nw::gfx::CreatePriorDepthReverseDepthRenderKeyFactory //! @sa nw::gfx::CreateTopPriorDepthRenderKeyFactory //! @sa nw::gfx::CreateTopPriorDepthReverseDepthRenderKeyFactory //! void Reset( ElementKeyFactoryType* opacityKeyFactory, ElementKeyFactoryType* translucentKeyFactory, ElementKeyFactoryType* additiveKeyFactory, ElementKeyFactoryType* subtractionKeyFactory, bool cacheEnabled = false) { this->Reset(cacheEnabled); if (opacityKeyFactory != NULL) { this->m_OpacityKeyFactory = opacityKeyFactory; } if (translucentKeyFactory != NULL) { this->m_TranslucentKeyFactory = translucentKeyFactory; } if (additiveKeyFactory != NULL) { this->m_AdditiveKeyFactory = additiveKeyFactory; } if (subtractionKeyFactory != NULL) { this->m_SubtractionKeyFactory = subtractionKeyFactory; } } //@} //---------------------------------------- //! @name 取得/設定 //@{ //! @brief 要素数を取得します。 int Size() const { return m_List.size(); } //! @brief 要素が1つもない場合は true を返します。 bool Empty() const { return m_List.empty(); } //! @brief 先頭の要素を参照します。 ElementType& Peek() { return m_List.front(); } //! @brief 先頭の要素を参照します。 const ElementType& Peek() const { return m_List.front(); } //! @brief 先頭のイテレータを取得します。 iterator Begin() { return m_List.begin(); } //! @brief 先頭のイテレータを取得します。 const_iterator Begin() const { return m_List.begin(); } //! @brief 末尾のイテレータを取得します。 iterator End() { return m_List.end(); } //! @brief 末尾のイテレータを取得します。 const_iterator End() const { return m_List.end(); } //! @brief 先頭のリバースイテレータを取得します。 reverse_iterator ReverseBegin() { return m_List.rbegin(); } //! @brief 先頭のリバースイテレータを取得します。 const_reverse_iterator ReverseBegin() const { return m_List.rbegin(); } //! @brief 末尾のリバースイテレータを取得します。 reverse_iterator ReverseEnd() { return m_List.rend(); } //! @brief 末尾のリバースイテレータを取得します。 const_reverse_iterator ReverseEnd() const { return m_List.rend(); } //@} private: explicit BasicRenderQueue(os::IAllocator* allocator) : GfxObject(allocator), m_List(allocator), m_OpacityKeyFactory(NULL), m_TranslucentKeyFactory(NULL), m_AdditiveKeyFactory(NULL), m_SubtractionKeyFactory(NULL), m_KeyCachingState(UNUSE_CACHED_KEY) {} BasicRenderQueue(os::IAllocator* allocator, int maxRenderElements) : GfxObject(allocator), m_List(maxRenderElements, allocator), m_OpacityKeyFactory(NULL), m_TranslucentKeyFactory(NULL), m_AdditiveKeyFactory(NULL), m_SubtractionKeyFactory(NULL), m_KeyCachingState(UNUSE_CACHED_KEY) {} virtual ~BasicRenderQueue() {} ElementKeyFactoryType* GetKeyFactory(ResMaterial::TranslucencyKind translucencyKind) { ElementKeyFactoryType* keyFactory = NULL; switch (translucencyKind) { case ResMaterial::TRANSLUCENCY_KIND_LAYER0: keyFactory = this->m_OpacityKeyFactory; break; case ResMaterial::TRANSLUCENCY_KIND_LAYER1: keyFactory = this->m_TranslucentKeyFactory; break; case ResMaterial::TRANSLUCENCY_KIND_LAYER2: keyFactory = this->m_SubtractionKeyFactory; break; case ResMaterial::TRANSLUCENCY_KIND_LAYER3: keyFactory = this->m_AdditiveKeyFactory; break; default: NW_FAILSAFE_IF(false) { keyFactory = this->m_OpacityKeyFactory; } break; } NW_NULL_ASSERT(keyFactory); return keyFactory; } TElementList m_List; ElementKeyFactoryType* m_OpacityKeyFactory; ElementKeyFactoryType* m_TranslucentKeyFactory; ElementKeyFactoryType* m_AdditiveKeyFactory; ElementKeyFactoryType* m_SubtractionKeyFactory; enum KeyCachingState { UNUSE_CACHED_KEY, USE_CACHED_KEY }; KeyCachingState m_KeyCachingState; }; //! @brief 標準のレンダーキューの定義です。 typedef BasicRenderQueue< BasicRenderElement, ut::MoveArray > > RenderQueue; //--------------------------------------------------------------------------- //! @brief ソート比較用関数オブジェクトです。 //--------------------------------------------------------------------------- struct RenderElementCompare : public std::binary_function { bool operator() ( const RenderElement& lhs, const RenderElement& rhs) { return lhs.Key() < rhs.Key(); } }; } // namespace gfx } // namespace nw #endif // NW_GFX_RENDERQUEUE_H_