7#include <bgfx/embedded_shader.h>
13#include <bx/handlealloc.h>
33 .add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)
34 .add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8,
true)
35 .add(bgfx::Attrib::TexCoord0, 4, bgfx::AttribType::Float)
56 .add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)
57 .add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float)
68 {-0.5f, -0.5f, 0.0f, 0.0f, 1.0f},
69 { 0.5f, -0.5f, 0.0f, 1.0f, 1.0f},
70 { 0.5f, 0.5f, 0.0f, 1.0f, 0.0f},
71 {-0.5f, 0.5f, 0.0f, 0.0f, 0.0f}
74static const uint16_t s_quadIndices[6] = {
198 const math::vec3 initialVelocity = particle.
end[0] - particle.
start;
199 const math::vec3 finalVelocity = particle.
end[1] - particle.
end[0];
202 const math::vec3 currentVelocity = math::mix(initialVelocity, finalVelocity, ttPos);
205 const math::vec3 velocityPerSecond = currentVelocity * (1.0f / particle.
lifeSpan);
207 return math::length(velocityPerSecond);
212 float avgSystemScale,
214 bool hasColorBySpeed,
216 const math::mat4& effectiveTransform)
218 const float ttPos = easePos(particle.
life);
229 const float speedFactor =
242 particle.
color = sampledColor;
253 const float speedFactor =
259 const float sizeMultiplier =
261 scale *= sizeMultiplier;
268 const math::vec3 p0 = math::mix(particle.
start, particle.
end[0], ttPos);
269 const math::vec3 p1 = math::mix(particle.
end[0], particle.
end[1], ttPos);
270 const math::vec3 localPos = math::mix(p0, p1, ttPos);
275 const math::vec4 worldPos4 = effectiveTransform * math::vec4(localPos, 1.0f);
276 particle.
position = math::vec3(worldPos4.x, worldPos4.y, worldPos4.z);
287 auto& uniforms_ = *_uniforms;
292 uniforms_.m_prevTransform = uniforms_.m_transform;
296 bool was_loop =
loop_;
298 loop_ = uniforms_.m_loop;
306 if(uniforms_.m_paused)
314 uniforms_.m_playing =
false;
319 math::vec3 effectivePosition, effectiveScale, effectiveEmissionShapeScale;
320 math::mat4 effectiveTransform;
321 getEffectiveTransform(uniforms_, effectivePosition, effectiveScale, effectiveEmissionShapeScale, effectiveTransform);
326 aabb.
add_point(effectivePosition - math::vec3(0.5f));
327 aabb.
add_point(effectivePosition + math::vec3(0.5f));
332 const float avgSystemScale = (effectiveScale.x + effectiveScale.y + effectiveScale.z) / 3.0f;
333 const bx::EaseFn easePos = bx::getEaseFunc(uniforms_.m_easePos);
336 const bool hasColorBySpeed =
337 (uniforms_.m_colorBySpeedVelocityRange.max > uniforms_.m_colorBySpeedVelocityRange.min);
338 const bool hasSizeBySpeed =
339 (uniforms_.m_sizeBySpeedVelocityRange.max > uniforms_.m_sizeBySpeedVelocityRange.min &&
340 uniforms_.m_sizeBySpeedRange.min != uniforms_.m_sizeBySpeedRange.max);
344 for(uint32_t ii = 0; ii < num; ++ii)
349 if(particle.
life > 1.0f)
362 updateParticleProperties(uniforms_, particle, avgSystemScale, easePos, hasColorBySpeed, hasSizeBySpeed, effectiveTransform);
365 math::vec3 padding(particle.
scale * 0.5f);
372 if(0.0f < uniforms_.m_emissionLifetime && uniforms_.m_playing)
377 if(uniforms_.m_loop || !initial_emission_complete)
379 spawn(uniforms_, aabb,_dt);
400 math::vec3& outPosition,
401 math::vec3& outScale,
402 math::vec3& outEmissionShapeScale,
403 math::mat4& outTransformMatrix)
const
425 const uint32_t numParticlesToEmit = uint32_t(
dt_ / timePerParticle);
426 dt_ -= numParticlesToEmit * timePerParticle;
430 const uint32_t actualEmitCount = math::min(numParticlesToEmit, maxEmittable);
432 if(actualEmitCount == 0)
438 math::vec3 effectivePosition, effectiveScale, effectiveEmissionShapeScale;
439 math::mat4 effectiveTransform;
440 getEffectiveTransform(uniforms_, effectivePosition, effectiveScale, effectiveEmissionShapeScale, effectiveTransform);
443 const float avgSystemScale = (effectiveScale.x + effectiveScale.y + effectiveScale.z) / 3.0f;
444 const bx::EaseFn easePos = bx::getEaseFunc(uniforms_.
m_easePos);
445 const bool hasColorBySpeed =
447 const bool hasSizeBySpeed =
452 const bool hasLifetimeByEmitterSpeed =
454 float emitterSpeed = 0.0f;
455 float lifetimeMultiplier = 1.0f;
456 if(hasLifetimeByEmitterSpeed && _dt > 0.0f)
462 const math::vec3 motionDelta = currentPos - prevPos;
463 emitterSpeed = math::length(motionDelta) / _dt;
466 const float speedFactor =
475 const math::mat3 rotationMatrix = math::mat3(effectiveTransform);
478 const math::vec3 systemScale = effectiveScale;
479 const math::vec3 emissionShapeScale = effectiveEmissionShapeScale;
481 const float lifeSpanSquared = lifeSpan * lifeSpan;
482 const math::vec3 gravityVector = math::vec3(0.0f, -9.81f * uniforms_.
m_gravityScale * lifeSpanSquared * systemScale.y, 0.0f);
483 const math::vec3 forceOverLifetimeVector = uniforms_.
m_forceOverLifetime * lifeSpanSquared * systemScale;
487 const math::vec3 currentPos = effectivePosition;
490 const math::vec3 up = math::vec3(0.0f, 1.0f, 0.0f);
493 for(uint32_t ii = 0; ii < actualEmitCount; ++ii)
497 const float baseEmissionPhase = float(ii) / float(actualEmitCount);
511 pos = math::ballRand(1.0f);
516 math::vec3 spherePos = math::ballRand(1.0f);
517 if(math::dot(spherePos, up) < 0.0f)
518 spherePos = -spherePos;
525 math::vec2 circlePos = math::diskRand(1.0f);
526 pos = math::vec3(circlePos.x, 0.0f, circlePos.y);
531 pos = math::vec3(math::linearRand(-1.0f, 1.0f),
532 math::linearRand(-1.0f, 1.0f),
533 math::linearRand(-1.0f, 1.0f));
537 pos = math::vec3(math::linearRand(-1.0f, 1.0f), 0.0f, math::linearRand(-1.0f, 1.0f));
542 pos = pos * emissionShapeScale;
553 dir = math::normalize(pos);
558 const math::vec3 scaledPos = systemScale * pos;
559 const math::vec3 start = scaledPos;
563 const float endVelocity = math::mix(endVelocityRange.
min, endVelocityRange.
max, bx::frnd(&
rng_));
564 const math::vec3 scaledDir = systemScale * dir;
565 const math::vec3 tmp1 = scaledDir * endVelocity;
566 const math::vec3 end = tmp1 + start;
568 particle->
life = 0.0f;
569 particle->
lifeSpan = lifeSpan * lifetimeMultiplier;
572 math::vec3 interpolatedEmitterPos = math::mix(prevPos, currentPos, emissionPhase);
578 particle->
start = start;
579 particle->
end[0] = end;
584 particle->
start = rotationMatrix * start + interpolatedEmitterPos;
585 particle->
end[0] = rotationMatrix * end + interpolatedEmitterPos;
591 const math::vec3 velocity = particle->
end[0] - particle->
start;
592 const math::vec3 dampedVelocity = velocity * velocityDampingFactor;
593 particle->
end[0] = particle->
start + dampedVelocity;
597 const math::vec3 totalForce = gravityVector + forceOverLifetimeVector;
598 particle->
end[1] = particle->
end[0] + totalForce;
615 updateParticleProperties(uniforms_, *particle, avgSystemScale, easePos, hasColorBySpeed, hasSizeBySpeed, effectiveTransform);
618 math::vec3 padding(particle->
scale * 0.5f);
645static int32_t particleSortFn(
const void* _lhs,
const void* _rhs)
649 return lhs.
dist > rhs.
dist ? -1 : 1;
654 void init(uint16_t _maxEmitters, bx::AllocatorI* _allocator)
658 if(
nullptr == _allocator)
660 static bx::DefaultAllocator allocator;
673 bgfx::makeRef(s_quadVertices,
sizeof(s_quadVertices)),
678 bgfx::makeRef(s_quadIndices,
sizeof(s_quadIndices))
681 s_texColor = bgfx::createUniform(
"s_texColor", bgfx::UniformType::Sampler);
699 bgfx::ProgramHandle _program,
700 const float* _mtxView,
701 const math::vec3& _eye,
702 bgfx::TextureHandle _texture)
704 BX_ASSERT(
isValid(_handle),
"renderEmitterById handle %d is not valid.", _handle.
idx);
714 const uint16_t instanceStride = 32;
717 uint32_t maxInstances = bgfx::getAvailInstanceDataBuffer(emitter.
num_particles_, instanceStride);
719 if(maxInstances == 0)
721 BX_WARN(
false,
"No instance buffer space available.");
726 bgfx::InstanceDataBuffer idb;
727 bgfx::allocInstanceDataBuffer(&idb, maxInstances, instanceStride);
737 bgfx::setInstanceDataBuffer(&idb);
740 bgfx::setState(0 | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A |
741 BGFX_STATE_DEPTH_TEST_LESS | BGFX_STATE_CULL_CW |
742 BGFX_STATE_BLEND_NORMAL);
746 bgfx::submit(_view, _program);
749 void generateInstanceData(
Emitter& emitter, bgfx::InstanceDataBuffer& idb, uint32_t maxInstances, uint16_t instanceStride,
const math::vec3& _eye)
751 uint8_t* data = idb.data;
757 const math::vec3 tmp0 = _eye - particle.
position;
766 uint32_t numToRender = math::min(emitter.
num_particles_, maxInstances);
767 for(uint32_t i = 0; i < numToRender; ++i)
773 float* posScale = (
float*)data;
777 posScale[3] = particle.
scale;
780 float* colorBlend = (
float*)&data[16];
787 float* rotationBlend = (
float*)&data[32];
788 rotationBlend[0] = 0.0f;
789 rotationBlend[1] = particle.
blend;
790 rotationBlend[2] = 0.0f;
791 rotationBlend[3] = 0.0f;
793 data += instanceStride;
810 return lhs.
dist > rhs.
dist ? -1 : 1;
814 uint8_t _view, bgfx::ProgramHandle _program,
815 const float* _mtxView,
const math::vec3& _eye,
816 bgfx::TextureHandle _texture)
818 if(_count == 0 || !bgfx::isValid(_texture))
823 APP_SCOPE_PERF(
"Rendering/Particle Pass/Render Batched Emitters");
827 uint32_t totalParticles = 0;
828 for(uint32_t i = 0; i < _count; ++i)
839 if(totalParticles == 0)
845 const uint16_t instanceStride = 48;
848 uint32_t maxInstances = bgfx::getAvailInstanceDataBuffer(totalParticles, instanceStride);
850 if(maxInstances == 0)
852 BX_WARN(
false,
"No instance buffer space available for batch rendering.");
857 bgfx::InstanceDataBuffer idb;
858 bgfx::allocInstanceDataBuffer(&idb, maxInstances, instanceStride);
868 bgfx::setInstanceDataBuffer(&idb);
871 bgfx::setState(0 | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A |
872 BGFX_STATE_DEPTH_TEST_LESS | BGFX_STATE_CULL_CW |
873 BGFX_STATE_BLEND_NORMAL);
877 bgfx::submit(_view, _program);
879 return totalParticles;
883 bgfx::InstanceDataBuffer& idb, uint32_t maxInstances,
884 uint16_t instanceStride,
const math::vec3& _eye)
887 static std::vector<BatchedParticle> batchedParticles;
888 batchedParticles.clear();
890 for(uint32_t emitterIdx = 0; emitterIdx < _count; ++emitterIdx)
892 if(!
isValid(_handles[emitterIdx]))
899 batchedParticles.reserve(batchedParticles.size() + emitter.
num_particles_);
900 for(uint32_t particleIdx = 0; particleIdx < emitter.
num_particles_; ++particleIdx)
903 const math::vec3 tmp0 = _eye - particle.
position;
904 const float distSquared = math::dot(tmp0, tmp0);
906 batchedParticles.emplace_back(
BatchedParticle{distSquared, emitterIdx, particleIdx});
911 std::sort(batchedParticles.begin(), batchedParticles.end(),
913 return a.dist > b.dist;
917 uint8_t* data = idb.data;
918 uint32_t numToRender = math::min(
static_cast<uint32_t
>(batchedParticles.size()), maxInstances);
920 for(uint32_t i = 0; i < numToRender; ++i)
927 float* posScale = (
float*)data;
931 posScale[3] = particle.
scale;
934 float* colorBlend = (
float*)&data[16];
941 float* rotationBlend = (
float*)&data[32];
942 rotationBlend[0] = 0.0f;
943 rotationBlend[1] = particle.
blend;
944 rotationBlend[2] = 0.0f;
945 rotationBlend[3] = 0.0f;
946 data += instanceStride;
954 if(UINT16_MAX !=
handle.idx)
964 BX_ASSERT(
isValid(_handle),
"destroyEmitter handle %d is not valid.", _handle.
idx);
968 if(
nullptr == _uniforms)
975 emitter.
update(_uniforms, _dt);
981 BX_ASSERT(
isValid(_handle),
"getAabb handle %d is not valid.", _handle.
idx);
986 BX_ASSERT(
isValid(_handle),
"getNumParticles handle %d is not valid.", _handle.
idx);
992 BX_ASSERT(
isValid(_handle),
"hasUpdated handle %d is not valid.", _handle.
idx);
998 BX_ASSERT(
isValid(_handle),
"destroyEmitter handle %d is not valid.", _handle.
idx);
1041void psInit(uint16_t _maxEmitters, bx::AllocatorI* _allocator)
1043 s_ctx.
init(_maxEmitters, _allocator);
1055 return s_ctx.
createEmitter(_shape, _direction, _maxParticles);
1065 BX_ASSERT(
isValid(_handle),
"psResetEmitter handle %d is not valid.", _handle.
idx);
1072 s_ctx.
getAabb(_handle, _outAabb);
1092 bgfx::ProgramHandle _program,
1093 const float* _mtxView,
1094 const math::vec3& _eye,
1095 bgfx::TextureHandle _texture)
1103 bgfx::ProgramHandle _program,
1104 const float* _mtxView,
1105 const math::vec3& _eye,
1106 bgfx::TextureHandle _texture)
1108 return s_ctx.
renderEmitterBatch(_handles, _count, _view, _program, _mtxView, _eye, _texture);
auto add_point(const T &element, float progress) -> size_t
void generate_lut(size_t lut_size=256)
auto sample(float progress) const -> T
transform_t< float > transform
void psResetEmitter(EmitterHandle _handle)
bool psHasUpdated(EmitterHandle _handle)
EmitterHandle psCreateEmitter(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles)
uint32_t psRenderEmitterBatch(const EmitterHandle *_handles, uint32_t _count, uint8_t _view, bgfx::ProgramHandle _program, const float *_mtxView, const math::vec3 &_eye, bgfx::TextureHandle _texture)
uint32_t psGetNumParticles(EmitterHandle _handle)
void psGetAabb(EmitterHandle _handle, math::bbox &_outAabb)
void psRenderEmitter(EmitterHandle _handle, uint8_t _view, bgfx::ProgramHandle _program, const float *_mtxView, const math::vec3 &_eye, bgfx::TextureHandle _texture)
void psUpdateEmitter(EmitterHandle _handle, float _dt, EmitterUniforms *_uniforms)
void psDestroyEmitter(EmitterHandle _handle)
void psInit(uint16_t _maxEmitters, bx::AllocatorI *_allocator)
#define APP_SCOPE_PERF(name)
Create a scoped performance timer that only accepts string literals.
static bgfx::VertexLayout ms_layout
static bgfx::VertexLayout ms_layout
Storage for box vector values and wraps up common functionality.
bbox & add_point(const vec3 &point)
Grows the bounding box based on the point passed.
void reset()
Resets the bounding box values.
float calculateParticleSpeed(const Particle &particle, float ttPos) const
void create(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles)
void getEffectiveTransform(const EmitterUniforms &uniforms_, math::vec3 &outPosition, math::vec3 &outScale, math::vec3 &outEmissionShapeScale, math::mat4 &outTransformMatrix) const
void spawn(EmitterUniforms &uniforms_, math::bbox &aabb, float _dt)
void updateParticleProperties(EmitterUniforms &uniforms_, Particle &particle, float avgSystemScale, bx::EaseFn easePos, bool hasColorBySpeed, bool hasSizeBySpeed, const math::mat4 &effectiveTransform)
uint32_t total_particles_spawned_
void update(EmitterUniforms *_uniforms, float _dt)
ParticleSort * particle_sort_
EmitterShape::Enum shape_
EmitterDirection::Enum direction_
EmitterHandle createEmitter(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles)
uint32_t getNumParticles(EmitterHandle _handle)
bool hasUpdated(EmitterHandle _handle)
void getAabb(EmitterHandle _handle, math::bbox &_outAabb)
bgfx::VertexBufferHandle m_quadVBH
void updateEmitter(EmitterHandle _handle, float _dt, EmitterUniforms *_uniforms)
static int32_t batchedParticleSortFn(const void *_lhs, const void *_rhs)
bx::AllocatorI * m_allocator
void generateInstanceData(Emitter &emitter, bgfx::InstanceDataBuffer &idb, uint32_t maxInstances, uint16_t instanceStride, const math::vec3 &_eye)
uint32_t renderEmitterBatch(const EmitterHandle *_handles, uint32_t _count, uint8_t _view, bgfx::ProgramHandle _program, const float *_mtxView, const math::vec3 &_eye, bgfx::TextureHandle _texture)
void init(uint16_t _maxEmitters, bx::AllocatorI *_allocator)
void destroyEmitter(EmitterHandle _handle)
void generateBatchedInstanceData(const EmitterHandle *_handles, uint32_t _count, bgfx::InstanceDataBuffer &idb, uint32_t maxInstances, uint16_t instanceStride, const math::vec3 &_eye)
bx::HandleAlloc * m_emitterAlloc
bgfx::IndexBufferHandle m_quadIBH
void renderEmitterById(EmitterHandle _handle, uint8_t _view, bgfx::ProgramHandle _program, const float *_mtxView, const math::vec3 &_eye, bgfx::TextureHandle _texture)
std::vector< Emitter > m_emitter
bgfx::UniformHandle s_texColor
bool isValid(SpriteHandle _handle)