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