1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     gfx_ParticleCollection.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: 28677 $
14  *---------------------------------------------------------------------------*/
15 
16 #ifndef NW_GFX_PARTICLECOLLECTION_H_
17 #define NW_GFX_PARTICLECOLLECTION_H_
18 
19 #include <nw/gfx/gfx_GfxObject.h>
20 #include <nw/gfx/gfx_ParticleSet.h>
21 #include <nw/gfx/gfx_ParticleShape.h>
22 
23 #include <nw/gfx/res/gfx_ResParticleCollection.h>
24 
25 #include <nw/os.h>
26 
27 namespace nw
28 {
29 namespace gfx
30 {
31 
32 namespace internal
33 {
34 //! パーティクルで用いるショートベクタ演算の最大の処理単位です。
35 const int PARTICLE_SIMD_WIDTH_MAX = 8;
36 } // namespace internal
37 
38 class ParticleSet;
39 class ParticleCollection;
40 
41 //---------------------------------------------------------------------------
42 //! @brief        パーティクルの粒子の情報を表すクラスです。
43 //---------------------------------------------------------------------------
44 class ParticleCollection : public GfxObject
45 {
46 private:
47     NW_DISALLOW_COPY_AND_ASSIGN(ParticleCollection);
48 
49 public:
50     NW_UT_RUNTIME_TYPEINFO;
51 
52     //! @brief パーティクルのための属性情報です。
53     //! @details :private
54     struct ParticleAttribute
55     {
56         s32 m_Usage;
57         bool m_IsStream;
58         f32* m_Stream;
59     };
60 
61     //----------------------------------------
62     //! @name 作成/破棄
63     //@{
64 
65     //! @brief        リソースからParticleCollectionノードを生成します。
66     //!
67     //! @param[in]    parent 親のノードです。
68     //! @param[in]    resource リソースです。
69     //! @param[in]    mainAllocator メインアロケータです。
70     //! @param[in]    deviceAllocator デバイスアロケータです。
71     //! @param[in]    shape シェイプです。
72     //!
73     //! @return       生成されたParticleCollectionノードです。
74     //!
75     static ParticleCollection* Create(
76         ParticleSet* parent,
77         ResParticleCollection resource,
78         os::IAllocator* mainAllocator,
79         os::IAllocator* deviceAllocator,
80         ParticleShape* shape);
81 
82     //! @brief        生成時に必要なメモリサイズを取得します。
83     //! @param[in]    resource リソースです。
84     //! @param[in]    alignment 計算に用いるアライメントです。2 のべき乗である必要があります。
85     //! @return       必要なメモリサイズです。
86     static size_t GetMemorySize(
87         ResParticleCollection resource,
88         size_t alignment = os::IAllocator::DEFAULT_ALIGNMENT)
89     {
90         os::MemorySizeCalculator size(alignment);
91 
92         GetMemorySizeInternal(&size, resource);
93 
94         return size.GetSizeWithPadding(alignment);
95     }
96 
97     //! @details :private
98     static void GetMemorySizeInternal(
99         os::MemorySizeCalculator* pSize,
100         ResParticleCollection resource);
101 
102     //! @brief        生成時に必要なデバイスメモリサイズを取得します。
103     //! @param[in]    resource リソースです。
104     //! @param[in]    alignment 計算に用いるアライメントです。2 のべき乗である必要があります。
105     //! @return       必要なデバイスメモリサイズです。
106     static size_t GetDeviceMemorySize(
107         ResParticleCollection resource,
108         size_t alignment = os::IAllocator::DEFAULT_ALIGNMENT)
109     {
110         os::MemorySizeCalculator size(alignment);
111 
112         GetDeviceMemorySizeInternal(&size, resource);
113 
114         return size.GetSizeWithPadding(alignment);
115     }
116 
117     //! @details :private
118     static void GetDeviceMemorySizeInternal(
119         os::MemorySizeCalculator* pSize,
120         ResParticleCollection resource);
121 
122     //@}
123 
124     //----------------------------------------
125     //! @name 取得/設定
126     //@{
127 
128     //! @brief リソースを取得します。
129     //! @return リソースを返します。
GetResParticleCollection()130     ResParticleCollection GetResParticleCollection()
131     {
132         return this->m_ResParticleCollection;
133     }
134 
135     //! @brief リソースを取得します。
136     //! @return リソースを返します。
GetResParticleCollection()137     const ResParticleCollection GetResParticleCollection() const
138     {
139         return this->m_ResParticleCollection;
140     }
141 
142     //! @brief パーティクル数の上限を取得します。
143     //! @return パーティクル数の上限を返します。
GetCapacity()144     int GetCapacity() const
145     {
146         return this->m_Capacity;
147     }
148 
149     //! @brief パーティクル数を取得します。
150     //! @return 現在のパーティクル数を返します。
GetCount()151     int GetCount() const
152     {
153         return this->m_Count;
154     }
155 
156     //! @brief パーティクル数を設定します。
157     //! @return 現在のパーティクル数を設定します。
158     //! @details :private
SetCount(int count)159     void SetCount(int count)
160     {
161         this->m_Count = count;
162     }
163 
164     //! @brief 有効なパーティクルのインデックスの最小値を取得します。
165     //! @details :private
GetMinActiveIndex()166     u16 GetMinActiveIndex() const
167     {
168         return this->m_MinActiveIndex;
169     }
170 
171     //! @brief 有効なパーティクルのインデックスの最大値を取得します。
172     //! @details :private
GetMaxActiveIndex()173     u16 GetMaxActiveIndex() const
174     {
175         return this->m_MaxActiveIndex;
176     }
177 
178     //! @brief 有効なパーティクルのインデックスの最小値を設定します。
179     //! @details :private
SetMinActiveIndex(u16 minActiveIndex)180     void SetMinActiveIndex(u16 minActiveIndex)
181     {
182         this->m_MinActiveIndex = minActiveIndex;
183     }
184 
185     //! @brief 有効なパーティクルのインデックスの最大値を設定します。
186     //! @details :private
SetMaxActiveIndex(u16 maxActiveIndex)187     void SetMaxActiveIndex(u16 maxActiveIndex)
188     {
189         this->m_MaxActiveIndex = maxActiveIndex;
190     }
191 
192     //! @brief 指定された属性がストリームかどうかを調べます。
193     //! @param[in] usage 利用種別です。
194     //! @return ストリームの場合はtrueを返します。
IsStream(ParticleUsage usage)195     bool IsStream(ParticleUsage usage) const
196     {
197         NW_ASSERT(usage >= 0 && usage < PARTICLEUSAGE_COUNT);
198 
199         return this->m_IsStream[usage];
200     }
201 
202     //! @details :private
GetBufferSide()203     bool GetBufferSide() const
204     {
205         return this->m_BufferSide;
206     }
207 
208     //! @brief 指定された属性のストリームのポインタを取得します。
209     //! @param[in] usage 利用種別です。
210     //! @param[in] side バッファの使用目的です。
211     //! @return ストリームを返します。
GetStreamPtr(ParticleUsage usage,ParticleBuffer side)212     void* GetStreamPtr(ParticleUsage usage, ParticleBuffer side)
213     {
214         NW_ASSERT(usage >= 0 && usage < PARTICLEUSAGE_COUNT);
215 
216         if (!this->m_IsStream[usage])
217         {
218             return NULL;
219         }
220 
221         if (this->m_BufferSide)
222         {
223             side = (ParticleBuffer)(1 - side);
224         }
225 
226         return this->m_StreamPtr[usage][side];
227     }
228 
229     //! @brief 指定された属性のストリームのポインタを取得します。
230     //! @param[in] usage 利用種別です。
231     //! @param[in] side バッファの使用目的です。
232     //! @return ストリームを返します。
GetStreamPtr(ParticleUsage usage,ParticleBuffer side)233     const void* GetStreamPtr(ParticleUsage usage, ParticleBuffer side) const
234     {
235         NW_ASSERT(usage >= 0 && usage < PARTICLEUSAGE_COUNT);
236 
237         if (!this->m_IsStream[usage])
238         {
239             return NULL;
240         }
241 
242         if (this->m_BufferSide)
243         {
244             side = (ParticleBuffer)(1 - side);
245         }
246 
247         return this->m_StreamPtr[usage][side];
248     }
249 
250     //! @brief 指定された属性のパラメータへのポインタを取得します。
251     //! @param[in] usage 利用種別です。
252     //! @return パラメータへのポインタを返します。
GetParameterPtr(ParticleUsage usage)253     const void* GetParameterPtr(ParticleUsage usage) const
254     {
255         NW_ASSERT(usage >= 0 && usage < PARTICLEUSAGE_COUNT);
256 
257         if (this->m_IsStream[usage])
258         {
259             return NULL;
260         }
261 
262         return this->m_StreamPtr[usage][0];
263     }
264 
265     //! @brief 指定された属性のパラメータを設定します。
266     //! @details :private
267     //! @param[in] usage 利用種別です。
268     //! @param[in] side バッファの使用目的です。
269     //! @param[in] ptr 新しいパラメータへのポインタです。
270     void SetParameter(ParticleUsage usage, ParticleBuffer side, const f32* ptr);
271 
272     //! @brief PARTICLEUSAGE_LIFEのパラメータを設定します。
273     //! @param[in] ptr 新しいパラメータへのポインタです。
SetLifeParameter(const ParticleTime * ptr)274     void SetLifeParameter(const ParticleTime* ptr)
275     {
276         SetParameter(PARTICLEUSAGE_LIFE, PARTICLE_BUFFER_FRONT, (const f32*)ptr);
277     }
278 
279     //! @brief 指定された属性のストリームまたはパラメータを取得します。
280     //! @param[in] usage 利用種別です。
281     //! @param[out] stream ストリームの場合にポインタを格納するポインタです。
282     //! @param[out] param パラメータの場合にパラメータを格納するポインタです。
283     //! @param[in] side バッファの使用目的です。
284     //! @return ストリームの場合は、ストリームへのポインタを返します。パラメータの場合はパラメータを返します。
285     template <class T>
GetStreamOrParameter(ParticleUsage usage,T ** stream,T * param,ParticleBuffer side)286     NW_FORCE_INLINE bool GetStreamOrParameter(ParticleUsage usage, T** stream, T* param, ParticleBuffer side)
287     {
288         if (this->m_IsStream[usage])
289         {
290             if (this->m_BufferSide)
291             {
292                 side = (ParticleBuffer)(1 - side);
293             }
294 
295             *stream = (T*)this->m_StreamPtr[usage][side];
296         }
297         else
298         {
299             *stream = NULL;
300             if (param != NULL && m_StreamPtr[usage][0] != NULL)
301             {
302                 memcpy(param, this->m_StreamPtr[usage][0], sizeof(T));
303             }
304         }
305         return true;
306     }
307 
308     //! @brief 全てのストリームを初期化します。
309     //! @details ストリームの状態を初期化します。この関数は描画中には使用できません。
310     //!          GPUからストリームへのアクセスがない状態で使用してください。
311     void Clear();
312 
313     //! @brief 計算用と描画用の対象となるバッファを入れ替えます。
SwapBuffer()314     void SwapBuffer()
315     {
316         ParticleShape* shape = this->m_ParticleShape;
317 
318         ParticleBuffer side = PARTICLE_BUFFER_FRONT;
319         if (this->m_BufferSide)
320         {
321             side = (ParticleBuffer)(1 - side);
322         }
323 
324         this->m_BufferSide = !this->m_BufferSide;
325         shape->SetBufferSide(this->m_BufferSide);
326 
327         int size = this->GetMaxActiveIndex() + 1;
328         for (int usage = 0; usage < PARTICLEUSAGE_COUNT; ++usage)
329         {
330             void* src = this->m_StreamPtr[usage][side];
331             void* dst = this->m_StreamPtr[usage][(ParticleBuffer)(1 - side)];
332 
333             if (usage != PARTICLEUSAGE_ACTIVEINDEX)
334             {
335                 if (src != dst)
336                 {
337                     nw::os::MemCpy(dst, src, this->m_StreamStride[usage] * size);
338                 }
339             }
340         }
341     }
342 
343     //! @brief 生成済み全てのパーティクル粒子を削除します。
344     //! @detail 次回の削除処理で実際に削除されます。
345     //! その前に、チャイルドパーティクルの放出処理があれば、終了時として扱われます。
KillParticles()346     void KillParticles()
347     {
348         NW_NULL_ASSERT(m_ParticleSet);
349         ParticleModel* model = static_cast<ParticleModel*>(m_ParticleSet->GetParent());
350         NW_NULL_ASSERT(model);
351         ParticleTime time = model->ParticleAnimFrameController().GetFrame();
352 
353         u16* activeIndex = (u16*)this->GetStreamPtr(
354             nw::gfx::PARTICLEUSAGE_ACTIVEINDEX,nw::gfx::PARTICLE_BUFFER_FRONT);
355         NW_NULL_ASSERT(activeIndex);
356 
357 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
358         ParticleTime* negtime =
359             (ParticleTime*)this->GetStreamPtr(PARTICLEUSAGE_NEG_TIMELIMIT, PARTICLE_BUFFER_FRONT);
360         NW_NULL_ASSERT(negtime);
361 
362         for (s32 i = 0; i < m_Count; ++i)
363         {
364             int convertedIndex = activeIndex[i];
365 
366             // -timeで消えるが念のためマージンを設ける
367             negtime[convertedIndex] = -time + 1.0f;
368         }
369 #else
370         ParticleTime* birth =
371             (ParticleTime*)this->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
372         NW_NULL_ASSERT(birth);
373 
374         ParticleTime lifeParam;
375         if (this->IsStream(PARTICLEUSAGE_LIFE))
376         {
377             ParticleTime* life =
378                 (ParticleTime*)this->GetStreamPtr(PARTICLEUSAGE_LIFE, PARTICLE_BUFFER_FRONT);
379 
380             for (s32 i = 0; i < m_Count; ++i)
381             {
382                 int convertedIndex = activeIndex[i];
383                 lifeParam = life[convertedIndex];
384                 birth[index] = time - lifeParam - 1;
385             }
386         }
387         else
388         {
389             lifeParam = *(ParticleTime*)this->GetParameterPtr(PARTICLEUSAGE_LIFE);
390             for (s32 i = 0; i < m_Count; ++i)
391             {
392                 int convertedIndex = activeIndex[i];
393                 birth[convertedIndex] = time - lifeParam - 1;
394             }
395         }
396 #endif
397     }
398 
399     //! @brief 指定したインデックスのパーティクル粒子を削除します。
400     //! @detail 次回の削除処理で実際に削除されます。
401     //! その前に、チャイルドパーティクルの放出処理があれば、終了時として扱われます。
402     //! @param[in] index ACTIVEINDEXが指し示すインデックスです。
KillParticle(int index)403     void KillParticle(int index)
404     {
405         NW_NULL_ASSERT(m_ParticleSet);
406         ParticleModel* model = static_cast<ParticleModel*>(m_ParticleSet->GetParent());
407         NW_NULL_ASSERT(model);
408         ParticleTime time = model->ParticleAnimFrameController().GetFrame();
409 
410 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
411         ParticleTime* negtime =
412             (ParticleTime*)this->GetStreamPtr(PARTICLEUSAGE_NEG_TIMELIMIT, PARTICLE_BUFFER_FRONT);
413         NW_NULL_ASSERT(negtime);
414 
415         negtime[index] = -time + 1.0f; // -timeで消えるが念のためマージンを設ける
416 #else
417         ParticleTime* birth =
418             (ParticleTime*)this->GetStreamPtr(PARTICLEUSAGE_BIRTH, PARTICLE_BUFFER_FRONT);
419         NW_NULL_ASSERT(birth);
420 
421         ParticleTime lifeParam;
422         if (this->IsStream(PARTICLEUSAGE_LIFE))
423         {
424             ParticleTime* life =
425                 (ParticleTime*)this->GetStreamPtr(PARTICLEUSAGE_LIFE, PARTICLE_BUFFER_FRONT);
426             lifeParam = life[index];
427         }
428         else
429         {
430             lifeParam = *(ParticleTime*)this->GetParameterPtr(PARTICLEUSAGE_LIFE);
431         }
432 
433         birth[index] = time - lifeParam - 1;
434 #endif
435     }
436 
437     //@}
438 
439 protected:
440     //----------------------------------------
441     //! @name コンストラクタ/デストラクタ
442     //@{
443 
444     //! @brief コンストラクタです。
445     ParticleCollection(
446         os::IAllocator* allocator,
447         os::IAllocator* deviceAllocator,
448         void* deviceMemory,
449         ResParticleCollection resObj);
450 
451     //! @brief デストラクタです。
452     virtual ~ParticleCollection();
453 
454     //@}
455 private:
456     ParticleAttribute m_ParticleAttribute[PARTICLEUSAGE_COUNT];
457     int m_Capacity;
458     int m_Count;
459     bool m_BufferSide;
460     bool m_IsStream[PARTICLEUSAGE_COUNT];
461     void* m_StreamPtr[PARTICLEUSAGE_COUNT][2];
462     int m_StreamStride[PARTICLEUSAGE_COUNT];
463 
464     u16 m_MinActiveIndex;
465     u16 m_MaxActiveIndex;
466 
467     ResParticleCollection m_ResParticleCollection;
468     ParticleShape* m_ParticleShape;
469     ParticleSet* m_ParticleSet;
470 
471     os::IAllocator* m_DeviceAllocator;
472     void* m_DeviceMemory;
473 };
474 
475 } // namespace gfx
476 } // namespace nw
477 
478 #endif // NW_GFX_PARTICLECOLLECTION_H_
479