/*---------------------------------------------------------------------------* Project: NintendoWare File: demo_Particle.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: 31829 $ *---------------------------------------------------------------------------*/ #ifndef NW_DEMO_PARTICLE_H_ #define NW_DEMO_PARTICLE_H_ #include #include #include namespace nw { namespace demo { //! @brief パーティクル管理用のクラスです。 //! Transformノードに管理用の情報を付加しています。 class ParticleHandle : public gfx::TransformNode { private: NW_DISALLOW_COPY_AND_ASSIGN(ParticleHandle); public: NW_UT_RUNTIME_TYPEINFO; //---------------------------------------- //! @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 シーンノードを生成します。 //! //! @param[in] allocator アロケータです。 //! //! @return 生成したシーンノードを返します。 //! ParticleHandle* Create(os::IAllocator* allocator); private: gfx::TransformNode::Description m_Description; }; //@} //---------------------------------------- //! @name パーティクル操作 //@{ //! @brief IDを取得します。 //! //! @return IDです。 int GetID() const { return m_Id; } //! @brief IDを設定します。 //! //! @param[in] id 設定するIDです。 void SetID(int id) { m_Id = id; } //! @brief 位置を設定します。 //! //! @param[in] x 設定する位置Xです。 //! @param[in] y 設定する位置Yです。 //! @param[in] z 設定する位置Zです。 void SetTranslate(f32 x, f32 y, f32 z) { this->Transform().SetTranslate(x, y, z); } //! @brief 位置を設定します。 //! //! @param[in] translate 設定する位置です。 void SetTranslate(const nw::math::VEC3& translate) { this->Transform().SetTranslate(translate); } //! @brief 位置を取得します。 void GetTranslate(nw::math::VEC3* translate) const { this->Transform().GetTranslate(translate); } //! @brief 回転を設定します。 //! //! @param[in] x 設定する回転値X(ラジアン)です。 //! @param[in] y 設定する回転値Y(ラジアン)です。 //! @param[in] z 設定する回転値Z(ラジアン)です。 void SetRotate(f32 radX, f32 radY, f32 radZ) { this->Transform().SetRotateXYZ(radX, radY, radZ ); } //! @brief 拡縮を設定します。 //! //! @param[in] x 設定する拡縮値Xです。 //! @param[in] y 設定する拡縮値Yです。 //! @param[in] z 設定する拡縮値Zです。 void SetScale(f32 x, f32 y, f32 z) { this->Transform().SetScale(x, y, z); } //! @brief ステップフレームを設定します。 //! //! @param[in] 設定するステップフレームです。 void SetStepFrame(f32 stepFrame) { u32 emitterNum = this->GetParticleEmitterSize(); for (u32 i = 0; i < emitterNum; ++i) { nw::gfx::ParticleEmitter* emitter = this->GetParticleEmitter(i); emitter->ParticleAnimFrameController().SetStepFrame(stepFrame); } u32 modelNum = this->GetParticleModelSize(); for (u32 i = 0; i < modelNum; ++i) { nw::gfx::ParticleModel* model = this->GetParticleModel(i); model->ParticleAnimFrameController().SetStepFrame(stepFrame); } } //! @brief フレームを設定します。 //! //! @param[in] 設定するフレームです。 void SetFrame(f32 frame) { u32 emitterNum = this->GetParticleEmitterSize(); for (u32 i = 0; i < emitterNum; ++i) { nw::gfx::ParticleEmitter* emitter = this->GetParticleEmitter(i); emitter->ParticleAnimFrameController().SetFrame(frame); } u32 modelNum = this->GetParticleModelSize(); for (u32 i = 0; i < modelNum; ++i) { nw::gfx::ParticleModel* model = this->GetParticleModel(i); model->ParticleAnimFrameController().SetFrame(frame); } } //! @brief 指定数だけフレームを回します。 //! //! @param[in] times フレームを回す回数です。 //! @param[in] addFrame 進めるフレーム数です。 //! @param[in] particleContext パーティクルコンテキストです。 void AddTimes(uint times, gfx::ParticleContext* particleContext) { for (u32 k = 0; k < times; ++k) { for (u32 i = 0; i < this->GetParticleModelSize(); ++i) { this->GetParticleModel(i)->UpdateParticleFrame(); } for (u32 i = 0; i < this->GetParticleEmitterSize(); ++i) { this->GetParticleEmitter(i)->UpdateParticleFrame(); this->GetParticleEmitter(i)->Emission(particleContext); } for (u32 i = 0; i < this->GetParticleModelSize(); ++i) { for (u32 j = 0; j < this->GetParticleModel(i)->GetParticleSetsCount(); ++j) { this->GetParticleModel(i)->GetParticleSets(j)->UpdateParticles(particleContext); } } } } //! @brief リセットします。 //! GPU処理中はコールできません。 //! //! @param[in] リセットを行い、初期状態に戻します。 void Reset() { u32 emitterNum = this->GetParticleEmitterSize(); for (u32 i = 0; i < emitterNum; ++i) { nw::gfx::ParticleEmitter* emitter = this->GetParticleEmitter(i); emitter->Reset(); } u32 modelNum = this->GetParticleModelSize(); for (u32 i = 0; i < modelNum; ++i) { nw::gfx::ParticleModel* model = this->GetParticleModel(i); model->ForeachParticleSet(nw::gfx::ParticleSetsClear()); model->ParticleAnimFrameController().SetFrame(0.f); // 処理順序の検査のためのヒント情報をリセットします model->ResetDebugHint(); } } //! @brief 放出量をセットします。 //! //! @param[in] ratio 設定する放出量です。 void SetEmissionRatio(f32 ratio) { u32 emitterNum = this->GetParticleEmitterSize(); for (u32 i = 0; i < emitterNum; ++i) { nw::gfx::ParticleEmitter* emitter = this->GetParticleEmitter(i); nw::gfx::ResParticleEmitterParameter resParameter = emitter->GetResParticleEmitterParameterCopy(); NW_ASSERT(resParameter.IsValid()); resParameter.SetEmissionRatio(ratio); } } //! @brief 現在のパーティクルの個数を取得します。 //! //! @return 現在のパーティクルの個数です。 u32 GetParticleSize() { u32 particleNum = 0; u32 modelNum = this->GetParticleModelSize(); for (u32 i = 0; i < modelNum; ++i) { nw::gfx::ParticleModel* model = this->GetParticleModel(i); for (u32 j = 0; j < model->GetParticleSetsCount(); ++j ) { nw::gfx::ParticleSet* particleSet = model->GetParticleSets(j); particleNum += particleSet->GetParticleCollection()->GetCount(); } } return particleNum; } //! @brief 終了状態かどうかを取得します。 //! //! @return 終了状態であれば、trueが返ります。 bool IsDone() { u32 modelNum = this->GetParticleModelSize(); for (u32 i = 0; i < modelNum; ++i) { nw::gfx::ParticleModel* model = this->GetParticleModel(i); if (model->HasParticle()) { return false; } } u32 emitterNum = this->GetParticleEmitterSize(); for (u32 i = 0; i < emitterNum; ++i) { nw::gfx::ParticleEmitter* emitter = this->GetParticleEmitter(i); if (emitter->IsAlive()) { return false; } } return true; } //! @brief 再生中のエフェクトがループエフェクト(無限ループ)かどうかを取得します。 //! //! @return ループエフェクト(無限ループ)であれば、trueが返ります。 bool IsInfinity() { bool isInfinity = false; u32 emitterNum = this->GetParticleEmitterSize(); for (u32 i = 0; i < emitterNum; ++i) { nw::gfx::ParticleEmitter* emitter = this->GetParticleEmitter(i); const nw::gfx::ResParticleEmitterParameter resource = emitter->GetResParticleEmitterParameterCopy(false); NW_ASSERT(resource.IsValid()); if (resource.GetEmissionSpanInfinity()) { isInfinity = true; } } return isInfinity; } //! @brief 粒子のスケールのオフセット値設定します。 //! //! @param[in] particleOffset 設定する粒子のスケールのオフセットです。 void SetParticleScaleOffset( f32 particleOffset ) { u32 modelNum = this->GetParticleModelSize(); for (u32 i = 0; i < modelNum; ++i) { nw::gfx::ParticleModel* model = this->GetParticleModel(i); for (int j = 0; j < (int)model->GetParticleSetsCount(); ++j) { nw::gfx::ParticleSet* particleSet = model->GetParticleSets(j); particleSet->SetScaleOffset(nw::math::VEC3(particleOffset, particleOffset, particleOffset)); } } } //! @brief 粒子のスケールのオフセット値設定します。 //! //! @param[in] particleOffsetX 設定する粒子のスケールのオフセットXです。 //! @param[in] particleOffsetY 設定する粒子のスケールのオフセットYです。 //! @param[in] particleOffsetZ 設定する粒子のスケールのオフセットZです。 void SetParticleScaleOffset( f32 particleOffsetX, f32 particleOffsetY, f32 particleOffsetZ = 1.f ) { u32 modelNum = this->GetParticleModelSize(); for (u32 i = 0; i < modelNum; ++i) { nw::gfx::ParticleModel* model = this->GetParticleModel(i); for (int j = 0; j < (int)model->GetParticleSetsCount(); ++j) { nw::gfx::ParticleSet* particleSet = model->GetParticleSets(j); particleSet->SetScaleOffset(nw::math::VEC3(particleOffsetX, particleOffsetY, particleOffsetZ)); } } } //! @brief 放出処理を行います。 //! //! @param[in] particleContext パーティクルコンテキストです。 void Emission(gfx::ParticleContext* particleContext) { for (u32 i = 0; i < this->GetParticleEmitterSize(); ++i) { nw::gfx::ParticleEmitter* emitter = this->GetParticleEmitter(i); emitter->UpdateParticleFrame(); emitter->Emission(particleContext); emitter->Reset(); } } //@} //---------------------------------------- //! @name インスタンス管理 //@{ //! @brief パーティクルモデルのインスタンスを登録します。 //! //! @param[in] 登録するパーティクルモデルのインスタンスです。 void RegisterParticleModel( nw::gfx::ParticleModel* model ) { m_ModelInstances.PushBack(model); } //! @brief パーティクルエミッタのインスタンスを登録します。 //! //! @param[in] 登録するパーティクルエミッタのインスタンスです。 void RegisterParticleEmitter( nw::gfx::ParticleEmitter* emitter ) { m_EmitterInstances.PushBack(emitter); } //! @brief 保持しているパーティクルモデル数を取得します。 //! //! @return 保持しているパーティクルモデル数です。 u32 GetParticleModelSize() const { return m_ModelInstances.size(); } //! @brief 保持しているパーティクルエミッタ数を取得します。 //! //! @return 保持しているパーティクルエミッタ数です。 u32 GetParticleEmitterSize() const { return m_EmitterInstances.size(); } //! @brief 保持しているパーティクルモデルインスタンスを取得します。 //! //! @return パーティクルモデルのインスタンスです。 nw::gfx::ParticleModel* GetParticleModel(u32 index) { if (this->GetParticleModelSize() > index) { return m_ModelInstances[index]; } return NULL; } //! @brief 保持しているパーティクルエミッタインスタンスを取得します。 //! //! @return パーティクルエミッタのインスタンスです。 nw::gfx::ParticleEmitter* GetParticleEmitter(u32 index) { if (this->GetParticleEmitterSize() > index) { return m_EmitterInstances[index]; } return NULL; } //! @brief パーティクルセットを取得します。 //! //! @param[in] name パーティクルセットの名前です。 //! @return パーティクルセットを返します。 nw::gfx::ParticleSet* GetParticleSet(const char* name) { u32 modelNum = this->GetParticleModelSize(); for (u32 i = 0; i < modelNum; ++i) { nw::gfx::ParticleModel* model = this->GetParticleModel(i); for (int j = 0; j < (int)model->GetParticleSetsCount(); ++j) { nw::gfx::ParticleSet* particleSet = model->GetParticleSets(j); const char* particleSetName = particleSet->GetResParticleSet().GetName(); if (std::strcmp(particleSetName, name) == 0) { return particleSet; } } } return NULL; } //@} protected: //---------------------------------------- //! @name コンストラクタ/デストラクタ //@{ //! コンストラクタです。 ParticleHandle( os::IAllocator* allocator, gfx::ResTransformNode resObj, const gfx::TransformNode::Description& description); //! デストラクタです。 virtual ~ParticleHandle() { } //@} private: int m_Id; ut::MoveArray m_ModelInstances; ut::MoveArray m_EmitterInstances; }; // NW4C1.2.0との互換性維持の為。 typedef ParticleHandle ParticleNode; //! @brief 一表現のためのパーティクル一式です // パーティクルを管理する機能はアプリケーション毎の実装になります。 // このクラスは参考のための実装例です。 // パフォーマンスよりも簡便性を重視した実装です。 class ParticleEffect { public: //---------------------------------------- //! @name 初期化・終了処理 //@{ //! @brief ParticleEffectクラスを生成します。 //! //! @return 生成したParticleEffectクラスを返します。 //! static ParticleEffect* Create(os::IAllocator* mainAllocator, os::IAllocator* deviceAllocator, bool autoAlocate, gfx::ParticleContext* particleContext); //! @brief ParticleEffectクラスを破棄します。 //! void Destroy(); private: //! @brief コンストラクタです。 //! //! @param[in] mainAllocator メインメモリのアロケータです。 //! @param[in] deviceAllocator デバイスメモリのアロケータです。 //! @param[in] autoAlocate 足りない時に自動でアロケートすることを許可するフラグです。 //! @param[in] particleContext パーティクルコンテキストです。 //! ParticleEffect( os::IAllocator* mainAllocator, os::IAllocator* deviceAllocator, bool autoAlocate, gfx::ParticleContext* particleContext ) : m_NextId(0), m_MainAllocator(mainAllocator), m_DeviceAllocator(deviceAllocator), m_AutoAlocate(autoAlocate), m_ResModels(mainAllocator), m_ResEmitters(mainAllocator), m_FreeInstances(mainAllocator), m_ActiveInstances(mainAllocator), m_ParticleContext(particleContext) { } //! デストラクタです。 ~ParticleEffect() { NW_ASSERT(this->GetActiveSize() == 0); this->FreePool(); } public: //! @brief リソースのセットアップを行います。 //! //! @param[in] useParticleMaterial パーティクルマテリアルを使用する場合はtrueを指定します。 void Setup(gfx::ResGraphicsFile resource, bool useParticleMaterial = false); //! @brief リソースから指定された名前のノードを管理対象として登録します。 //! //! @param[in] resource グラフィックスリソースです。 //! @param[in] nodeNames 登録するノード一覧です。 void Register(gfx::ResGraphicsFile resource, const char** nodeNames); //! @brief 全てのリソースを管理対象として登録します。 //! //! @param[in] resource グラフィックスリソースです。 void Register(gfx::ResGraphicsFile resource); //@} //---------------------------------------- //! @name 生成・破棄 //@{ //! @brief ParticleHandleを生成します。 //! @return 生成されたParticleHandleインスタンスを返します。 ParticleHandle* CreateMultiEmitterParticleHandle(u32 particleEmitterNum); //! @brief 生成済みのParticleHandleをリース(レンタル)します。 //! @return 生成済みのParticleHandleインスタンスを返します。 ParticleHandle* LeaseInstance() { if (this->GetFreeSize() == 0) { if (this->m_AutoAlocate) { this->AddPool(1); } else { return NULL; } } ParticleHandle* node = this->m_FreeInstances.Back(); this->m_FreeInstances.PopBack(); this->m_ActiveInstances.PushBack(node); this->ResetParticle(node); return node; } //! @brief リース済みのParticleHandleを返却します。 //! @param[in] node 開放するトップノードです。 void ReleaseInstance(gfx::SceneNode* node) { ParticleHandle* topNode = ut::DynamicCast(node); if (topNode != NULL) { int activeCount = this->GetActiveSize(); this->m_ActiveInstances.EraseFind(topNode); NW_ASSERT(this->GetActiveSize() == activeCount - 1); this->m_FreeInstances.PushBack(topNode); } } //! @details :private void DestroyParticleHandle(ParticleHandle* node); void DestroyParticleNode(ParticleHandle* node) { return DestroyParticleHandle(node); } //@} //---------------------------------------- //! @name 取得 //@{ //! @brief インデックスでリース中のエフェクトを取得します。 //! @param[in] index インデックスです。 //! @return 該当するトップノードです。 ParticleHandle* GetActiveEffect(int index) { if (index < 0 || index >= this->GetActiveSize()) { return NULL; } return this->m_ActiveInstances[index]; } //! @brief ノードを初期化します。 //! @param[in] topNode 初期化するエフェクトをトップノードで指定します。 void ResetParticle(nw::gfx::SceneNode* topNode) { if (nw::ut::IsTypeOf(topNode)) { nw::gfx::ParticleModel* particleModel = nw::ut::DynamicCast(topNode); particleModel->ForeachParticleSet(nw::gfx::ParticleSetsClear()); particleModel->ParticleAnimFrameController().SetFrame(0); } else if (nw::ut::IsTypeOf(topNode)) { nw::gfx::ParticleEmitter* emitter = nw::ut::DynamicCast(topNode); emitter->Reset(); } nw::gfx::SceneNodeChildren::iterator end = topNode->GetChildEnd(); for (nw::gfx::SceneNodeChildren::iterator child = topNode->GetChildBegin(); child != end; ++child) { ResetParticle(*child); } } //@} //---------------------------------------- //! @name インスタンスプール //@{ //! @brief リース中を含む確保済みのインスタンスの総数を取得します。 int GetPoolSize() const { return this->GetFreeSize() + this->GetActiveSize(); } //! @brief リース中のでないフリーのインスタンスの数を取得します。 int GetFreeSize() const { return this->m_FreeInstances.Size(); } //! @brief リース中のインスタンスの数を取得します。 int GetActiveSize() const { return this->m_ActiveInstances.Size(); } //! @brief 指定数のインスタンスを新しくプールします。 void AddPool(int num) { for (int i = 0 ; i < num ; ++i) { ParticleHandle* node = this->Allocate(); this->m_FreeInstances.push_back(node); } } //! @brief リース中以外の全てのインスタンスを破棄します。 void FreePool() { while (this->m_FreeInstances.Size() > 0) { ParticleHandle* node = this->m_FreeInstances.Back(); this->m_FreeInstances.PopBack(); DestroyParticleHandle(node); } } //@} //---------------------------------------- //! @name シェーダバイナリ管理 //@{ //! @brief 外部シェーダファイルをロードします。 static void InitializeShaderBinary(const wchar_t* shaderFilePath, os::IAllocator* deviceAllocator); //! @brief ロードした外部シェーダファイルを破棄します。 static void FinalizeShaderBinary(); //@} private: //! @details :private ParticleHandle* Allocate(); int m_NextId; os::IAllocator* m_MainAllocator; os::IAllocator* m_DeviceAllocator; bool m_AutoAlocate; ut::MoveArray m_ResModels; ut::MoveArray m_ResEmitters; ut::MoveArray m_FreeInstances; ut::MoveArray m_ActiveInstances; gfx::ParticleContext* m_ParticleContext; static nw::demo::ResourceSet* m_ShaderResource; static os::IAllocator* m_ShaderAllocator; }; } // namespace demo } // namespace nw #endif // NW_DEMO_PARTICLE_H_