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