1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     gfx_ParticleEmitter.cpp
4 
5   Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain proprietary
8   information of Nintendo and/or its licensed developers and are protected by
9   national and international copyright laws. They may not be disclosed to third
10   parties or copied or duplicated in any form, in whole or in part, without the
11   prior written consent of Nintendo.
12 
13   The content herein is highly confidential and should be handled accordingly.
14 
15   $Revision: 31311 $
16  *---------------------------------------------------------------------------*/
17 
18 #include "precompiled.h"
19 
20 #include <nw/gfx/gfx_ParticleContext.h>
21 #include <nw/gfx/gfx_ParticleEmitter.h>
22 #include <nw/gfx/gfx_ParticleRandom.h>
23 #include <nw/gfx/gfx_ISceneVisitor.h>
24 
25 #include <nw/ut/ut_ResUtil.h>
26 #include <nw/ut/ut_ResDictionary.h>
27 
28 namespace nw
29 {
30 namespace gfx
31 {
32 
33 NW_UT_RUNTIME_TYPEINFO_DEFINITION(ParticleEmitter, TransformNode);
34 
35 void
GetMemorySizeInternal(os::MemorySizeCalculator * pSize,ResParticleEmitter resNode,const ParticleEmitter::Description & description)36 ParticleEmitter::GetMemorySizeInternal(
37     os::MemorySizeCalculator* pSize,
38     ResParticleEmitter resNode,
39     const ParticleEmitter::Description& description)
40 {
41     os::MemorySizeCalculator& size = *pSize;
42 
43     // リソースのコピー
44     ResParticleEmitterParameterData* resParameterData = NULL;
45     if (resNode.GetIsResourceCopyEnabled())
46     {
47         size += sizeof(ResParticleEmitterParameterData);
48     }
49 
50     // フォームのリソースのコピー
51     ResParticleFormData* resFormData = NULL;
52     const ResParticleForm resForm = resNode.GetParticleForm();
53     if (resForm.IsValid() && resForm.GetIsResourceCopyEnabled())
54     {
55         switch(resForm.GetTypeInfo())
56         {
57         case ResParticleCubeForm::TYPE_INFO:
58             size += sizeof(ResParticleCubeFormData);
59             break;
60         case ResParticleCylinderForm::TYPE_INFO:
61             size += sizeof(ResParticleCylinderFormData);
62             break;
63         case ResParticleDiscForm::TYPE_INFO:
64             size += sizeof(ResParticleDiscFormData);
65             break;
66         case ResParticlePointForm::TYPE_INFO:
67             size += sizeof(ResParticlePointFormData);
68             break;
69         case ResParticleRectangleForm::TYPE_INFO:
70             size += sizeof(ResParticleRectangleFormData);
71             break;
72         case ResParticleSphereForm::TYPE_INFO:
73             size += sizeof(ResParticleSphereFormData);
74             break;
75         default:
76             NW_FATAL_ERROR("unknown form type");
77         }
78     }
79 
80     size += sizeof(ParticleEmitter);
81 
82     TransformNode::GetMemorySizeForInitialize(pSize, resNode, description);
83 }
84 
85 //----------------------------------------
86 ParticleEmitter*
Create(SceneNode * parent,ResSceneObject resource,const ParticleEmitter::Description & description,os::IAllocator * allocator)87 ParticleEmitter::Create(
88     SceneNode* parent,
89     ResSceneObject resource,
90     const ParticleEmitter::Description& description,
91     os::IAllocator* allocator
92 )
93 {
94     NW_NULL_ASSERT(allocator);
95 
96     ResParticleEmitter resNode = ResDynamicCast<ResParticleEmitter>(resource);
97     NW_ASSERT(resNode.IsValid());
98 
99     // リソースのコピー
100     ResParticleEmitterParameterData* resParameterData = NULL;
101     if (resNode.GetIsResourceCopyEnabled())
102     {
103         void* resourceMemory = allocator->Alloc(sizeof(ResParticleEmitterParameterData));
104         if (resourceMemory == NULL)
105         {
106             return NULL;
107         }
108 
109         resParameterData = new(resourceMemory) ResParticleEmitterParameterData;
110         ::nw::os::MemCpy(
111             resParameterData,
112             &resNode.ptr()->m_IsResourceCopyEnabled,
113             sizeof(ResParticleEmitterParameterData));
114     }
115 
116     // フォームのリソースのコピー
117     ResParticleFormData* resFormData = NULL;
118     const ResParticleForm resForm = resNode.GetParticleForm();
119     if (resForm.IsValid() && resForm.GetIsResourceCopyEnabled())
120     {
121         int size = 0;
122 
123         switch(resForm.GetTypeInfo())
124         {
125         case ResParticleCubeForm::TYPE_INFO:
126             {
127                 size = sizeof(ResParticleCubeFormData);
128                 void* resourceMemory = allocator->Alloc(size);
129                 if (resourceMemory != NULL)
130                 {
131                     resFormData = new(resourceMemory) ResParticleCubeFormData;
132                 }
133             }
134             break;
135         case ResParticleCylinderForm::TYPE_INFO:
136             {
137                 size = sizeof(ResParticleCylinderFormData);
138                 void* resourceMemory = allocator->Alloc(size);
139                 if (resourceMemory != NULL)
140                 {
141                     resFormData = new(resourceMemory) ResParticleCylinderFormData;
142                 }
143             }
144             break;
145         case ResParticleDiscForm::TYPE_INFO:
146             {
147                 size = sizeof(ResParticleDiscFormData);
148                 void* resourceMemory = allocator->Alloc(size);
149                 if (resourceMemory != NULL)
150                 {
151                     resFormData = new(resourceMemory) ResParticleDiscFormData;
152                 }
153             }
154             break;
155         case ResParticlePointForm::TYPE_INFO:
156             {
157                 size = sizeof(ResParticlePointFormData);
158                 void* resourceMemory = allocator->Alloc(size);
159                 if (resourceMemory != NULL)
160                 {
161                     resFormData = new(resourceMemory) ResParticlePointFormData;
162                 }
163             }
164             break;
165         case ResParticleRectangleForm::TYPE_INFO:
166             {
167                 size = sizeof(ResParticleRectangleFormData);
168                 void* resourceMemory = allocator->Alloc(size);
169                 if (resourceMemory != NULL)
170                 {
171                     resFormData = new(resourceMemory) ResParticleRectangleFormData;
172                 }
173             }
174             break;
175         case ResParticleSphereForm::TYPE_INFO:
176             {
177                 size = sizeof(ResParticleSphereFormData);
178                 void* resourceMemory = allocator->Alloc(size);
179                 if (resourceMemory != NULL)
180                 {
181                     resFormData = new(resourceMemory) ResParticleSphereFormData;
182                 }
183             }
184             break;
185         default:
186             NW_FATAL_ERROR("unknown form type");
187         }
188 
189         if (resFormData == NULL)
190         {
191             if (resParameterData != NULL)
192             {
193                 allocator->Free(resParameterData);
194                 return NULL;
195             }
196         }
197         else
198         {
199             ::nw::os::MemCpy(
200                 resFormData,
201                 resForm.ptr(),
202                 size);
203         }
204     }
205 
206     void* memory = allocator->Alloc(sizeof(ParticleEmitter));
207     if (memory == NULL)
208     {
209         if (resParameterData != NULL)
210         {
211             allocator->Free(resParameterData);
212         }
213 
214         if (resFormData != NULL)
215         {
216             allocator->Free(resFormData);
217         }
218 
219         return NULL;
220     }
221 
222     ParticleEmitter* node = new(memory) ParticleEmitter(
223         allocator,
224         resNode,
225         description,
226         ResParticleEmitterParameter(resParameterData),
227         ResParticleForm(resFormData));
228 
229     {
230         Result result = node->Initialize(allocator);
231         if (!result.IsSuccess())
232         {
233             SafeDestroy(node);
234             return NULL;
235         }
236     }
237 
238     if (parent)
239     {
240         bool result = parent->AttachChild(node);
241         NW_ASSERT(result);
242     }
243 
244     return node;
245 }
246 
247 //----------------------------------------
ParticleEmitter(os::IAllocator * allocator,ResParticleEmitter resObj,const ParticleEmitter::Description & description,ResParticleEmitterParameter resParameterObj,ResParticleForm resFormObj)248 ParticleEmitter::ParticleEmitter(
249     os::IAllocator* allocator,
250     ResParticleEmitter resObj,
251     const ParticleEmitter::Description& description,
252     ResParticleEmitterParameter resParameterObj,
253     ResParticleForm resFormObj
254 )
255 : TransformNode(allocator, resObj, description),
256 m_IsFirstEmission(true),
257 m_EmissionCount(0),
258 m_NextEmissionTime(0),
259 m_ParticleSet(NULL),
260 m_ParticleAnimFrameController(0, 16777215, anim::PlayPolicy_Loop),
261 m_ResParameter(resParameterObj),
262 m_ResForm(resFormObj)
263 {
264 }
265 
266 //----------------------------------------
~ParticleEmitter()267 ParticleEmitter::~ParticleEmitter()
268 {
269     if (this->m_ResParameter.IsValid())
270     {
271         this->GetAllocator().Free(this->m_ResParameter.ptr());
272     }
273 
274     if (this->m_ResForm.IsValid())
275     {
276         this->GetAllocator().Free(this->m_ResForm.ptr());
277     }
278 }
279 
280 //----------------------------------------
281 void
Accept(ISceneVisitor * visitor)282 ParticleEmitter::Accept(
283     ISceneVisitor* visitor
284 )
285 {
286     visitor->VisitParticleEmitter(this);
287     AcceptChildren(visitor);
288 }
289 
290 int
GetEmissionCount(f32 prevTime,f32 time)291 ParticleEmitter::GetEmissionCount(
292     f32 prevTime,
293     f32 time
294 )
295 {
296 #if 0 // 1.1.0 で変更しました。
297     NW_UNUSED_VARIABLE(prevTime);
298 
299     ResParticleEmitterParameter resource = this->GetResParticleEmitterParameterCopy(false);
300     if (!resource.IsValid())
301     {
302         return 0;
303     }
304 
305     f32 cookedTime = time - resource.GetEmissionStart() - 1;
306 
307     if (cookedTime < 0)
308     {
309         return 0;
310     }
311 
312     if (!resource.GetEmissionSpanInfinity() && cookedTime >= resource.GetEmissionSpan())
313     {
314         return 0;
315     }
316 
317     while (this->m_NextEmissionTime <= cookedTime)
318     {
319         f32 c = resource.GetEmissionRatio();
320         c *= 1.0f + resource.GetEmissionRatioRandom() * this->m_ParticleRandom.NextFloatSignedOne();
321         if (c < 0)
322         {
323             c = 0;
324         }
325 
326         this->m_EmissionCount += c;
327 
328         int t = resource.GetEmissionInterval();
329         t = (int)(t * (1.0f + resource.GetEmissionIntervalRandom() * this->m_ParticleRandom.NextFloatSignedOne()));
330 
331         // 最速でも次のフレームまで出さない
332         if (t < 1)
333         {
334             t = 1;
335         }
336 
337         this->m_NextEmissionTime += t;
338     }
339 
340     if (this->m_IsFirstEmission)
341     {
342         if (resource.GetEmissionRatio() != 0 && this->m_EmissionCount > 0.0f)
343         {
344             this->m_IsFirstEmission = false;
345             if (this->m_EmissionCount < 1.0f)
346             {
347                 this->m_EmissionCount = 1.0f;
348             }
349         }
350     }
351 
352     int count = (int)this->m_EmissionCount;
353     this->m_EmissionCount -= count;
354 
355     return count;
356 #else
357     // 逆再生時は放出しない
358     if (prevTime >= time)
359     {
360         return 0;
361     }
362 
363     ResParticleEmitterParameter resource = this->GetResParticleEmitterParameterCopy(false);
364     NW_ASSERT(resource.IsValid());
365 
366     f32 cookedPrevTime = prevTime - resource.GetEmissionStart() - 1;
367 
368     if (!resource.GetEmissionSpanInfinity() && cookedPrevTime >= resource.GetEmissionSpan() - 1)
369     {
370         return 0;
371     }
372 
373     f32 cookedTime = time - resource.GetEmissionStart() - 1;
374 
375     if (cookedTime < 0)
376     {
377         return 0;
378     }
379 
380     while (this->m_NextEmissionTime <= cookedTime)
381     {
382         f32 c = resource.GetEmissionRatio();
383         c *= 1.0f + resource.GetEmissionRatioRandom() * this->m_ParticleRandom.NextFloatSignedOne();
384         if (c < 0)
385         {
386             c = 0;
387         }
388 
389         this->m_EmissionCount += c;
390 
391         int t = resource.GetEmissionInterval();
392         t = (int)(t * (1.0f + resource.GetEmissionIntervalRandom() * this->m_ParticleRandom.NextFloatSignedOne()));
393 
394         // 最速でも次のフレームまで出さない
395         if (t < 1)
396         {
397             t = 1;
398         }
399 
400         this->m_NextEmissionTime += t;
401     }
402 
403     if (this->m_IsFirstEmission)
404     {
405         if (resource.GetEmissionRatio() != 0 && this->m_EmissionCount > 0.0f)
406         {
407             this->m_IsFirstEmission = false;
408             if (this->m_EmissionCount < 1.0f)
409             {
410                 this->m_EmissionCount = 1.0f;
411             }
412         }
413     }
414 
415     int count = (int)this->m_EmissionCount;
416     this->m_EmissionCount -= count;
417 
418     return count;
419 #endif
420 }
421 
422 int
Emission(ParticleContext * particleContext)423 ParticleEmitter::Emission(
424     ParticleContext* particleContext
425 )
426 {
427     NW_UNUSED_VARIABLE(particleContext);
428 
429     if (this->m_ParticleSet == NULL)
430     {
431         return 0;
432     }
433 
434     f32 prevTime = m_ParticleAnimFrameController.GetAnimFrame().GetLastFrame();
435     f32 time = m_ParticleAnimFrameController.GetFrame();
436 
437     int emissionCount = GetEmissionCount(prevTime, time);
438 
439     if (emissionCount < 1)
440     {
441         return 0;
442     }
443 
444     ResParticleForm resForm = this->GetResParticleFormCopy(false);
445     NW_ASSERT(resForm.IsValid());
446 
447 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
448     nw::math::VEC3* positions = particleContext->GetEmissionPositionWork(emissionCount);
449 
450     if (emissionCount > particleContext->GetEmissionWorkCapacity())
451     {
452         emissionCount = particleContext->GetEmissionWorkCapacity();
453     }
454 #else
455     ParticleCollection* targetCollection = m_ParticleSet->GetParticleCollection();
456     bool targetIsAscendingOrder = m_ParticleSet->IsAscendingOrder();
457 
458     u16* activeIndex = (u16*)targetCollection->GetStreamPtr(PARTICLEUSAGE_ACTIVEINDEX, PARTICLE_BUFFER_FRONT);
459     NW_NULL_ASSERT(activeIndex);
460     const int startIndex = targetIsAscendingOrder ? targetCollection->GetCount() : targetCollection->GetCapacity() - targetCollection->GetCount() - 1;
461     activeIndex += startIndex;
462 
463     const int incrIndex = targetIsAscendingOrder ? 1 : -1;
464 
465     nw::math::VEC3* targetTranslate = (nw::math::VEC3*)targetCollection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT);
466     NW_NULL_ASSERT(targetTranslate);
467 
468     emissionCount = m_ParticleSet->AddParticles(emissionCount);
469 #endif
470 
471     switch(resForm.GetTypeInfo())
472     {
473     case ResParticleCubeForm::TYPE_INFO:
474         {
475             const ResParticleCubeForm cubeForm = ResStaticCast<ResParticleCubeForm>(resForm);
476 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
477             this->CalcCubeForm(cubeForm, emissionCount, &this->m_ParticleRandom, positions);
478 #else
479             this->CalcCubeForm(cubeForm, emissionCount, &this->m_ParticleRandom,
480                 activeIndex, incrIndex, targetTranslate);
481 #endif
482         }
483         break;
484     case ResParticleCylinderForm::TYPE_INFO:
485         {
486             const ResParticleCylinderForm cylinderForm = ResStaticCast<ResParticleCylinderForm>(resForm);
487 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
488             this->CalcCylinderForm(cylinderForm, emissionCount, &this->m_ParticleRandom, positions);
489 #else
490             this->CalcCylinderForm(cylinderForm, emissionCount, &this->m_ParticleRandom,
491                 activeIndex, incrIndex, targetTranslate);
492 #endif
493         }
494         break;
495     case ResParticleDiscForm::TYPE_INFO:
496         {
497             const ResParticleDiscForm discForm = ResStaticCast<ResParticleDiscForm>(resForm);
498 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
499             this->CalcDiscForm(discForm, emissionCount, &this->m_ParticleRandom, positions);
500 #else
501             this->CalcDiscForm(discForm, emissionCount, &this->m_ParticleRandom,
502                 activeIndex, incrIndex, targetTranslate);
503 #endif
504         }
505         break;
506     case ResParticlePointForm::TYPE_INFO:
507         {
508             const ResParticlePointForm pointForm = ResStaticCast<ResParticlePointForm>(resForm);
509 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
510             this->CalcPointForm(pointForm, emissionCount, &this->m_ParticleRandom, positions);
511 #else
512             this->CalcPointForm(pointForm, emissionCount, &this->m_ParticleRandom,
513                 activeIndex, incrIndex, targetTranslate);
514 #endif
515         }
516         break;
517     case ResParticleRectangleForm::TYPE_INFO:
518         {
519             const ResParticleRectangleForm rectangleForm = ResStaticCast<ResParticleRectangleForm>(resForm);
520 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
521             this->CalcRectangleForm(rectangleForm, emissionCount, &this->m_ParticleRandom, positions);
522 #else
523             this->CalcRectangleForm(rectangleForm, emissionCount, &this->m_ParticleRandom,
524                 activeIndex, incrIndex, targetTranslate);
525 #endif
526         }
527         break;
528     case ResParticleSphereForm::TYPE_INFO:
529         {
530             const ResParticleSphereForm sphereForm = ResStaticCast<ResParticleSphereForm>(resForm);
531 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
532             this->CalcSphereForm(sphereForm, emissionCount, &this->m_ParticleRandom, positions);
533 #else
534             this->CalcSphereForm(sphereForm, emissionCount, &this->m_ParticleRandom,
535                 activeIndex, incrIndex, targetTranslate);
536 #endif
537         }
538         break;
539     default:
540         NW_FATAL_ERROR("unknown form type");
541     }
542 
543 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
544     m_ParticleSet->AddParticles(this->WorldMatrix(), positions, NULL, NULL, emissionCount);
545 #else
546     {
547         ParticleModel* model = static_cast<ParticleModel*>(m_ParticleSet->GetParent());
548         NW_NULL_ASSERT(model);
549         ParticleTime targetTime = model->ParticleAnimFrameController().GetFrame();
550 
551         m_ParticleSet->InitializeParticles(startIndex, emissionCount, incrIndex, targetTime);
552     }
553 
554     if (m_ParticleSet->WorldMatrix() != this->WorldMatrix())
555     {
556         nw::math::MTX34 transformMatrix;
557         nw::math::MTX34Mult(&transformMatrix, m_ParticleSet->InverseWorldMatrix(), this->WorldMatrix());
558 
559         nw::math::VEC3* translate = (nw::math::VEC3*)targetCollection->GetStreamPtr(PARTICLEUSAGE_TRANSLATE, PARTICLE_BUFFER_FRONT);
560 
561         nw::math::VEC3* velocity = (nw::math::VEC3*)targetCollection->GetStreamPtr(PARTICLEUSAGE_VELOCITY, PARTICLE_BUFFER_FRONT);
562 
563         for (int i = 0; i < emissionCount; ++i)
564         {
565             int dstIndex = *activeIndex;
566             activeIndex += incrIndex;
567 
568             nw::math::VEC3Transform(&translate[dstIndex], transformMatrix, translate[dstIndex]);
569             nw::math::VEC3TransformNormal(&velocity[dstIndex], transformMatrix, velocity[dstIndex]);
570         }
571     }
572 #endif
573 
574     return emissionCount;
575 }
576 
577 void
CalcCubeForm(const ResParticleCubeForm & cubeForm,int emissionCount,ParticleRandom * random,nw::math::VEC3 * positions)578 ParticleEmitter::CalcCubeForm(
579     const ResParticleCubeForm& cubeForm,
580     int emissionCount,
581     ParticleRandom* random,
582 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
583     nw::math::VEC3* positions
584 #else
585     u16* activeIndex,
586     int incrIndex,
587     nw::math::VEC3* targetTranslate
588 #endif
589 )
590 {
591     const f32 Epsilon = 1e-5f;
592 
593     nw::math::VEC3 cookedScale(cubeForm.GetScale());
594     if (nw::math::FAbs(cookedScale.x) < Epsilon)
595     {
596         cookedScale.x = Epsilon;
597     }
598 
599     if (nw::math::FAbs(cookedScale.y) < Epsilon)
600     {
601         cookedScale.y = Epsilon;
602     }
603 
604     if (nw::math::FAbs(cookedScale.z) < Epsilon)
605     {
606         cookedScale.z = Epsilon;
607     }
608 
609     // TBD:等間隔
610     for (int i = 0; i < emissionCount; ++i)
611     {
612         nw::math::VEC3 position(0.0f, 0.0f, 0.0f);
613 
614         if (cubeForm.GetInner() == 0.0f)
615         {
616             position.x = random->NextFloatSignedOne();
617             position.y = random->NextFloatSignedOne();
618             position.z = random->NextFloatSignedOne();
619 
620             position.x *= cookedScale.x;
621             position.y *= cookedScale.y;
622             position.z *= cookedScale.z;
623         }
624         else if (cubeForm.GetInner() == 1.0f)
625         {
626             f32 d = random->NextFloat() *
627                 ((cookedScale.x * cookedScale.y) +
628                 (cookedScale.y * cookedScale.z) +
629                 (cookedScale.z * cookedScale.x));
630             int sign;
631             if (random->Next(2) == 0)
632             {
633                 sign = 1;
634             }
635             else
636             {
637                 sign = -1;
638             }
639 
640             f32 r = random->NextFloatSignedOne();
641             f32 s = random->NextFloatSignedOne();
642 
643             if (d < cookedScale.x * cookedScale.y)
644             {
645                 position.Set(r, s, (f32)sign);
646             }
647             else if (d < (cookedScale.x * cookedScale.y) + (cookedScale.y * cookedScale.z))
648             {
649                 position.Set((f32)sign, r, s);
650             }
651             else
652             {
653                 position.Set(r, (f32)sign, s);
654             }
655 
656             position.x *= cookedScale.x;
657             position.y *= cookedScale.y;
658             position.z *= cookedScale.z;
659         }
660         else
661         {
662             f32 xyz = cookedScale.x * cookedScale.y * cookedScale.z;
663             f32 topbottom = xyz * (1.0f - cubeForm.GetInner());
664             f32 frontback = topbottom * cubeForm.GetInner();
665             f32 leftright = frontback * cubeForm.GetInner();
666 
667             f32 f = random->NextFloat() * (topbottom + frontback + leftright);
668 
669             position.x = random->NextFloatSignedOne();
670             position.y = random->NextFloatSignedOne();
671             position.z = random->NextFloatSignedOne();
672 
673             if (f < topbottom)
674             {
675                 position.y *= 1.0f - cubeForm.GetInner();
676                 if (position.y >= 0)
677                 {
678                     position.y = 1.0f - position.y;
679                 }
680                 else
681                 {
682                     position.y = -1.0f - position.y;
683                 }
684             }
685             else if (f < topbottom + frontback)
686             {
687                 position.y *= cubeForm.GetInner();
688                 position.z *= 1.0f - cubeForm.GetInner();
689                 if (position.z >= 0)
690                 {
691                     position.z = 1.0f - position.z;
692                 }
693                 else
694                 {
695                     position.z = -1.0f - position.z;
696                 }
697             }
698             else
699             {
700                 position.x *= 1.0f - cubeForm.GetInner();
701                 if (position.x >= 0)
702                 {
703                     position.x = 1.0f - position.x;
704                 }
705                 else
706                 {
707                     position.x = -1.0f - position.x;
708                 }
709             }
710 
711             position.x *= cookedScale.x;
712             position.y *= cookedScale.y;
713             position.z *= cookedScale.z;
714         }
715 
716 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
717         positions[i].Set(position.x, position.y, position.z);
718 #else
719         int index = *activeIndex;
720         activeIndex += incrIndex;
721         targetTranslate[index].Set(position.x, position.y, position.z);
722 #endif
723     }
724 }
725 
726 void
CalcCylinderForm(const ResParticleCylinderForm & cylinderForm,int emissionCount,ParticleRandom * random,nw::math::VEC3 * positions)727 ParticleEmitter::CalcCylinderForm(
728     const ResParticleCylinderForm& cylinderForm,
729     int emissionCount,
730     ParticleRandom* random,
731 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
732     nw::math::VEC3* positions
733 #else
734     u16* activeIndex,
735     int incrIndex,
736     nw::math::VEC3* targetTranslate
737 #endif
738 )
739 {
740     const f32 Epsilon = 1e-5f;
741 
742     f32 angleOffset;
743     if (cylinderForm.GetFixedOffset())
744     {
745         angleOffset = cylinderForm.GetAngleOffset();
746     }
747     else
748     {
749         angleOffset = random->NextFloat() * nw::math::F_PI * 2.0f;
750     }
751 
752     nw::math::VEC3 cookedScale(cylinderForm.GetScale());
753     if (nw::math::FAbs(cookedScale.x) < Epsilon)
754     {
755         cookedScale.x = Epsilon;
756     }
757 
758     if (nw::math::FAbs(cookedScale.y) < Epsilon)
759     {
760         cookedScale.y = Epsilon;
761     }
762 
763     if (nw::math::FAbs(cookedScale.z) < Epsilon)
764     {
765         cookedScale.z = Epsilon;
766     }
767 
768     // TBD:等間隔
769     for (int i = 0; i < emissionCount; ++i)
770     {
771         nw::math::VEC3 position(0.0f, 0.0f, 0.0f);
772 
773         f32 distance = random->NextFloat();
774 
775         if (cylinderForm.GetInner() == 0.0f)
776         {
777             // 全体
778             distance = nw::math::FSqrt(distance);
779         }
780         else if (cylinderForm.GetInner() == 1.0f)
781         {
782             // 外周のみ
783             distance = 1.0f;
784         }
785         else
786         {
787             distance = nw::math::FSqrt(distance + (cylinderForm.GetInner() * cylinderForm.GetInner() * (1.0f - distance)));
788         }
789 
790         f32 angle = random->NextFloat() * cylinderForm.GetAngleWidth() + angleOffset;
791 
792         if (cylinderForm.GetAngleSwing() != 0.0f)
793         {
794             angle += random->NextFloatSignedHalf() * cylinderForm.GetAngleSwing();
795         }
796 
797         f32 sin = 0.0f;
798         f32 cos = 1.0f;
799         if (angle != 0.0f)
800         {
801             nw::math::SinCosRad(&sin, &cos, angle);
802         }
803 
804         position.x = sin * distance;
805         position.y = random->NextFloatSignedOne();
806         position.z = -cos * distance;
807 
808         position.x *= cookedScale.x;
809         position.y *= cookedScale.y;
810         position.z *= cookedScale.z;
811 
812 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
813         positions[i].Set(position.x, position.y, position.z);
814 #else
815         int index = *activeIndex;
816         activeIndex += incrIndex;
817         targetTranslate[index].Set(position.x, position.y, position.z);
818 #endif
819     }
820 }
821 
822 void
CalcDiscForm(const ResParticleDiscForm & discForm,int emissionCount,ParticleRandom * random,nw::math::VEC3 * positions)823 ParticleEmitter::CalcDiscForm(
824     const ResParticleDiscForm& discForm,
825     int emissionCount,
826     ParticleRandom* random,
827 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
828     nw::math::VEC3* positions
829 #else
830     u16* activeIndex,
831     int incrIndex,
832     nw::math::VEC3* targetTranslate
833 #endif
834 )
835 {
836     const f32 Epsilon = 1e-5f;
837 
838     f32 angleOffset;
839     if (discForm.GetFixedOffset())
840     {
841         angleOffset = discForm.GetAngleOffset();
842     }
843     else
844     {
845         angleOffset = random->NextFloat() * nw::math::F_PI * 2.0f;
846     }
847 
848     f32 angleStep = 0.0f;
849     if (discForm.GetEvenInterval() && emissionCount != 0)
850     {
851         f32 f = nw::math::FMod(discForm.GetAngleWidth(), nw::math::F_PI * 2.0f);
852         f32 epsilon = 256.0f * 2.0f * nw::math::F_PI * nw::math::F_ULP;
853 
854         if (f < epsilon ||
855             f > (2 * nw::math::F_PI) - epsilon ||
856             emissionCount == 1)
857         {
858             angleStep = discForm.GetAngleWidth() / emissionCount;
859         }
860         else
861         {
862             angleStep = discForm.GetAngleWidth() / (emissionCount - 1);
863         }
864     }
865 
866     nw::math::VEC2 cookedScale(discForm.GetScale());
867     if (nw::math::FAbs(cookedScale.x) < Epsilon)
868     {
869         cookedScale.x = Epsilon;
870     }
871 
872     if (nw::math::FAbs(cookedScale.y) < Epsilon)
873     {
874         cookedScale.y = Epsilon;
875     }
876 
877     for (int i = 0; i < emissionCount; ++i)
878     {
879         nw::math::VEC3 position(0.0f, 0.0f, 0.0f);
880 
881         f32 distance = random->NextFloat();
882 
883         if (discForm.GetInner() == 0.0f)
884         {
885             // 全体
886             distance = nw::math::FSqrt(distance);
887         }
888         else if (discForm.GetInner() == 1.0f)
889         {
890             // 外周のみ
891             distance = 1.0f;
892         }
893         else
894         {
895             distance = nw::math::FSqrt(distance + (discForm.GetInner() * discForm.GetInner() * (1.0f - distance)));
896         }
897 
898         f32 angle;
899         if (angleStep != 0.0f)
900         {
901             angle = (angleStep * i) - (discForm.GetAngleWidth() / 2.0f);
902         }
903         else
904         {
905             angle = random->NextFloatSignedHalf() * discForm.GetAngleWidth();
906         }
907 
908         if (discForm.GetAngleSwing() != 0.0f)
909         {
910             angle += random->NextFloatSignedHalf() * discForm.GetAngleSwing();
911         }
912 
913         angle += angleOffset;
914 
915         f32 sin = 0.0f;
916         f32 cos = 1.0f;
917         if (angle != 0.0f)
918         {
919             nw::math::SinCosRad(&sin, &cos, angle);
920         }
921 
922         position.x = sin * distance;
923         position.y = 0;
924         position.z = -cos * distance;
925 
926         position.x *= cookedScale.x;
927         position.z *= cookedScale.y;
928 
929         if (position.x == 0.0f && position.z == 0.0f)
930         {
931             position.x = Epsilon;
932         }
933 
934 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
935         positions[i].Set(position.x, position.y, position.z);
936 #else
937         int index = *activeIndex;
938         activeIndex += incrIndex;
939         targetTranslate[index].Set(position.x, position.y, position.z);
940 #endif
941     }
942 }
943 
944 void
CalcPointForm(const ResParticlePointForm & pointForm,int emissionCount,ParticleRandom * random,nw::math::VEC3 * positions)945 ParticleEmitter::CalcPointForm(
946     const ResParticlePointForm& pointForm,
947     int emissionCount,
948     ParticleRandom* random,
949 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
950     nw::math::VEC3* positions
951 #else
952     u16* activeIndex,
953     int incrIndex,
954     nw::math::VEC3* targetTranslate
955 #endif
956 )
957 {
958     NW_UNUSED_VARIABLE(pointForm)
959     NW_UNUSED_VARIABLE(random)
960 
961     const float Radius = 1e-3f;
962 
963     for (int i = 0; i < emissionCount; ++i)
964     {
965 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
966         positions[i].Set(
967             random->NextFloatSignedHalf() * Radius,
968             random->NextFloatSignedHalf() * Radius,
969             random->NextFloatSignedHalf() * Radius);
970 #else
971         int index = *activeIndex;
972         activeIndex += incrIndex;
973         targetTranslate[index].x = random->NextFloatSignedHalf() * Radius;
974         targetTranslate[index].y = random->NextFloatSignedHalf() * Radius;
975         targetTranslate[index].z = random->NextFloatSignedHalf() * Radius;
976 #endif
977     }
978 }
979 
980 void
CalcSphereForm(const ResParticleSphereForm & sphereForm,int emissionCount,ParticleRandom * random,nw::math::VEC3 * positions)981 ParticleEmitter::CalcSphereForm(
982     const ResParticleSphereForm& sphereForm,
983     int emissionCount,
984     ParticleRandom* random,
985 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
986     nw::math::VEC3* positions
987 #else
988     u16* activeIndex,
989     int incrIndex,
990     nw::math::VEC3* targetTranslate
991 #endif
992 )
993 {
994     const f32 Epsilon = 1e-5f;
995 
996     f32 angleOffset;
997     if (sphereForm.GetFixedOffset())
998     {
999         angleOffset = sphereForm.GetAngleOffset();
1000     }
1001     else
1002     {
1003         angleOffset = random->NextFloat() * nw::math::F_PI * 2.0f;
1004     }
1005 
1006     nw::math::VEC3 cookedScale(sphereForm.GetScale());
1007     if (nw::math::FAbs(cookedScale.x) < Epsilon)
1008     {
1009         cookedScale.x = Epsilon;
1010     }
1011 
1012     if (nw::math::FAbs(cookedScale.y) < Epsilon)
1013     {
1014         cookedScale.y = Epsilon;
1015     }
1016 
1017     if (nw::math::FAbs(cookedScale.z) < Epsilon)
1018     {
1019         cookedScale.z = Epsilon;
1020     }
1021 
1022     // TBD:等間隔
1023     for (int i = 0; i < emissionCount; ++i)
1024     {
1025         nw::math::VEC3 position(0.0f, 0.0f, 0.0f);
1026 
1027         f32 distance = random->NextFloat();
1028 
1029         if (sphereForm.GetInner() == 0.0f)
1030         {
1031             // 全体
1032             distance = 1.0f - (distance * distance * distance);
1033         }
1034         else if (sphereForm.GetInner() == 1.0f)
1035         {
1036             // 外周のみ
1037             distance = 1.0f;
1038         }
1039         else
1040         {
1041             distance = 1.0f - (distance * distance * distance);
1042             distance = distance + (sphereForm.GetInner() * (1.0f - distance));
1043         }
1044 
1045         f32 yaw = random->NextFloat() * sphereForm.GetAngleWidth() + angleOffset;
1046         f32 pitch = nw::math::F_PI * random->NextFloatSignedHalf();
1047 
1048         if (sphereForm.GetAngleSwing() != 0.0f)
1049         {
1050             yaw += random->NextFloatSignedHalf() * sphereForm.GetAngleSwing();
1051             pitch += random->NextFloatSignedHalf() * sphereForm.GetAngleSwing();
1052         }
1053 
1054         f32 sinYaw = 0.0f;
1055         f32 cosYaw = 1.0f;
1056         if (yaw != 0.0f)
1057         {
1058             nw::math::SinCosRad(&sinYaw, &cosYaw, yaw);
1059         }
1060 
1061         f32 sinPitch = 0.0f;
1062         f32 cosPitch = 1.0f;
1063         if (pitch != 0.0f)
1064         {
1065             nw::math::SinCosRad(&sinPitch, &cosPitch, pitch);
1066         }
1067 
1068         position.x = sinYaw * -cosPitch;
1069         position.y = -sinPitch;
1070         position.z = cosYaw * cosPitch;
1071 
1072         position *= distance;
1073 
1074         position.x *= cookedScale.x;
1075         position.y *= cookedScale.y;
1076         position.z *= cookedScale.z;
1077 
1078 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1079         positions[i].Set(position.x, position.y, position.z);
1080 #else
1081         int index = *activeIndex;
1082         activeIndex += incrIndex;
1083         targetTranslate[index].Set(position.x, position.y, position.z);
1084 #endif
1085     }
1086 }
1087 
1088 void
CalcRectangleForm(const ResParticleRectangleForm & rectangleForm,int emissionCount,ParticleRandom * random,nw::math::VEC3 * positions)1089 ParticleEmitter::CalcRectangleForm(
1090     const ResParticleRectangleForm& rectangleForm,
1091     int emissionCount,
1092     ParticleRandom* random,
1093 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1094     nw::math::VEC3* positions
1095 #else
1096     u16* activeIndex,
1097     int incrIndex,
1098     nw::math::VEC3* targetTranslate
1099 #endif
1100 )
1101 {
1102     const f32 Epsilon = 1e-5f;
1103 
1104     nw::math::VEC2 cookedScale(rectangleForm.GetScale());
1105     if (nw::math::FAbs(cookedScale.x) < Epsilon)
1106     {
1107         cookedScale.x = Epsilon;
1108     }
1109 
1110     if (nw::math::FAbs(cookedScale.y) < Epsilon)
1111     {
1112         cookedScale.y = Epsilon;
1113     }
1114 
1115     // TBD:等間隔
1116     for (int i = 0; i < emissionCount; ++i)
1117     {
1118         nw::math::VEC3 position(0.0f, 0.0f, 0.0f);
1119 
1120         if (rectangleForm.GetInner() == 0.0f)
1121         {
1122             position.x = random->NextFloatSignedOne();
1123             position.z = random->NextFloatSignedOne();
1124 
1125             position.x *= cookedScale.x;
1126             position.z *= cookedScale.y; // ScaleはVector2のため
1127         }
1128         else if (rectangleForm.GetInner() == 1.0f)
1129         {
1130             f32 r = random->NextFloatSignedOne() * 2.0f *
1131                 (cookedScale.x + cookedScale.y);
1132 
1133             if (r >= 0)
1134             {
1135                 if (r < cookedScale.x * 2.0f)
1136                 {
1137                     position.Set(
1138                         r - cookedScale.x,
1139                         0,
1140                         cookedScale.y);
1141                 }
1142                 else
1143                 {
1144                     position.Set(
1145                         cookedScale.x,
1146                         0,
1147                         r - (cookedScale.x * 2.0f) - cookedScale.y);
1148                 }
1149             }
1150             else
1151             {
1152                 if (r > -cookedScale.x * 2.0f)
1153                 {
1154                     position.Set(
1155                         r + cookedScale.x,
1156                         0,
1157                         -cookedScale.y);
1158                 }
1159                 else
1160                 {
1161                     position.Set(
1162                         -cookedScale.x,
1163                         0,
1164                         r + (cookedScale.x * 2.0f) + cookedScale.y);
1165                 }
1166             }
1167         }
1168         else
1169         {
1170             // 均等に分布させるために面積比で場所を決める
1171             f32 f = random->NextFloat() * (1.0f + rectangleForm.GetInner());
1172 
1173             position.x = random->NextFloatSignedOne();
1174             position.z = random->NextFloatSignedOne();
1175 
1176             if (f < 1.0f)
1177             {
1178                 position.z *= 1.0f - rectangleForm.GetInner();
1179                 if (position.z >= 0)
1180                 {
1181                     position.z = 1.0f - position.z;
1182                         }
1183                 else
1184                 {
1185                     position.z = -1.0f - position.z;
1186                 }
1187             }
1188             else
1189             {
1190                 position.z *= rectangleForm.GetInner();
1191                 position.x *= 1.0f - rectangleForm.GetInner();
1192                 if (position.x >= 0)
1193                 {
1194                     position.x = 1.0f - position.x;
1195                 }
1196                 else
1197                 {
1198                     position.x = -1.0f - position.x;
1199                 }
1200             }
1201 
1202             position.x *= cookedScale.x;
1203             position.z *= cookedScale.y;
1204 
1205             if (position.x == 0.0f && position.z == 0.0f)
1206             {
1207                         position.x = Epsilon;
1208             }
1209         }
1210 
1211 #ifdef NW_GFX_PARTICLE_COMPAT_1_1
1212         positions[i].Set(position.x, position.y, position.z);
1213 #else
1214         int index = *activeIndex;
1215         activeIndex += incrIndex;
1216         targetTranslate[index].Set(position.x, position.y, position.z);
1217 #endif
1218     }
1219 }
1220 
1221 
1222 } // namespace gfx
1223 } // namespace nw
1224