summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/bgfx/examples/common/ps/particle_system.cpp
diff options
context:
space:
mode:
author Branimir Karadžić <branimirkaradzic@gmail.com>2017-02-05 13:56:35 +0100
committer Miodrag Milanovic <mmicko@gmail.com>2017-02-05 13:56:35 +0100
commit1607745432f50099711d7aee355111111e66ff51 (patch)
treec4eaec8008f6e0283ca652cdffb55a34eca9b9bb /3rdparty/bgfx/examples/common/ps/particle_system.cpp
parent193e8c8a89a4be5c63909cdbbcca4d9d6f8fee02 (diff)
Update BGFX and BX (nw)
Diffstat (limited to '3rdparty/bgfx/examples/common/ps/particle_system.cpp')
-rw-r--r--3rdparty/bgfx/examples/common/ps/particle_system.cpp703
1 files changed, 703 insertions, 0 deletions
diff --git a/3rdparty/bgfx/examples/common/ps/particle_system.cpp b/3rdparty/bgfx/examples/common/ps/particle_system.cpp
new file mode 100644
index 00000000000..4e135048dd2
--- /dev/null
+++ b/3rdparty/bgfx/examples/common/ps/particle_system.cpp
@@ -0,0 +1,703 @@
+/*
+ * Copyright 2011-2017 Branimir Karadzic. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#include <bgfx/bgfx.h>
+#include <bgfx/embedded_shader.h>
+
+#include "particle_system.h"
+#include "../bgfx_utils.h"
+
+#include <bx/easing.h>
+#include <bx/crtimpl.h>
+#include <bx/handlealloc.h>
+
+#include "vs_particle.bin.h"
+#include "fs_particle.bin.h"
+
+static const bgfx::EmbeddedShader s_embeddedShaders[] =
+{
+ BGFX_EMBEDDED_SHADER(vs_particle),
+ BGFX_EMBEDDED_SHADER(fs_particle),
+
+ BGFX_EMBEDDED_SHADER_END()
+};
+
+static const bx::EaseFn s_easeFunc[] =
+{
+ bx::easeLinear,
+ bx::easeInQuad,
+ bx::easeOutQuad,
+ bx::easeInOutQuad,
+ bx::easeOutInQuad,
+ bx::easeInCubic,
+ bx::easeOutCubic,
+ bx::easeInOutCubic,
+ bx::easeOutInCubic,
+ bx::easeInQuart,
+ bx::easeOutQuart,
+ bx::easeInOutQuart,
+ bx::easeOutInQuart,
+ bx::easeInQuint,
+ bx::easeOutQuint,
+ bx::easeInOutQuint,
+ bx::easeOutInQuint,
+ bx::easeInSine,
+ bx::easeOutSine,
+ bx::easeInOutSine,
+ bx::easeOutInSine,
+ bx::easeInExpo,
+ bx::easeOutExpo,
+ bx::easeInOutExpo,
+ bx::easeOutInExpo,
+ bx::easeInCirc,
+ bx::easeOutCirc,
+ bx::easeInOutCirc,
+ bx::easeOutInCirc,
+ bx::easeInElastic,
+ bx::easeOutElastic,
+ bx::easeInOutElastic,
+ bx::easeOutInElastic,
+ bx::easeInBack,
+ bx::easeOutBack,
+ bx::easeInOutBack,
+ bx::easeOutInBack,
+ bx::easeInBounce,
+ bx::easeOutBounce,
+ bx::easeInOutBounce,
+ bx::easeOutInBounce,
+};
+BX_STATIC_ASSERT(BX_COUNTOF(s_easeFunc) == bx::Easing::Count);
+
+struct PosColorTexCoord0Vertex
+{
+ float m_x;
+ float m_y;
+ float m_z;
+ uint32_t m_abgr;
+ float m_u;
+ float m_v;
+ float m_blend;
+ float m_angle;
+
+ static void init()
+ {
+ ms_decl
+ .begin()
+ .add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)
+ .add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true)
+ .add(bgfx::Attrib::TexCoord0, 4, bgfx::AttribType::Float)
+ .end();
+ }
+
+ static bgfx::VertexDecl ms_decl;
+};
+
+bgfx::VertexDecl PosColorTexCoord0Vertex::ms_decl;
+
+void EmitterUniforms::reset()
+{
+ m_position[0] = 0.0f;
+ m_position[1] = 0.0f;
+ m_position[2] = 0.0f;
+
+ m_angle[0] = 0.0f;
+ m_angle[1] = 0.0f;
+ m_angle[2] = 0.0f;
+
+ m_particlesPerSecond = 0;
+
+ m_offsetStart[0] = 0.0f;
+ m_offsetStart[1] = 1.0f;
+ m_offsetEnd[0] = 2.0f;
+ m_offsetEnd[1] = 3.0f;
+
+ m_rgba[0] = 0x00ffffff;
+ m_rgba[1] = UINT32_MAX;
+ m_rgba[2] = UINT32_MAX;
+ m_rgba[3] = UINT32_MAX;
+ m_rgba[4] = 0x00ffffff;
+
+ m_blendStart[0] = 0.8f;
+ m_blendStart[1] = 1.0f;
+ m_blendEnd[0] = 0.0f;
+ m_blendEnd[1] = 0.2f;
+
+ m_scaleStart[0] = 0.1f;
+ m_scaleStart[1] = 0.2f;
+ m_scaleEnd[0] = 0.3f;
+ m_scaleEnd[1] = 0.4f;
+
+ m_lifeSpan[0] = 1.0f;
+ m_lifeSpan[1] = 2.0f;
+
+ m_gravityScale = 0.0f;
+
+ m_easePos = bx::Easing::Linear;
+ m_easeRgba = bx::Easing::Linear;
+ m_easeBlend = bx::Easing::Linear;
+ m_easeScale = bx::Easing::Linear;
+}
+
+namespace ps
+{
+ struct Particle
+ {
+ float start[3];
+ float end[2][3];
+ float blendStart;
+ float blendEnd;
+ float scaleStart;
+ float scaleEnd;
+
+ uint32_t rgba[5];
+
+ float life;
+ float lifeSpan;
+ };
+
+ struct ParticleSort
+ {
+ float dist;
+ uint32_t idx;
+ };
+
+ inline uint32_t toAbgr(const float* _rgba)
+ {
+ return 0
+ | (uint8_t(_rgba[0]*255.0f)<< 0)
+ | (uint8_t(_rgba[1]*255.0f)<< 8)
+ | (uint8_t(_rgba[2]*255.0f)<<16)
+ | (uint8_t(_rgba[3]*255.0f)<<24)
+ ;
+ }
+
+ inline uint32_t toAbgr(float _rr, float _gg, float _bb, float _aa)
+ {
+ return 0
+ | (uint8_t(_rr*255.0f)<< 0)
+ | (uint8_t(_gg*255.0f)<< 8)
+ | (uint8_t(_bb*255.0f)<<16)
+ | (uint8_t(_aa*255.0f)<<24)
+ ;
+ }
+
+ struct Emitter
+ {
+ void create(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles);
+ void destroy();
+
+ void reset()
+ {
+ m_num = 0;
+ memset(&m_aabb, 0, sizeof(Aabb) );
+ }
+
+ void update(float _dt)
+ {
+ uint32_t num = m_num;
+ for (uint32_t ii = 0; ii < num; ++ii)
+ {
+ Particle& particle = m_particles[ii];
+ particle.life += _dt * 1.0f/particle.lifeSpan;
+
+ if (particle.life > 1.0f)
+ {
+ if (ii != num-1)
+ {
+ memcpy(&particle, &m_particles[num-1], sizeof(Particle) );
+ --ii;
+ }
+
+ --num;
+ }
+ }
+
+ m_num = num;
+
+ if (0 < m_uniforms.m_particlesPerSecond)
+ {
+ spawn(_dt);
+ }
+ }
+
+ void spawn(float _dt)
+ {
+ float mtx[16];
+ bx::mtxSRT(mtx
+ , 1.0f, 1.0f, 1.0f
+ , m_uniforms.m_angle[0], m_uniforms.m_angle[1], m_uniforms.m_angle[2]
+ , m_uniforms.m_position[0], m_uniforms.m_position[1], m_uniforms.m_position[2]
+ );
+
+ const float timePerParticle = 1.0f/m_uniforms.m_particlesPerSecond;
+ m_dt += _dt;
+ const uint32_t numParticles = uint32_t(m_dt / timePerParticle);
+ m_dt -= numParticles * timePerParticle;
+
+ float time = 0.0f;
+ for (uint32_t ii = 0
+ ; ii < numParticles && m_num < m_max
+ ; ++ii
+ )
+ {
+ Particle& particle = m_particles[m_num];
+ m_num++;
+
+ const float up[3] = { 0.0f, 1.0f, 0.0f };
+
+ float pos[3];
+ switch (m_shape)
+ {
+ default:
+ case EmitterShape::Sphere:
+ bx::randUnitSphere(pos, &m_rng);
+ break;
+
+ case EmitterShape::Hemisphere:
+ bx::randUnitHemisphere(pos, &m_rng, up);
+ break;
+
+ case EmitterShape::Circle:
+ bx::randUnitCircle(pos, &m_rng);
+ break;
+
+ case EmitterShape::Disc:
+ {
+ float tmp[3];
+ bx::randUnitCircle(tmp, &m_rng);
+ bx::vec3Mul(pos, tmp, bx::frnd(&m_rng) );
+ }
+ break;
+
+ case EmitterShape::Rect:
+ pos[0] = bx::frndh(&m_rng);
+ pos[1] = 0.0f;
+ pos[2] = bx::frndh(&m_rng);
+ break;
+ }
+
+ float dir[3];
+ switch (m_direction)
+ {
+ default:
+ case EmitterDirection::Up:
+ bx::vec3Move(dir, up);
+ break;
+
+ case EmitterDirection::Outward:
+ bx::vec3Norm(dir, pos);
+ break;
+ }
+
+ float start[3];
+ float end[3];
+ const float startOffset = bx::flerp(m_uniforms.m_offsetStart[0], m_uniforms.m_offsetStart[1], bx::frnd(&m_rng) );
+ bx::vec3Mul(start, pos, startOffset);
+
+ const float endOffset = bx::flerp(m_uniforms.m_offsetEnd[0], m_uniforms.m_offsetEnd[1], bx::frnd(&m_rng) );
+ float tmp1[3];
+ bx::vec3Mul(tmp1, dir, endOffset);
+ bx::vec3Add(end, tmp1, start);
+
+ particle.life = time;
+ particle.lifeSpan = bx::flerp(m_uniforms.m_lifeSpan[0], m_uniforms.m_lifeSpan[1], bx::frnd(&m_rng) );
+
+ float gravity[3] = { 0.0f, -9.81f * m_uniforms.m_gravityScale * bx::fsq(particle.lifeSpan), 0.0f };
+
+ bx::vec3MulMtx(particle.start, start, mtx);
+ bx::vec3MulMtx(particle.end[0], end, mtx);
+ bx::vec3Add(particle.end[1], particle.end[0], gravity);
+
+ memcpy(particle.rgba, m_uniforms.m_rgba, BX_COUNTOF(m_uniforms.m_rgba)*sizeof(uint32_t) );
+
+ particle.blendStart = bx::flerp(m_uniforms.m_blendStart[0], m_uniforms.m_blendStart[1], bx::frnd(&m_rng) );
+ particle.blendEnd = bx::flerp(m_uniforms.m_blendEnd[0], m_uniforms.m_blendEnd[1], bx::frnd(&m_rng) );
+
+ particle.scaleStart = bx::flerp(m_uniforms.m_scaleStart[0], m_uniforms.m_scaleStart[1], bx::frnd(&m_rng) );
+ particle.scaleEnd = bx::flerp(m_uniforms.m_scaleEnd[0], m_uniforms.m_scaleEnd[1], bx::frnd(&m_rng) );
+
+ time += timePerParticle;
+ }
+ }
+
+ uint32_t render(const float* _mtxView, const float* _eye, uint32_t _first, uint32_t _max, ParticleSort* _outSort, PosColorTexCoord0Vertex* _outVertices)
+ {
+ bx::EaseFn easeRgba = s_easeFunc[m_uniforms.m_easeRgba];
+ bx::EaseFn easePos = s_easeFunc[m_uniforms.m_easePos];
+ bx::EaseFn easeBlend = s_easeFunc[m_uniforms.m_easeBlend];
+ bx::EaseFn easeScale = s_easeFunc[m_uniforms.m_easeScale];
+
+ Aabb aabb =
+ {
+ { bx::huge, bx::huge, bx::huge },
+ { -bx::huge, -bx::huge, -bx::huge },
+ };
+
+ for (uint32_t jj = 0, num = m_num, current = _first
+ ; jj < num && current < _max
+ ; ++jj, ++current
+ )
+ {
+ const Particle& particle = m_particles[jj];
+
+ const float ttPos = easePos(particle.life);
+ const float ttScale = easeScale(particle.life);
+ const float ttBlend = bx::fsaturate(easeBlend(particle.life) );
+ const float ttRgba = bx::fsaturate(easeRgba(particle.life) );
+
+ float p0[3];
+ bx::vec3Lerp(p0, particle.start, particle.end[0], ttPos);
+
+ float p1[3];
+ bx::vec3Lerp(p1, particle.end[0], particle.end[1], ttPos);
+
+ float pos[3];
+ bx::vec3Lerp(pos, p0, p1, ttPos);
+
+ ParticleSort& sort = _outSort[current];
+ float tmp[3];
+ bx::vec3Sub(tmp, _eye, pos);
+ sort.dist = bx::fsqrt(bx::vec3Dot(tmp, tmp) );
+ sort.idx = current;
+
+ uint32_t idx = uint32_t(ttRgba*4);
+ float ttmod = bx::fmod(ttRgba, 0.25f)/0.25f;
+ uint32_t rgbaStart = particle.rgba[idx];
+ uint32_t rgbaEnd = particle.rgba[idx+1];
+
+ float rr = bx::flerp( ( (uint8_t*)&rgbaStart)[0], ( (uint8_t*)&rgbaEnd)[0], ttmod)/255.0f;
+ float gg = bx::flerp( ( (uint8_t*)&rgbaStart)[1], ( (uint8_t*)&rgbaEnd)[1], ttmod)/255.0f;
+ float bb = bx::flerp( ( (uint8_t*)&rgbaStart)[2], ( (uint8_t*)&rgbaEnd)[2], ttmod)/255.0f;
+ float aa = bx::flerp( ( (uint8_t*)&rgbaStart)[3], ( (uint8_t*)&rgbaEnd)[3], ttmod)/255.0f;
+
+ float blend = bx::flerp(particle.blendStart, particle.blendEnd, ttBlend);
+ float scale = bx::flerp(particle.scaleStart, particle.scaleEnd, ttScale);
+
+ uint32_t abgr = toAbgr(rr, gg, bb, aa);
+
+ float udir[3] = { _mtxView[0]*scale, _mtxView[4]*scale, _mtxView[8]*scale };
+ float vdir[3] = { _mtxView[1]*scale, _mtxView[5]*scale, _mtxView[9]*scale };
+
+ PosColorTexCoord0Vertex* vertex = &_outVertices[current*4];
+ bx::vec3Sub(tmp, pos, udir);
+ bx::vec3Sub(&vertex->m_x, tmp, vdir);
+ aabbExpand(aabb, &vertex->m_x);
+ vertex->m_abgr = abgr;
+ vertex->m_u = 0.0f;
+ vertex->m_v = 0.0f;
+ vertex->m_blend = blend;
+ ++vertex;
+
+ bx::vec3Add(tmp, pos, udir);
+ bx::vec3Sub(&vertex->m_x, tmp, vdir);
+ aabbExpand(aabb, &vertex->m_x);
+ vertex->m_abgr = abgr;
+ vertex->m_u = 1.0f;
+ vertex->m_v = 0.0f;
+ vertex->m_blend = blend;
+ ++vertex;
+
+ bx::vec3Add(tmp, pos, udir);
+ bx::vec3Add(&vertex->m_x, tmp, vdir);
+ aabbExpand(aabb, &vertex->m_x);
+ vertex->m_abgr = abgr;
+ vertex->m_u = 1.0f;
+ vertex->m_v = 1.0f;
+ vertex->m_blend = blend;
+ ++vertex;
+
+ bx::vec3Sub(tmp, pos, udir);
+ bx::vec3Add(&vertex->m_x, tmp, vdir);
+ aabbExpand(aabb, &vertex->m_x);
+ vertex->m_abgr = abgr;
+ vertex->m_u = 0.0f;
+ vertex->m_v = 1.0f;
+ vertex->m_blend = blend;
+ ++vertex;
+ }
+
+ m_aabb = aabb;
+
+ return m_num;
+ }
+
+ EmitterShape::Enum m_shape;
+ EmitterDirection::Enum m_direction;
+
+ float m_dt;
+ bx::RngMwc m_rng;
+ EmitterUniforms m_uniforms;
+
+ Aabb m_aabb;
+
+ Particle* m_particles;
+ uint32_t m_num;
+ uint32_t m_max;
+ };
+
+ static int32_t particleSortFn(const void* _lhs, const void* _rhs)
+ {
+ const ParticleSort& lhs = *(const ParticleSort*)_lhs;
+ const ParticleSort& rhs = *(const ParticleSort*)_rhs;
+ return lhs.dist > rhs.dist ? -1 : 1;
+ }
+
+ struct ParticleSystem
+ {
+ void init(uint16_t _maxEmitters, bx::AllocatorI* _allocator)
+ {
+ m_allocator = _allocator;
+
+#if BX_CONFIG_ALLOCATOR_CRT
+ if (NULL == _allocator)
+ {
+ static bx::CrtAllocator allocator;
+ m_allocator = &allocator;
+ }
+#endif // BX_CONFIG_ALLOCATOR_CRT
+
+ m_emitterAlloc = bx::createHandleAlloc(m_allocator, _maxEmitters);
+ m_emitter = (Emitter*)BX_ALLOC(m_allocator, sizeof(Emitter)*_maxEmitters);
+
+ PosColorTexCoord0Vertex::init();
+
+ m_num = 0;
+
+ s_texColor = bgfx::createUniform("s_texColor", bgfx::UniformType::Int1);
+ m_particleTexture = loadTexture("textures/particle.ktx");
+
+ bgfx::RendererType::Enum type = bgfx::getRendererType();
+ m_particleProgram = bgfx::createProgram(
+ bgfx::createEmbeddedShader(s_embeddedShaders, type, "vs_particle")
+ , bgfx::createEmbeddedShader(s_embeddedShaders, type, "fs_particle")
+ , true
+ );
+ }
+
+ void shutdown()
+ {
+ bgfx::destroyProgram(m_particleProgram);
+ bgfx::destroyTexture(m_particleTexture);
+ bgfx::destroyUniform(s_texColor);
+
+ bx::destroyHandleAlloc(m_allocator, m_emitterAlloc);
+ BX_FREE(m_allocator, m_emitter);
+
+ m_allocator = NULL;
+ }
+
+ void update(float _dt)
+ {
+ uint32_t numParticles = 0;
+ for (uint16_t ii = 0, num = m_emitterAlloc->getNumHandles(); ii < num; ++ii)
+ {
+ const uint16_t idx = m_emitterAlloc->getHandleAt(ii);
+ Emitter& emitter = m_emitter[idx];
+ emitter.update(_dt);
+ numParticles += emitter.m_num;
+ }
+
+ m_num = numParticles;
+ }
+
+ void render(uint8_t _view, const float* _mtxView, const float* _eye)
+ {
+ if (0 != m_num)
+ {
+ bgfx::TransientVertexBuffer tvb;
+ bgfx::TransientIndexBuffer tib;
+
+ const uint32_t numVertices = bgfx::getAvailTransientVertexBuffer(m_num*4, PosColorTexCoord0Vertex::ms_decl);
+ const uint32_t numIndices = bgfx::getAvailTransientIndexBuffer(m_num*6);
+ const uint32_t max = bx::uint32_min(numVertices/4, numIndices/6);
+ BX_WARN(m_num == max
+ , "Truncating transient buffer for particles to maximum available (requested %d, available %d)."
+ , m_num
+ , max
+ );
+
+ if (0 < max)
+ {
+ bgfx::allocTransientBuffers(&tvb
+ , PosColorTexCoord0Vertex::ms_decl
+ , max*4
+ , &tib
+ , max*6
+ );
+ PosColorTexCoord0Vertex* vertices = (PosColorTexCoord0Vertex*)tvb.data;
+
+ ParticleSort* particleSort = (ParticleSort*)BX_ALLOC(m_allocator, max*sizeof(ParticleSort) );
+
+ uint32_t pos = 0;
+ for (uint16_t ii = 0, numEmitters = m_emitterAlloc->getNumHandles(); ii < numEmitters; ++ii)
+ {
+ const uint16_t idx = m_emitterAlloc->getHandleAt(ii);
+ Emitter& emitter = m_emitter[idx];
+ pos += emitter.render(_mtxView, _eye, pos, max, particleSort, vertices);
+ }
+
+ qsort(particleSort
+ , max
+ , sizeof(ParticleSort)
+ , particleSortFn
+ );
+
+ uint16_t* indices = (uint16_t*)tib.data;
+ for (uint32_t ii = 0; ii < max; ++ii)
+ {
+ const ParticleSort& sort = particleSort[ii];
+ uint16_t* index = &indices[ii*6];
+ uint16_t idx = (uint16_t)sort.idx;
+ index[0] = idx*4+0;
+ index[1] = idx*4+1;
+ index[2] = idx*4+2;
+ index[3] = idx*4+2;
+ index[4] = idx*4+3;
+ index[5] = idx*4+0;
+ }
+
+ BX_FREE(m_allocator, particleSort);
+
+ bgfx::setState(0
+ | BGFX_STATE_RGB_WRITE
+ | BGFX_STATE_ALPHA_WRITE
+ | BGFX_STATE_DEPTH_TEST_LESS
+ | BGFX_STATE_CULL_CW
+ | BGFX_STATE_BLEND_NORMAL
+ );
+ bgfx::setVertexBuffer(&tvb);
+ bgfx::setIndexBuffer(&tib);
+ bgfx::setTexture(0, s_texColor, m_particleTexture);
+ bgfx::submit(_view, m_particleProgram);
+ }
+ }
+ }
+
+ EmitterHandle createEmitter(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles)
+ {
+ EmitterHandle handle = { m_emitterAlloc->alloc() };
+
+ if (UINT16_MAX != handle.idx)
+ {
+ m_emitter[handle.idx].create(_shape, _direction, _maxParticles);
+ }
+
+ return handle;
+ }
+
+ void updateEmitter(EmitterHandle _handle, const EmitterUniforms* _uniforms)
+ {
+ BX_CHECK(m_emitterAlloc.isValid(_handle.idx)
+ , "destroyEmitter handle %d is not valid."
+ , _handle.idx
+ );
+
+ Emitter& emitter = m_emitter[_handle.idx];
+
+ if (NULL == _uniforms)
+ {
+ emitter.reset();
+ }
+ else
+ {
+ memcpy(&emitter.m_uniforms, _uniforms, sizeof(EmitterUniforms) );
+ }
+ }
+
+ void getAabb(EmitterHandle _handle, Aabb& _outAabb)
+ {
+ BX_CHECK(m_emitterAlloc.isValid(_handle.idx)
+ , "getAabb handle %d is not valid."
+ , _handle.idx
+ );
+ _outAabb = m_emitter[_handle.idx].m_aabb;
+ }
+
+ void destroyEmitter(EmitterHandle _handle)
+ {
+ BX_CHECK(m_emitterAlloc.isValid(_handle.idx)
+ , "destroyEmitter handle %d is not valid."
+ , _handle.idx
+ );
+
+ m_emitter[_handle.idx].destroy();
+ m_emitterAlloc->free(_handle.idx);
+ }
+
+ bx::AllocatorI* m_allocator;
+
+ bx::HandleAlloc* m_emitterAlloc;
+ Emitter* m_emitter;
+
+ bgfx::UniformHandle s_texColor;
+ bgfx::TextureHandle m_particleTexture;
+ bgfx::ProgramHandle m_particleProgram;
+
+ uint32_t m_num;
+ };
+
+ static ParticleSystem s_ctx;
+
+ void Emitter::create(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles)
+ {
+ m_dt = 0.0f;
+ m_uniforms.reset();
+ m_shape = _shape;
+ m_direction = _direction;
+
+ m_num = 0;
+ m_max = _maxParticles;
+ m_particles = (Particle*)BX_ALLOC(s_ctx.m_allocator, m_max*sizeof(Particle) );
+ }
+
+ void Emitter::destroy()
+ {
+ BX_FREE(s_ctx.m_allocator, m_particles);
+ m_particles = NULL;
+ }
+
+} // namespace ps
+
+using namespace ps;
+
+void psInit(uint16_t _maxEmitters, bx::AllocatorI* _allocator)
+{
+ s_ctx.init(_maxEmitters, _allocator);
+}
+
+void psShutdown()
+{
+ s_ctx.shutdown();
+}
+
+EmitterHandle psCreateEmitter(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles)
+{
+ return s_ctx.createEmitter(_shape, _direction, _maxParticles);
+}
+
+void psUpdateEmitter(EmitterHandle _handle, const EmitterUniforms* _uniforms)
+{
+ s_ctx.updateEmitter(_handle, _uniforms);
+}
+
+void psGetAabb(EmitterHandle _handle, Aabb& _outAabb)
+{
+ s_ctx.getAabb(_handle, _outAabb);
+}
+
+void psDestroyEmitter(EmitterHandle _handle)
+{
+ s_ctx.destroyEmitter(_handle);
+}
+
+void psUpdate(float _dt)
+{
+ s_ctx.update(_dt);
+}
+
+void psRender(uint8_t _view, const float* _mtxView, const float* _eye)
+{
+ s_ctx.render(_view, _mtxView, _eye);
+}