From f9934d52539ecb414831834d4dda1b1e85e63d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Tue, 3 Jan 2017 21:36:49 -0800 Subject: [PATCH] Cleanup. --- examples/32-particles/particles.cpp | 2 +- examples/common/ps/particle_system.cpp | 915 +++++++++++++------------ examples/common/ps/particle_system.h | 2 +- 3 files changed, 470 insertions(+), 449 deletions(-) diff --git a/examples/32-particles/particles.cpp b/examples/32-particles/particles.cpp index 45316eb0e..ce084429e 100644 --- a/examples/32-particles/particles.cpp +++ b/examples/32-particles/particles.cpp @@ -110,7 +110,7 @@ struct Emitter // if (ImGui::CollapsingHeader("General") ) { if (ImGui::Combo("Shape", (int*)&m_shape, s_shapeNames, BX_COUNTOF(s_shapeNames) ) - || ImGui::Combo("Direction", (int*)&m_direction, s_directionName, BX_COUNTOF(s_directionName) ) ) + || ImGui::Combo("Direction", (int*)&m_direction, s_directionName, BX_COUNTOF(s_directionName) ) ) { psDestroyEmitter(m_handle); m_handle = psCreateEmitter(m_shape, m_direction, 1024); diff --git a/examples/common/ps/particle_system.cpp b/examples/common/ps/particle_system.cpp index f44658b16..1918d28f3 100644 --- a/examples/common/ps/particle_system.cpp +++ b/examples/common/ps/particle_system.cpp @@ -96,286 +96,6 @@ struct PosColorTexCoord0Vertex bgfx::VertexDecl PosColorTexCoord0Vertex::ms_decl; -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; - } - - 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) const - { - 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]; - - 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); - 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); - 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); - 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); - vertex->m_abgr = abgr; - vertex->m_u = 0.0f; - vertex->m_v = 1.0f; - vertex->m_blend = blend; - ++vertex; - } - - return m_num; - } - - EmitterShape::Enum m_shape; - EmitterDirection::Enum m_direction; - - float m_dt; - bx::RngMwc m_rng; - EmitterUniforms m_uniforms; - - Particle* m_particles; - uint32_t m_num; - uint32_t m_max; -}; - void EmitterUniforms::reset() { m_position[0] = 0.0f; @@ -420,234 +140,535 @@ void EmitterUniforms::reset() m_easeScale = bx::Easing::Linear; } -static int32_t particleSortFn(const void* _lhs, const void* _rhs) +namespace ps { - const ParticleSort& lhs = *(const ParticleSort*)_lhs; - const ParticleSort& rhs = *(const ParticleSort*)_rhs; - return lhs.dist > rhs.dist ? -1 : 1; -} - -struct ParticleSystem -{ - void init(bx::AllocatorI* _allocator) + struct Particle { - m_allocator = _allocator; + float start[3]; + float end[2][3]; + float blendStart; + float blendEnd; + float scaleStart; + float scaleEnd; -#if BX_CONFIG_ALLOCATOR_CRT - if (NULL == _allocator) - { - static bx::CrtAllocator allocator; - m_allocator = &allocator; - } -#endif // BX_CONFIG_ALLOCATOR_CRT + uint32_t rgba[5]; - PosColorTexCoord0Vertex::init(); + float life; + float lifeSpan; + }; - m_num = 0; + struct ParticleSort + { + float dist; + uint32_t idx; + }; - 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 - ); + 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) + ; } - void shutdown() + inline uint32_t toAbgr(float _rr, float _gg, float _bb, float _aa) { - bgfx::destroyProgram(m_particleProgram); - bgfx::destroyTexture(m_particleTexture); - bgfx::destroyUniform(s_texColor); - - m_allocator = NULL; + 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) + ; } - void update(float _dt) + struct Emitter { - uint32_t numParticles = 0; - for (uint16_t ii = 0, num = m_emitterAlloc.getNumHandles(); ii < num; ++ii) + void create(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles); + void destroy(); + + void reset() { - const uint16_t idx = m_emitterAlloc.getHandleAt(ii); - Emitter& emitter = m_emitter[idx]; - emitter.update(_dt); - numParticles += emitter.m_num; + m_num = 0; } - m_num = numParticles; - } - - void render(uint8_t _view, const float* _mtxView, const float* _eye) - { - if (0 != m_num) + void update(float _dt) { - 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) + uint32_t num = m_num; + for (uint32_t ii = 0; ii < num; ++ii) { - bgfx::allocTransientBuffers(&tvb - , PosColorTexCoord0Vertex::ms_decl - , max*4 - , &tib - , max*6 - ); - PosColorTexCoord0Vertex* vertices = (PosColorTexCoord0Vertex*)tvb.data; + Particle& particle = m_particles[ii]; + particle.life += _dt * 1.0f/particle.lifeSpan; - 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) + if (particle.life > 1.0f) { - const uint16_t idx = m_emitterAlloc.getHandleAt(ii); - const Emitter& emitter = m_emitter[idx]; - pos += emitter.render(_mtxView, _eye, pos, max, particleSort, vertices); + if (ii != num-1) + { + memcpy(&particle, &m_particles[num-1], sizeof(Particle) ); + --ii; + } + + --num; } + } - qsort(particleSort - , max - , sizeof(ParticleSort) - , particleSortFn - ); + m_num = num; - 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); + if (0 < m_uniforms.m_particlesPerSecond) + { + spawn(_dt); } } - } - EmitterHandle createEmitter(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles) - { - EmitterHandle handle = { m_emitterAlloc.alloc() }; - - if (UINT16_MAX != handle.idx) + void spawn(float _dt) { - m_emitter[handle.idx].create(_shape, _direction, _maxParticles); + 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; + } } - return handle; - } - - void updateEmitter(EmitterHandle _handle, const EmitterUniforms* _uniforms) - { - Emitter& emitter = m_emitter[_handle.idx]; - - if (NULL == _uniforms) + uint32_t render(const float* _mtxView, const float* _eye, uint32_t _first, uint32_t _max, ParticleSort* _outSort, PosColorTexCoord0Vertex* _outVertices) const { - emitter.reset(); - } - else - { - memcpy(&emitter.m_uniforms, _uniforms, sizeof(EmitterUniforms) ); - } - } + 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]; - void destroyEmitter(EmitterHandle _handle) + 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); + 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); + 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); + 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); + vertex->m_abgr = abgr; + vertex->m_u = 0.0f; + vertex->m_v = 1.0f; + vertex->m_blend = blend; + ++vertex; + } + + return m_num; + } + + EmitterShape::Enum m_shape; + EmitterDirection::Enum m_direction; + + float m_dt; + bx::RngMwc m_rng; + EmitterUniforms m_uniforms; + + Particle* m_particles; + uint32_t m_num; + uint32_t m_max; + }; + + static int32_t particleSortFn(const void* _lhs, const void* _rhs) { - m_emitter[_handle.idx].destroy(); - m_emitterAlloc.free(_handle.idx); + const ParticleSort& lhs = *(const ParticleSort*)_lhs; + const ParticleSort& rhs = *(const ParticleSort*)_rhs; + return lhs.dist > rhs.dist ? -1 : 1; } - bx::AllocatorI* m_allocator; + struct ParticleSystem + { + void init(uint16_t _maxEmitters, bx::AllocatorI* _allocator) + { + m_allocator = _allocator; -#define MAX_EMITTERS 64 - bx::HandleAllocT m_emitterAlloc; - Emitter m_emitter[MAX_EMITTERS]; +#if BX_CONFIG_ALLOCATOR_CRT + if (NULL == _allocator) + { + static bx::CrtAllocator allocator; + m_allocator = &allocator; + } +#endif // BX_CONFIG_ALLOCATOR_CRT - bgfx::UniformHandle s_texColor; - bgfx::TextureHandle m_particleTexture; - bgfx::ProgramHandle m_particleProgram; + m_emitterAlloc = bx::createHandleAlloc(m_allocator, _maxEmitters); + m_emitter = (Emitter*)BX_ALLOC(m_allocator, sizeof(Emitter)*_maxEmitters); - uint32_t m_num; -}; + PosColorTexCoord0Vertex::init(); -static ParticleSystem s_ps; + m_num = 0; -void Emitter::create(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles) + 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); + const 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 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) { - 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_ps.m_allocator, m_max*sizeof(Particle) ); -} - -void Emitter::destroy() -{ - BX_FREE(s_ps.m_allocator, m_particles); - m_particles = NULL; -} - -void psInit(bx::AllocatorI* _allocator) -{ - s_ps.init(_allocator); + s_ctx.init(_maxEmitters, _allocator); } void psShutdown() { - s_ps.shutdown(); + s_ctx.shutdown(); } EmitterHandle psCreateEmitter(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles) { - return s_ps.createEmitter(_shape, _direction, _maxParticles); + return s_ctx.createEmitter(_shape, _direction, _maxParticles); } void psUpdateEmitter(EmitterHandle _handle, const EmitterUniforms* _uniforms) { - s_ps.updateEmitter(_handle, _uniforms); + s_ctx.updateEmitter(_handle, _uniforms); } void psDestroyEmitter(EmitterHandle _handle) { - s_ps.destroyEmitter(_handle); + s_ctx.destroyEmitter(_handle); } void psUpdate(float _dt) { - s_ps.update(_dt); + s_ctx.update(_dt); } void psRender(uint8_t _view, const float* _mtxView, const float* _eye) { - s_ps.render(_view, _mtxView, _eye); + s_ctx.render(_view, _mtxView, _eye); } diff --git a/examples/common/ps/particle_system.h b/examples/common/ps/particle_system.h index c2366ac52..8c80f3e90 100644 --- a/examples/common/ps/particle_system.h +++ b/examples/common/ps/particle_system.h @@ -63,7 +63,7 @@ struct EmitterUniforms struct EmitterHandle { uint16_t idx; }; /// -void psInit(bx::AllocatorI* _allocator = NULL); +void psInit(uint16_t _maxEmitters = 64, bx::AllocatorI* _allocator = NULL); /// void psShutdown();