1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     demo_Particle.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: 25938 $
14  *---------------------------------------------------------------------------*/
15 
16 #ifndef NW_DEMO_PARTICLE_H_
17 #define NW_DEMO_PARTICLE_H_
18 
19 #include <nw/gfx.h>
20 #include <nw/ut.h>
21 
22 namespace nw
23 {
24 
25 namespace demo
26 {
27 
28 //! @brief パーティクル管理用のクラスです。
29 //! Transformノードに管理用の情報を付加しています。
30 class ParticleNode : public gfx::TransformNode
31 {
32 private:
33     NW_DISALLOW_COPY_AND_ASSIGN(ParticleNode);
34 
35 public:
36     NW_UT_RUNTIME_TYPEINFO;
37 
38     //----------------------------------------
39     //! @name 作成/破棄
40     //@{
41 
42     //! @brief シーンノードを動的に構築するためのクラスです。
43     //!
44     //! IsFixedSizeMemory の初期値は true です。false に変更すると、各種最大数の設定は無視されます。
45     class DynamicBuilder
46     {
47     public:
48         //! コンストラクタです。
DynamicBuilder()49         DynamicBuilder() {}
50         //! デストラクタです。
~DynamicBuilder()51         ~DynamicBuilder() {}
52 
53         //! @brief 生成時以外にもメモリを確保するかどうかのフラグを設定します。
54         //!
55         //!        true を指定すると、生成時のみ固定サイズのメモリ確保を行います。
56         //!
57         //!        false を指定すると、生成時以外にも必要に応じて動的にメモリ確保が行われます。
IsFixedSizeMemory(bool isFixedSizeMemory)58         DynamicBuilder& IsFixedSizeMemory(bool isFixedSizeMemory)
59         {
60             m_Description.isFixedSizeMemory = isFixedSizeMemory;
61             return *this;
62         }
63 
64         //! 子の最大数を設定します。
MaxChildren(int maxChildren)65         DynamicBuilder& MaxChildren(int maxChildren)
66         {
67             m_Description.maxChildren = maxChildren;
68             return *this;
69         }
70 
71         //! 管理できるコールバックの最大数を設定します。
MaxCallbacks(int maxCallbacks)72         DynamicBuilder& MaxCallbacks(int maxCallbacks)
73         {
74             m_Description.maxCallbacks = maxCallbacks;
75             return *this;
76         }
77 
78         //! @brief        シーンノードを生成します。
79         //!
80         //! @param[in]    allocator アロケータです。
81         //!
82         //! @return       生成したシーンノードを返します。
83         //!
84         ParticleNode* Create(os::IAllocator* allocator);
85 
86     private:
87         gfx::TransformNode::Description m_Description;
88     };
89 
90     //! @brief IDを取得します。
91     //!
92     //! @return IDです。
GetID()93     int GetID() const
94     {
95         return m_Id;
96     }
97 
98     //! @brief IDを設定します。
99     //!
100     //! @param[in] id 設定するIDです。
SetID(int id)101     void SetID(int id)
102     {
103         m_Id = id;
104     }
105 
106     //! @brief 位置を設定します。
107     //!
108     //! @param[in] x 設定する位置Xです。
109     //! @param[in] y 設定する位置Yです。
110     //! @param[in] z 設定する位置Zです。
SetTranslate(f32 x,f32 y,f32 z)111     void SetTranslate(f32 x, f32 y, f32 z)
112     {
113         this->Transform().SetTranslate(x, y, z);
114     }
115 
116     //! @brief 位置を設定します。
117     //!
118     //! @param[in] translate 設定する位置です。
SetTranslate(const nw::math::VEC3 & translate)119     void SetTranslate(const nw::math::VEC3& translate)
120     {
121         this->Transform().SetTranslate(translate);
122     }
123 
124     //! @brief 位置を取得します。
GetTranslate(nw::math::VEC3 * translate)125     void GetTranslate(nw::math::VEC3* translate) const
126     {
127         this->Transform().GetTranslate(translate);
128     }
129 
130     //! @brief 回転を設定します。
131     //!
132     //! @param[in] x 設定する回転値X(ラジアン)です。
133     //! @param[in] y 設定する回転値Y(ラジアン)です。
134     //! @param[in] z 設定する回転値Z(ラジアン)です。
SetRotate(f32 radX,f32 radY,f32 radZ)135     void SetRotate(f32 radX, f32 radY, f32 radZ)
136     {
137         this->Transform().SetRotateXYZ(radX, radY, radZ );
138     }
139 
140     //! @brief 拡縮を設定します。
141     //!
142     //! @param[in] x 設定する拡縮値Xです。
143     //! @param[in] y 設定する拡縮値Yです。
144     //! @param[in] z 設定する拡縮値Zです。
SetScale(f32 x,f32 y,f32 z)145     void SetScale(f32 x, f32 y, f32 z)
146     {
147         this->Transform().SetScale(x, y, z);
148     }
149 
150     //! @brief ステップフレームを設定します。
151     //!
152     //! @param[in] 設定するステップフレームです。
SetStepFrame(f32 stepFrame)153     void SetStepFrame(f32 stepFrame)
154     {
155         u32 emitterNum = this->GetParticleEmitterSize();
156         for (u32 i = 0; i < emitterNum; ++i)
157         {
158             nw::gfx::ParticleEmitter* emitter = this->GetParticleEmitter(i);
159             emitter->ParticleAnimFrameController().SetStepFrame(stepFrame);
160         }
161 
162         u32 modelNum = this->GetParticleModelSize();
163 
164         for (u32 i = 0; i < modelNum; ++i)
165         {
166             nw::gfx::ParticleModel* model = this->GetParticleModel(i);
167             model->ParticleAnimFrameController().SetStepFrame(stepFrame);
168         }
169     }
170 
171     //! @brief フレームを設定します。
172     //!
173     //! @param[in] 設定するフレームです。
SetFrame(f32 frame)174     void SetFrame(f32 frame)
175     {
176         u32 emitterNum = this->GetParticleEmitterSize();
177         for (u32 i = 0; i < emitterNum; ++i)
178         {
179             nw::gfx::ParticleEmitter* emitter = this->GetParticleEmitter(i);
180             emitter->ParticleAnimFrameController().SetFrame(frame);
181         }
182 
183         u32 modelNum = this->GetParticleModelSize();
184 
185         for (u32 i = 0; i < modelNum; ++i)
186         {
187             nw::gfx::ParticleModel* model = this->GetParticleModel(i);
188             model->ParticleAnimFrameController().SetFrame(frame);
189         }
190     }
191 
192     //! @brief リセットします。
193     //!        GPU処理中はコールできません。
194     //!
195     //! @param[in] リセットを行い、初期状態に戻します。
Reset()196     void Reset()
197     {
198         u32 emitterNum = this->GetParticleEmitterSize();
199         for (u32 i = 0; i < emitterNum; ++i)
200         {
201             nw::gfx::ParticleEmitter* emitter = this->GetParticleEmitter(i);
202             emitter->Reset();
203         }
204 
205         u32 modelNum = this->GetParticleModelSize();
206 
207         for (u32 i = 0; i < modelNum; ++i)
208         {
209             nw::gfx::ParticleModel* model = this->GetParticleModel(i);
210             model->ForeachParticleSet(nw::gfx::ParticleSetsClear());
211             model->ParticleAnimFrameController().SetFrame(0.f);
212         }
213     }
214 
215     //! @brief 放出量をセットします。
216     //!
217     //! @param[in] ratio 設定する放出量です。
SetEmissionRatio(f32 ratio)218     void SetEmissionRatio(f32 ratio)
219     {
220         u32 emitterNum = this->GetParticleEmitterSize();
221         for (u32 i = 0; i < emitterNum; ++i)
222         {
223             nw::gfx::ParticleEmitter* emitter = this->GetParticleEmitter(i);
224             nw::gfx::ResParticleEmitterParameter resParameter = emitter->GetResParticleEmitterParameterCopy();
225             NW_ASSERT(resParameter.IsValid());
226 
227             resParameter.SetEmissionRatio(ratio);
228         }
229     }
230 
231     //! @brief 現在のパーティクルの個数を取得します。
232     //!
233     //! @return 現在のパーティクルの個数です。
GetParticleSize()234     u32 GetParticleSize()
235     {
236         u32 particleNum = 0;
237 
238         u32 modelNum = this->GetParticleModelSize();
239 
240         for (u32 i = 0; i < modelNum; ++i)
241         {
242             nw::gfx::ParticleModel* model = this->GetParticleModel(i);
243             for (u32 j = 0; j < model->GetParticleSetsCount(); ++j )
244             {
245                 nw::gfx::ParticleSet* particleSet = model->GetParticleSets(j);
246                 particleNum += particleSet->GetParticleCollection()->GetCount();
247             }
248         }
249 
250         return particleNum;
251     }
252 
253     //! @brief 終了状態かどうかを取得します。
254     //!
255     //! @return 終了状態であれば、trueが返ります。
IsDone()256     bool IsDone()
257     {
258         bool isDone = true;
259 
260         u32 emitterNum = this->GetParticleEmitterSize();
261         for (u32 i = 0; i < emitterNum; ++i)
262         {
263             nw::gfx::ParticleEmitter* emitter = this->GetParticleEmitter(i);
264             if (emitter->IsAlive())
265             {
266                 isDone = false;
267             }
268         }
269 
270         u32 modelNum = this->GetParticleModelSize();
271 
272         for (u32 i = 0; i < modelNum; ++i)
273         {
274             nw::gfx::ParticleModel* model = this->GetParticleModel(i);
275             if (model->HasParticle())
276             {
277                 isDone = false;
278             }
279         }
280 
281         return isDone;
282     }
283 
284 
285     //! @brief 粒子のスケールのオフセット値設定します。
286     //!
287     //! @param[in] particleOffset 設定する粒子のスケールのオフセットです。
SetParticleScaleOffset(f32 particleOffset)288     void SetParticleScaleOffset( f32 particleOffset )
289     {
290         u32 modelNum = this->GetParticleModelSize();
291 
292         for (u32 i = 0; i < modelNum; ++i)
293         {
294             nw::gfx::ParticleModel* model = this->GetParticleModel(i);
295 
296             for (int j = 0; j < (int)model->GetParticleSetsCount(); ++j)
297             {
298                 nw::gfx::ParticleSet* particleSet = model->GetParticleSets(j);
299                 particleSet->SetScaleOffset(nw::math::VEC3(particleOffset, particleOffset, particleOffset));
300             }
301         }
302     }
303 
304     //! @brief 粒子のスケールのオフセット値設定します。
305     //!
306     //! @param[in] particleOffsetX 設定する粒子のスケールのオフセットXです。
307     //! @param[in] particleOffsetY 設定する粒子のスケールのオフセットYです。
308     //! @param[in] particleOffsetZ 設定する粒子のスケールのオフセットZです。
309     void SetParticleScaleOffset( f32 particleOffsetX, f32 particleOffsetY, f32 particleOffsetZ = 1.f )
310     {
311         u32 modelNum = this->GetParticleModelSize();
312 
313         for (u32 i = 0; i < modelNum; ++i)
314         {
315             nw::gfx::ParticleModel* model = this->GetParticleModel(i);
316 
317             for (int j = 0; j < (int)model->GetParticleSetsCount(); ++j)
318             {
319                 nw::gfx::ParticleSet* particleSet = model->GetParticleSets(j);
320                 particleSet->SetScaleOffset(nw::math::VEC3(particleOffsetX, particleOffsetY, particleOffsetZ));
321             }
322         }
323     }
324 
325     //! @brief パーティクルモデルのインスタンスを登録します。
326     //!
327     //! @param[in] 登録するパーティクルモデルのインスタンスです。
RegisterParticleModel(nw::gfx::ParticleModel * model)328     void RegisterParticleModel( nw::gfx::ParticleModel* model )
329     {
330         m_ModelInstances.PushBack(model);
331     }
332 
333     //! @brief パーティクルエミッタのインスタンスを登録します。
334     //!
335     //! @param[in] 登録するパーティクルエミッタのインスタンスです。
RegisterParticleEmitter(nw::gfx::ParticleEmitter * emitter)336     void RegisterParticleEmitter( nw::gfx::ParticleEmitter* emitter )
337     {
338         m_EmitterInstances.PushBack(emitter);
339     }
340 
341 
342     //! @brief 保持しているパーティクルモデル数を取得します。
343     //!
344     //! @return 保持しているパーティクルモデル数です。
GetParticleModelSize()345     u32 GetParticleModelSize() const
346     {
347         return m_ModelInstances.size();
348     }
349 
350     //! @brief 保持しているパーティクルエミッタ数を取得します。
351     //!
352     //! @return 保持しているパーティクルエミッタ数です。
GetParticleEmitterSize()353     u32 GetParticleEmitterSize() const
354     {
355         return m_EmitterInstances.size();
356     }
357 
358     //! @brief 保持しているパーティクルモデルインスタンスを取得します。
359     //!
360     //! @return パーティクルモデルのインスタンスです。
GetParticleModel(u32 index)361     nw::gfx::ParticleModel* GetParticleModel(u32 index)
362     {
363         if (this->GetParticleModelSize() > index)
364         {
365             return m_ModelInstances[index];
366         }
367         return NULL;
368     }
369 
370     //! @brief 保持しているパーティクルエミッタインスタンスを取得します。
371     //!
372     //! @return パーティクルエミッタのインスタンスです。
GetParticleEmitter(u32 index)373     nw::gfx::ParticleEmitter* GetParticleEmitter(u32 index)
374     {
375         if (this->GetParticleEmitterSize() > index)
376         {
377             return m_EmitterInstances[index];
378         }
379         return NULL;
380     }
381 
382 protected:
383     //----------------------------------------
384     //! @name コンストラクタ/デストラクタ
385     //@{
386 
387     //! コンストラクタです。
388     ParticleNode(
389         os::IAllocator* allocator,
390         gfx::ResTransformNode resObj,
391         const gfx::TransformNode::Description& description);
392 
393     //! デストラクタです。
~ParticleNode()394     virtual ~ParticleNode()
395     {
396 
397     }
398 
399     //@}
400 
401 private:
402     int m_Id;
403     ut::MoveArray<nw::gfx::ParticleModel*>      m_ModelInstances;
404     ut::MoveArray<nw::gfx::ParticleEmitter*>    m_EmitterInstances;
405 };
406 
407 
408 
409 
410 //! @brief 一表現のためのパーティクル一式です
411 // パーティクルを管理する機能はアプリケーション毎の実装になります。
412 // このクラスは参考のための実装例です。
413 // パフォーマンスよりも簡便性を重視した実装です。
414 class ParticleEffect
415 {
416 public:
417     //----------------------------------------
418     //! @name 初期化・終了処理
419     //@{
420 
421     //! @brief ParticleEffectクラスを生成します。
422     //!
423     //! @return 生成したParticleEffectクラスを返します。
424     //!
425     static ParticleEffect* Create(os::IAllocator* mainAllocator,
426                                   os::IAllocator* deviceAllocator,
427                                   bool autoAlocate,
428                                   gfx::ParticleContext* particleContext);
429 
430     //! @brief ParticleEffectクラスを破棄します。
431     //!
432     void Destroy();
433 
434 private:
435     //! @brief コンストラクタです。
436     //!
437     //! @param[in] mainAllocator メインメモリのアロケータです。
438     //! @param[in] deviceAllocator デバイスメモリのアロケータです。
439     //! @param[in] autoAlocate 足りない時に自動でアロケートすることを許可するフラグです。
440     //! @param[in] particleContext パーティクルコンテキストです。
441     //!
ParticleEffect(os::IAllocator * mainAllocator,os::IAllocator * deviceAllocator,bool autoAlocate,gfx::ParticleContext * particleContext)442     ParticleEffect(
443         os::IAllocator* mainAllocator,
444         os::IAllocator* deviceAllocator,
445         bool autoAlocate,
446         gfx::ParticleContext* particleContext
447         )
448         : m_NextId(0),
449         m_MainAllocator(mainAllocator),
450         m_DeviceAllocator(deviceAllocator),
451         m_AutoAlocate(autoAlocate),
452         m_ResModels(mainAllocator),
453         m_ResEmitters(mainAllocator),
454         m_FreeInstances(mainAllocator),
455         m_ActiveInstances(mainAllocator),
456         m_ParticleContext(particleContext)
457     {
458     }
459 
460     //! デストラクタです。
~ParticleEffect()461     ~ParticleEffect()
462     {
463         NW_ASSERT(this->GetActiveSize() == 0);
464         this->FreePool();
465     }
466 public:
467     //! @brief リソースのセットアップを行います。
468     //!
469     //! @param[in] useParticleMaterial パーティクルマテリアルを使用する場合はtrueを指定します。
470     void Setup(gfx::ResGraphicsFile resource, bool useParticleMaterial = false);
471 
472     //! @brief リソースから指定された名前のノードを管理対象として登録します。
473     //!
474     //! @param[in] resource グラフィックスリソースです。
475     //! @param[in] nodeNames 登録するノード一覧です。
476     void Register(gfx::ResGraphicsFile resource, const char** nodeNames);
477 
478     //! @brief 全てのリソースを管理対象として登録します。
479     //!
480     //! @param[in] resource グラフィックスリソースです。
481     void Register(gfx::ResGraphicsFile resource);
482 
483     //@}
484 
485     //----------------------------------------
486     //! @name 生成・破棄
487     //@{
488 
489     //! @brief ノードを生成します。
490     //! @return 生成したノードをまとめるトップノード(demo::ParticleNode)を返します。
LeaseInstance()491     ParticleNode* LeaseInstance()
492     {
493         if (this->GetFreeSize() == 0)
494         {
495             if (this->m_AutoAlocate)
496             {
497                 this->AddPool(1);
498             }
499             else
500             {
501                 return NULL;
502             }
503         }
504 
505         ParticleNode* node = this->m_FreeInstances.Back();
506         this->m_FreeInstances.PopBack();
507         this->m_ActiveInstances.PushBack(node);
508 
509         this->ResetParticle(node);
510 
511         return node;
512     }
513 
514     //! @brief ノードを解放します。
515     //! @param[in] node 開放するトップノードです。
ReleaseInstance(gfx::SceneNode * node)516     void ReleaseInstance(gfx::SceneNode* node)
517     {
518         ParticleNode* topNode = ut::DynamicCast<ParticleNode*>(node);
519 
520         if (topNode != NULL)
521         {
522             int activeCount = this->GetActiveSize();
523             this->m_ActiveInstances.EraseFind(topNode);
524             NW_ASSERT(this->GetActiveSize() == activeCount - 1);
525 
526             this->m_FreeInstances.PushBack(topNode);
527         }
528     }
529 
530     //@}
531 
532     //----------------------------------------
533     //! @name 取得
534     //@{
535 
536     //! @brief インデックスでリース中のエフェクトを取得します。
537     //! @param[in] index インデックスです。
538     //! @return 該当するトップノードです。
GetActiveEffect(int index)539     ParticleNode* GetActiveEffect(int index)
540     {
541         if (index < 0 || index >= this->GetActiveSize())
542         {
543             return NULL;
544         }
545 
546         return this->m_ActiveInstances[index];
547     }
548 
549     //! @brief ノードを初期化します。
550     //! @param[in] topNode 初期化するエフェクトをトップノードで指定します。
ResetParticle(nw::gfx::SceneNode * topNode)551     void ResetParticle(nw::gfx::SceneNode* topNode)
552     {
553         if (nw::ut::IsTypeOf<nw::gfx::ParticleModel>(topNode))
554         {
555             nw::gfx::ParticleModel* particleModel =
556                 nw::ut::DynamicCast<nw::gfx::ParticleModel*>(topNode);
557 
558             particleModel->ForeachParticleSet(nw::gfx::ParticleSetsClear());
559             particleModel->ParticleAnimFrameController().SetFrame(0);
560         }
561         else if (nw::ut::IsTypeOf<nw::gfx::ParticleEmitter>(topNode))
562         {
563             nw::gfx::ParticleEmitter* emitter =
564                 nw::ut::DynamicCast<nw::gfx::ParticleEmitter*>(topNode);
565 
566             emitter->Reset();
567         }
568 
569         nw::gfx::SceneNodeChildren::iterator end = topNode->GetChildEnd();
570         for (nw::gfx::SceneNodeChildren::iterator child = topNode->GetChildBegin();
571             child != end;
572             ++child)
573         {
574             ResetParticle(*child);
575         }
576     }
577 
578     //@}
579 
580     //----------------------------------------
581     //! @name インスタンスプール
582     //@{
583 
584     //! @brief リース中を含む確保済みのインスタンスの総数を取得します。
GetPoolSize()585     int GetPoolSize() const
586     {
587         return this->GetFreeSize() + this->GetActiveSize();
588     }
589 
590     //! @brief リース中のでないフリーのインスタンスの数を取得します。
GetFreeSize()591     int GetFreeSize() const
592     {
593         return this->m_FreeInstances.Size();
594     }
595 
596     //! @brief リース中のインスタンスの数を取得します。
GetActiveSize()597     int GetActiveSize() const
598     {
599         return this->m_ActiveInstances.Size();
600     }
601 
602     //! @brief 指定数のインスタンスを新しくプールします。
AddPool(int num)603     void AddPool(int num)
604     {
605         for (int i = 0 ; i < num ; ++i)
606         {
607             ParticleNode* node = this->Allocate();
608             this->m_FreeInstances.push_back(node);
609         }
610     }
611 
612     //! @brief リース中以外の全てのインスタンスを破棄します。
FreePool()613     void FreePool()
614     {
615         while (this->m_FreeInstances.Size() > 0)
616         {
617             ParticleNode* node = this->m_FreeInstances.Back();
618             this->m_FreeInstances.PopBack();
619             DestroyParticleNode(node);
620         }
621     }
622 
623     //@}
624 
625 private:
626     //! @details :private
627     ParticleNode* Allocate();
628 
629     //! @details :private
630     void DestroyParticleNode(ParticleNode* node);
631 
632     int m_NextId;
633 
634     os::IAllocator* m_MainAllocator;
635     os::IAllocator* m_DeviceAllocator;
636 
637     bool m_AutoAlocate;
638 
639     ut::MoveArray<gfx::ResParticleModel> m_ResModels;
640     ut::MoveArray<gfx::ResParticleEmitter> m_ResEmitters;
641 
642     ut::MoveArray<ParticleNode*> m_FreeInstances;
643     ut::MoveArray<ParticleNode*> m_ActiveInstances;
644 
645     gfx::ParticleContext* m_ParticleContext;
646 };
647 
648 
649 } // namespace demo
650 
651 } // namespace nw
652 
653 #endif // NW_DEMO_PARTICLE_H_
654