1 /*
2 * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
3 * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
4 */
5
6 #include <bgfx/bgfx.h>
7 #include <bgfx/embedded_shader.h>
8
9 #include "particle_system.h"
10 #include "../bgfx_utils.h"
11 #include "../packrect.h"
12
13 #include <bx/easing.h>
14 #include <bx/handlealloc.h>
15
16 #include "vs_particle.bin.h"
17 #include "fs_particle.bin.h"
18
19 static const bgfx::EmbeddedShader s_embeddedShaders[] =
20 {
21 BGFX_EMBEDDED_SHADER(vs_particle),
22 BGFX_EMBEDDED_SHADER(fs_particle),
23
24 BGFX_EMBEDDED_SHADER_END()
25 };
26
27 struct PosColorTexCoord0Vertex
28 {
29 float m_x;
30 float m_y;
31 float m_z;
32 uint32_t m_abgr;
33 float m_u;
34 float m_v;
35 float m_blend;
36 float m_angle;
37
initPosColorTexCoord0Vertex38 static void init()
39 {
40 ms_layout
41 .begin()
42 .add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)
43 .add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true)
44 .add(bgfx::Attrib::TexCoord0, 4, bgfx::AttribType::Float)
45 .end();
46 }
47
48 static bgfx::VertexLayout ms_layout;
49 };
50
51 bgfx::VertexLayout PosColorTexCoord0Vertex::ms_layout;
52
reset()53 void EmitterUniforms::reset()
54 {
55 m_position[0] = 0.0f;
56 m_position[1] = 0.0f;
57 m_position[2] = 0.0f;
58
59 m_angle[0] = 0.0f;
60 m_angle[1] = 0.0f;
61 m_angle[2] = 0.0f;
62
63 m_particlesPerSecond = 0;
64
65 m_offsetStart[0] = 0.0f;
66 m_offsetStart[1] = 1.0f;
67 m_offsetEnd[0] = 2.0f;
68 m_offsetEnd[1] = 3.0f;
69
70 m_rgba[0] = 0x00ffffff;
71 m_rgba[1] = UINT32_MAX;
72 m_rgba[2] = UINT32_MAX;
73 m_rgba[3] = UINT32_MAX;
74 m_rgba[4] = 0x00ffffff;
75
76 m_blendStart[0] = 0.8f;
77 m_blendStart[1] = 1.0f;
78 m_blendEnd[0] = 0.0f;
79 m_blendEnd[1] = 0.2f;
80
81 m_scaleStart[0] = 0.1f;
82 m_scaleStart[1] = 0.2f;
83 m_scaleEnd[0] = 0.3f;
84 m_scaleEnd[1] = 0.4f;
85
86 m_lifeSpan[0] = 1.0f;
87 m_lifeSpan[1] = 2.0f;
88
89 m_gravityScale = 0.0f;
90
91 m_easePos = bx::Easing::Linear;
92 m_easeRgba = bx::Easing::Linear;
93 m_easeBlend = bx::Easing::Linear;
94 m_easeScale = bx::Easing::Linear;
95 }
96
97 namespace ps
98 {
99 struct Particle
100 {
101 bx::Vec3 start;
102 bx::Vec3 end[2];
103 float blendStart;
104 float blendEnd;
105 float scaleStart;
106 float scaleEnd;
107
108 uint32_t rgba[5];
109
110 float life;
111 float lifeSpan;
112 };
113
114 struct ParticleSort
115 {
116 float dist;
117 uint32_t idx;
118 };
119
toAbgr(const float * _rgba)120 inline uint32_t toAbgr(const float* _rgba)
121 {
122 return 0
123 | (uint8_t(_rgba[0]*255.0f)<< 0)
124 | (uint8_t(_rgba[1]*255.0f)<< 8)
125 | (uint8_t(_rgba[2]*255.0f)<<16)
126 | (uint8_t(_rgba[3]*255.0f)<<24)
127 ;
128 }
129
toAbgr(float _rr,float _gg,float _bb,float _aa)130 inline uint32_t toAbgr(float _rr, float _gg, float _bb, float _aa)
131 {
132 return 0
133 | (uint8_t(_rr*255.0f)<< 0)
134 | (uint8_t(_gg*255.0f)<< 8)
135 | (uint8_t(_bb*255.0f)<<16)
136 | (uint8_t(_aa*255.0f)<<24)
137 ;
138 }
139
140 #define SPRITE_TEXTURE_SIZE 1024
141 template<uint16_t MaxHandlesT = 256, uint16_t TextureSizeT = 1024>
142 struct SpriteT
143 {
SpriteTps::SpriteT144 SpriteT()
145 : m_ra(TextureSizeT, TextureSizeT)
146 {
147 }
148
createps::SpriteT149 EmitterSpriteHandle create(uint16_t _width, uint16_t _height)
150 {
151 EmitterSpriteHandle handle = { bx::kInvalidHandle };
152
153 if (m_handleAlloc.getNumHandles() < m_handleAlloc.getMaxHandles() )
154 {
155 Pack2D pack;
156 if (m_ra.find(_width, _height, pack) )
157 {
158 handle.idx = m_handleAlloc.alloc();
159 m_pack[handle.idx] = pack;
160 }
161 }
162
163 return handle;
164 }
165
destroyps::SpriteT166 void destroy(EmitterSpriteHandle _sprite)
167 {
168 const Pack2D& pack = m_pack[_sprite.idx];
169 m_ra.clear(pack);
170 m_handleAlloc.free(_sprite.idx);
171 }
172
getps::SpriteT173 const Pack2D& get(EmitterSpriteHandle _sprite) const
174 {
175 return m_pack[_sprite.idx];
176 }
177
178 bx::HandleAllocT<MaxHandlesT> m_handleAlloc;
179 Pack2D m_pack[MaxHandlesT];
180 RectPack2DT<256> m_ra;
181 };
182
183 struct Emitter
184 {
185 void create(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles);
186 void destroy();
187
resetps::Emitter188 void reset()
189 {
190 m_dt = 0.0f;
191 m_uniforms.reset();
192 m_num = 0;
193 bx::memSet(&m_aabb, 0, sizeof(Aabb) );
194
195 m_rng.reset();
196 }
197
updateps::Emitter198 void update(float _dt)
199 {
200 uint32_t num = m_num;
201 for (uint32_t ii = 0; ii < num; ++ii)
202 {
203 Particle& particle = m_particles[ii];
204 particle.life += _dt * 1.0f/particle.lifeSpan;
205
206 if (particle.life > 1.0f)
207 {
208 if (ii != num-1)
209 {
210 bx::memCopy(&particle, &m_particles[num-1], sizeof(Particle) );
211 --ii;
212 }
213
214 --num;
215 }
216 }
217
218 m_num = num;
219
220 if (0 < m_uniforms.m_particlesPerSecond)
221 {
222 spawn(_dt);
223 }
224 }
225
spawnps::Emitter226 void spawn(float _dt)
227 {
228 float mtx[16];
229 bx::mtxSRT(mtx
230 , 1.0f, 1.0f, 1.0f
231 , m_uniforms.m_angle[0], m_uniforms.m_angle[1], m_uniforms.m_angle[2]
232 , m_uniforms.m_position[0], m_uniforms.m_position[1], m_uniforms.m_position[2]
233 );
234
235 const float timePerParticle = 1.0f/m_uniforms.m_particlesPerSecond;
236 m_dt += _dt;
237 const uint32_t numParticles = uint32_t(m_dt / timePerParticle);
238 m_dt -= numParticles * timePerParticle;
239
240 constexpr bx::Vec3 up = { 0.0f, 1.0f, 0.0f };
241
242 float time = 0.0f;
243 for (uint32_t ii = 0
244 ; ii < numParticles && m_num < m_max
245 ; ++ii
246 )
247 {
248 Particle& particle = m_particles[m_num];
249 m_num++;
250
251 bx::Vec3 pos;
252 switch (m_shape)
253 {
254 default:
255 case EmitterShape::Sphere:
256 pos = bx::randUnitSphere(&m_rng);
257 break;
258
259 case EmitterShape::Hemisphere:
260 pos = bx::randUnitHemisphere(&m_rng, up);
261 break;
262
263 case EmitterShape::Circle:
264 pos = bx::randUnitCircle(&m_rng);
265 break;
266
267 case EmitterShape::Disc:
268 {
269 const bx::Vec3 tmp = bx::randUnitCircle(&m_rng);
270 pos = bx::mul(tmp, bx::frnd(&m_rng) );
271 }
272 break;
273
274 case EmitterShape::Rect:
275 pos =
276 {
277 bx::frndh(&m_rng),
278 0.0f,
279 bx::frndh(&m_rng),
280 };
281 break;
282 }
283
284 bx::Vec3 dir;
285 switch (m_direction)
286 {
287 default:
288 case EmitterDirection::Up:
289 dir = up;
290 break;
291
292 case EmitterDirection::Outward:
293 dir = bx::normalize(pos);
294 break;
295 }
296
297 const float startOffset = bx::lerp(m_uniforms.m_offsetStart[0], m_uniforms.m_offsetStart[1], bx::frnd(&m_rng) );
298 const bx::Vec3 start = bx::mul(pos, startOffset);
299
300 const float endOffset = bx::lerp(m_uniforms.m_offsetEnd[0], m_uniforms.m_offsetEnd[1], bx::frnd(&m_rng) );
301 const bx::Vec3 tmp1 = bx::mul(dir, endOffset);
302 const bx::Vec3 end = bx::add(tmp1, start);
303
304 particle.life = time;
305 particle.lifeSpan = bx::lerp(m_uniforms.m_lifeSpan[0], m_uniforms.m_lifeSpan[1], bx::frnd(&m_rng) );
306
307 const bx::Vec3 gravity = { 0.0f, -9.81f * m_uniforms.m_gravityScale * bx::square(particle.lifeSpan), 0.0f };
308
309 particle.start = bx::mul(start, mtx);
310 particle.end[0] = bx::mul(end, mtx);
311 particle.end[1] = bx::add(particle.end[0], gravity);
312
313 bx::memCopy(particle.rgba, m_uniforms.m_rgba, BX_COUNTOF(m_uniforms.m_rgba)*sizeof(uint32_t) );
314
315 particle.blendStart = bx::lerp(m_uniforms.m_blendStart[0], m_uniforms.m_blendStart[1], bx::frnd(&m_rng) );
316 particle.blendEnd = bx::lerp(m_uniforms.m_blendEnd[0], m_uniforms.m_blendEnd[1], bx::frnd(&m_rng) );
317
318 particle.scaleStart = bx::lerp(m_uniforms.m_scaleStart[0], m_uniforms.m_scaleStart[1], bx::frnd(&m_rng) );
319 particle.scaleEnd = bx::lerp(m_uniforms.m_scaleEnd[0], m_uniforms.m_scaleEnd[1], bx::frnd(&m_rng) );
320
321 time += timePerParticle;
322 }
323 }
324
renderps::Emitter325 uint32_t render(const float _uv[4], const float* _mtxView, const bx::Vec3& _eye, uint32_t _first, uint32_t _max, ParticleSort* _outSort, PosColorTexCoord0Vertex* _outVertices)
326 {
327 bx::EaseFn easeRgba = bx::getEaseFunc(m_uniforms.m_easeRgba);
328 bx::EaseFn easePos = bx::getEaseFunc(m_uniforms.m_easePos);
329 bx::EaseFn easeBlend = bx::getEaseFunc(m_uniforms.m_easeBlend);
330 bx::EaseFn easeScale = bx::getEaseFunc(m_uniforms.m_easeScale);
331
332 Aabb aabb =
333 {
334 { bx::kInfinity, bx::kInfinity, bx::kInfinity },
335 { -bx::kInfinity, -bx::kInfinity, -bx::kInfinity },
336 };
337
338 for (uint32_t jj = 0, num = m_num, current = _first
339 ; jj < num && current < _max
340 ; ++jj, ++current
341 )
342 {
343 const Particle& particle = m_particles[jj];
344
345 const float ttPos = easePos(particle.life);
346 const float ttScale = easeScale(particle.life);
347 const float ttBlend = bx::clamp(easeBlend(particle.life), 0.0f, 1.0f);
348 const float ttRgba = bx::clamp(easeRgba(particle.life), 0.0f, 1.0f);
349
350 const bx::Vec3 p0 = bx::lerp(particle.start, particle.end[0], ttPos);
351 const bx::Vec3 p1 = bx::lerp(particle.end[0], particle.end[1], ttPos);
352 const bx::Vec3 pos = bx::lerp(p0, p1, ttPos);
353
354 ParticleSort& sort = _outSort[current];
355 const bx::Vec3 tmp0 = bx::sub(_eye, pos);
356 sort.dist = bx::length(tmp0);
357 sort.idx = current;
358
359 uint32_t idx = uint32_t(ttRgba*4);
360 float ttmod = bx::mod(ttRgba, 0.25f)/0.25f;
361 uint32_t rgbaStart = particle.rgba[idx];
362 uint32_t rgbaEnd = particle.rgba[idx+1];
363
364 float rr = bx::lerp( ( (uint8_t*)&rgbaStart)[0], ( (uint8_t*)&rgbaEnd)[0], ttmod)/255.0f;
365 float gg = bx::lerp( ( (uint8_t*)&rgbaStart)[1], ( (uint8_t*)&rgbaEnd)[1], ttmod)/255.0f;
366 float bb = bx::lerp( ( (uint8_t*)&rgbaStart)[2], ( (uint8_t*)&rgbaEnd)[2], ttmod)/255.0f;
367 float aa = bx::lerp( ( (uint8_t*)&rgbaStart)[3], ( (uint8_t*)&rgbaEnd)[3], ttmod)/255.0f;
368
369 float blend = bx::lerp(particle.blendStart, particle.blendEnd, ttBlend);
370 float scale = bx::lerp(particle.scaleStart, particle.scaleEnd, ttScale);
371
372 uint32_t abgr = toAbgr(rr, gg, bb, aa);
373
374 const bx::Vec3 udir = { _mtxView[0]*scale, _mtxView[4]*scale, _mtxView[8]*scale };
375 const bx::Vec3 vdir = { _mtxView[1]*scale, _mtxView[5]*scale, _mtxView[9]*scale };
376
377 PosColorTexCoord0Vertex* vertex = &_outVertices[current*4];
378
379 const bx::Vec3 ul = bx::sub(bx::sub(pos, udir), vdir);
380 bx::store(&vertex->m_x, ul);
381 aabbExpand(aabb, ul);
382 vertex->m_abgr = abgr;
383 vertex->m_u = _uv[0];
384 vertex->m_v = _uv[1];
385 vertex->m_blend = blend;
386 ++vertex;
387
388 const bx::Vec3 ur = bx::sub(bx::add(pos, udir), vdir);
389 bx::store(&vertex->m_x, ur);
390 aabbExpand(aabb, ur);
391 vertex->m_abgr = abgr;
392 vertex->m_u = _uv[2];
393 vertex->m_v = _uv[1];
394 vertex->m_blend = blend;
395 ++vertex;
396
397 const bx::Vec3 br = bx::add(bx::add(pos, udir), vdir);
398 bx::store(&vertex->m_x, br);
399 aabbExpand(aabb, br);
400 vertex->m_abgr = abgr;
401 vertex->m_u = _uv[2];
402 vertex->m_v = _uv[3];
403 vertex->m_blend = blend;
404 ++vertex;
405
406 const bx::Vec3 bl = bx::add(bx::sub(pos, udir), vdir);
407 bx::store(&vertex->m_x, bl);
408 aabbExpand(aabb, bl);
409 vertex->m_abgr = abgr;
410 vertex->m_u = _uv[0];
411 vertex->m_v = _uv[3];
412 vertex->m_blend = blend;
413 ++vertex;
414 }
415
416 m_aabb = aabb;
417
418 return m_num;
419 }
420
421 EmitterShape::Enum m_shape;
422 EmitterDirection::Enum m_direction;
423
424 float m_dt;
425 bx::RngMwc m_rng;
426 EmitterUniforms m_uniforms;
427
428 Aabb m_aabb;
429
430 Particle* m_particles;
431 uint32_t m_num;
432 uint32_t m_max;
433 };
434
particleSortFn(const void * _lhs,const void * _rhs)435 static int32_t particleSortFn(const void* _lhs, const void* _rhs)
436 {
437 const ParticleSort& lhs = *(const ParticleSort*)_lhs;
438 const ParticleSort& rhs = *(const ParticleSort*)_rhs;
439 return lhs.dist > rhs.dist ? -1 : 1;
440 }
441
442 struct ParticleSystem
443 {
initps::ParticleSystem444 void init(uint16_t _maxEmitters, bx::AllocatorI* _allocator)
445 {
446 m_allocator = _allocator;
447
448 if (NULL == _allocator)
449 {
450 static bx::DefaultAllocator allocator;
451 m_allocator = &allocator;
452 }
453
454 m_emitterAlloc = bx::createHandleAlloc(m_allocator, _maxEmitters);
455 m_emitter = (Emitter*)BX_ALLOC(m_allocator, sizeof(Emitter)*_maxEmitters);
456
457 PosColorTexCoord0Vertex::init();
458
459 m_num = 0;
460
461 s_texColor = bgfx::createUniform("s_texColor", bgfx::UniformType::Sampler);
462 m_texture = bgfx::createTexture2D(
463 SPRITE_TEXTURE_SIZE
464 , SPRITE_TEXTURE_SIZE
465 , false
466 , 1
467 , bgfx::TextureFormat::BGRA8
468 );
469
470 bgfx::RendererType::Enum type = bgfx::getRendererType();
471 m_particleProgram = bgfx::createProgram(
472 bgfx::createEmbeddedShader(s_embeddedShaders, type, "vs_particle")
473 , bgfx::createEmbeddedShader(s_embeddedShaders, type, "fs_particle")
474 , true
475 );
476 }
477
shutdownps::ParticleSystem478 void shutdown()
479 {
480 bgfx::destroy(m_particleProgram);
481 bgfx::destroy(m_texture);
482 bgfx::destroy(s_texColor);
483
484 bx::destroyHandleAlloc(m_allocator, m_emitterAlloc);
485 BX_FREE(m_allocator, m_emitter);
486
487 m_allocator = NULL;
488 }
489
createSpriteps::ParticleSystem490 EmitterSpriteHandle createSprite(uint16_t _width, uint16_t _height, const void* _data)
491 {
492 EmitterSpriteHandle handle = m_sprite.create(_width, _height);
493
494 if (isValid(handle) )
495 {
496 const Pack2D& pack = m_sprite.get(handle);
497 bgfx::updateTexture2D(
498 m_texture
499 , 0
500 , 0
501 , pack.m_x
502 , pack.m_y
503 , pack.m_width
504 , pack.m_height
505 , bgfx::copy(_data, pack.m_width*pack.m_height*4)
506 );
507 }
508
509 return handle;
510 }
511
destroyps::ParticleSystem512 void destroy(EmitterSpriteHandle _handle)
513 {
514 m_sprite.destroy(_handle);
515 }
516
updateps::ParticleSystem517 void update(float _dt)
518 {
519 uint32_t numParticles = 0;
520 for (uint16_t ii = 0, num = m_emitterAlloc->getNumHandles(); ii < num; ++ii)
521 {
522 const uint16_t idx = m_emitterAlloc->getHandleAt(ii);
523 Emitter& emitter = m_emitter[idx];
524 emitter.update(_dt);
525 numParticles += emitter.m_num;
526 }
527
528 m_num = numParticles;
529 }
530
renderps::ParticleSystem531 void render(uint8_t _view, const float* _mtxView, const bx::Vec3& _eye)
532 {
533 if (0 != m_num)
534 {
535 bgfx::TransientVertexBuffer tvb;
536 bgfx::TransientIndexBuffer tib;
537
538 const uint32_t numVertices = bgfx::getAvailTransientVertexBuffer(m_num*4, PosColorTexCoord0Vertex::ms_layout);
539 const uint32_t numIndices = bgfx::getAvailTransientIndexBuffer(m_num*6);
540 const uint32_t max = bx::uint32_min(numVertices/4, numIndices/6);
541 BX_WARN(m_num == max
542 , "Truncating transient buffer for particles to maximum available (requested %d, available %d)."
543 , m_num
544 , max
545 );
546
547 if (0 < max)
548 {
549 bgfx::allocTransientBuffers(&tvb
550 , PosColorTexCoord0Vertex::ms_layout
551 , max*4
552 , &tib
553 , max*6
554 );
555 PosColorTexCoord0Vertex* vertices = (PosColorTexCoord0Vertex*)tvb.data;
556
557 ParticleSort* particleSort = (ParticleSort*)BX_ALLOC(m_allocator, max*sizeof(ParticleSort) );
558
559 uint32_t pos = 0;
560 for (uint16_t ii = 0, numEmitters = m_emitterAlloc->getNumHandles(); ii < numEmitters; ++ii)
561 {
562 const uint16_t idx = m_emitterAlloc->getHandleAt(ii);
563 Emitter& emitter = m_emitter[idx];
564
565 const Pack2D& pack = m_sprite.get(emitter.m_uniforms.m_handle);
566 const float invTextureSize = 1.0f/SPRITE_TEXTURE_SIZE;
567 const float uv[4] =
568 {
569 pack.m_x * invTextureSize,
570 pack.m_y * invTextureSize,
571 (pack.m_x + pack.m_width ) * invTextureSize,
572 (pack.m_y + pack.m_height) * invTextureSize,
573 };
574
575 pos += emitter.render(uv, _mtxView, _eye, pos, max, particleSort, vertices);
576 }
577
578 qsort(particleSort
579 , max
580 , sizeof(ParticleSort)
581 , particleSortFn
582 );
583
584 uint16_t* indices = (uint16_t*)tib.data;
585 for (uint32_t ii = 0; ii < max; ++ii)
586 {
587 const ParticleSort& sort = particleSort[ii];
588 uint16_t* index = &indices[ii*6];
589 uint16_t idx = (uint16_t)sort.idx;
590 index[0] = idx*4+0;
591 index[1] = idx*4+1;
592 index[2] = idx*4+2;
593 index[3] = idx*4+2;
594 index[4] = idx*4+3;
595 index[5] = idx*4+0;
596 }
597
598 BX_FREE(m_allocator, particleSort);
599
600 bgfx::setState(0
601 | BGFX_STATE_WRITE_RGB
602 | BGFX_STATE_WRITE_A
603 | BGFX_STATE_DEPTH_TEST_LESS
604 | BGFX_STATE_CULL_CW
605 | BGFX_STATE_BLEND_NORMAL
606 );
607 bgfx::setVertexBuffer(0, &tvb);
608 bgfx::setIndexBuffer(&tib);
609 bgfx::setTexture(0, s_texColor, m_texture);
610 bgfx::submit(_view, m_particleProgram);
611 }
612 }
613 }
614
createEmitterps::ParticleSystem615 EmitterHandle createEmitter(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles)
616 {
617 EmitterHandle handle = { m_emitterAlloc->alloc() };
618
619 if (UINT16_MAX != handle.idx)
620 {
621 m_emitter[handle.idx].create(_shape, _direction, _maxParticles);
622 }
623
624 return handle;
625 }
626
updateEmitterps::ParticleSystem627 void updateEmitter(EmitterHandle _handle, const EmitterUniforms* _uniforms)
628 {
629 BX_CHECK(m_emitterAlloc.isValid(_handle.idx)
630 , "destroyEmitter handle %d is not valid."
631 , _handle.idx
632 );
633
634 Emitter& emitter = m_emitter[_handle.idx];
635
636 if (NULL == _uniforms)
637 {
638 emitter.reset();
639 }
640 else
641 {
642 bx::memCopy(&emitter.m_uniforms, _uniforms, sizeof(EmitterUniforms) );
643 }
644 }
645
getAabbps::ParticleSystem646 void getAabb(EmitterHandle _handle, Aabb& _outAabb)
647 {
648 BX_CHECK(m_emitterAlloc.isValid(_handle.idx)
649 , "getAabb handle %d is not valid."
650 , _handle.idx
651 );
652 _outAabb = m_emitter[_handle.idx].m_aabb;
653 }
654
destroyEmitterps::ParticleSystem655 void destroyEmitter(EmitterHandle _handle)
656 {
657 BX_CHECK(m_emitterAlloc.isValid(_handle.idx)
658 , "destroyEmitter handle %d is not valid."
659 , _handle.idx
660 );
661
662 m_emitter[_handle.idx].destroy();
663 m_emitterAlloc->free(_handle.idx);
664 }
665
666 bx::AllocatorI* m_allocator;
667
668 bx::HandleAlloc* m_emitterAlloc;
669 Emitter* m_emitter;
670
671 typedef SpriteT<256, SPRITE_TEXTURE_SIZE> Sprite;
672 Sprite m_sprite;
673
674 bgfx::UniformHandle s_texColor;
675 bgfx::TextureHandle m_texture;
676 bgfx::ProgramHandle m_particleProgram;
677
678 uint32_t m_num;
679 };
680
681 static ParticleSystem s_ctx;
682
create(EmitterShape::Enum _shape,EmitterDirection::Enum _direction,uint32_t _maxParticles)683 void Emitter::create(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles)
684 {
685 reset();
686
687 m_shape = _shape;
688 m_direction = _direction;
689 m_max = _maxParticles;
690 m_particles = (Particle*)BX_ALLOC(s_ctx.m_allocator, m_max*sizeof(Particle) );
691 }
692
destroy()693 void Emitter::destroy()
694 {
695 BX_FREE(s_ctx.m_allocator, m_particles);
696 m_particles = NULL;
697 }
698
699 } // namespace ps
700
701 using namespace ps;
702
psInit(uint16_t _maxEmitters,bx::AllocatorI * _allocator)703 void psInit(uint16_t _maxEmitters, bx::AllocatorI* _allocator)
704 {
705 s_ctx.init(_maxEmitters, _allocator);
706 }
707
psShutdown()708 void psShutdown()
709 {
710 s_ctx.shutdown();
711 }
712
psCreateSprite(uint16_t _width,uint16_t _height,const void * _data)713 EmitterSpriteHandle psCreateSprite(uint16_t _width, uint16_t _height, const void* _data)
714 {
715 return s_ctx.createSprite(_width, _height, _data);
716 }
717
psDestroy(EmitterSpriteHandle _handle)718 void psDestroy(EmitterSpriteHandle _handle)
719 {
720 s_ctx.destroy(_handle);
721 }
722
psCreateEmitter(EmitterShape::Enum _shape,EmitterDirection::Enum _direction,uint32_t _maxParticles)723 EmitterHandle psCreateEmitter(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles)
724 {
725 return s_ctx.createEmitter(_shape, _direction, _maxParticles);
726 }
727
psUpdateEmitter(EmitterHandle _handle,const EmitterUniforms * _uniforms)728 void psUpdateEmitter(EmitterHandle _handle, const EmitterUniforms* _uniforms)
729 {
730 s_ctx.updateEmitter(_handle, _uniforms);
731 }
732
psGetAabb(EmitterHandle _handle,Aabb & _outAabb)733 void psGetAabb(EmitterHandle _handle, Aabb& _outAabb)
734 {
735 s_ctx.getAabb(_handle, _outAabb);
736 }
737
psDestroyEmitter(EmitterHandle _handle)738 void psDestroyEmitter(EmitterHandle _handle)
739 {
740 s_ctx.destroyEmitter(_handle);
741 }
742
psUpdate(float _dt)743 void psUpdate(float _dt)
744 {
745 s_ctx.update(_dt);
746 }
747
psRender(uint8_t _view,const float * _mtxView,const bx::Vec3 & _eye)748 void psRender(uint8_t _view, const float* _mtxView, const bx::Vec3& _eye)
749 {
750 s_ctx.render(_view, _mtxView, _eye);
751 }
752