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