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