7#include <bgfx/embedded_shader.h>
14#include <bx/handlealloc.h>
16#include <glm/gtc/random.hpp>
24#define POOLSTL_STD_SUPPLEMENT 1
25#include <poolstl/poolstl.hpp>
38 .add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)
39 .add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float)
50 {-0.5f, -0.5f, 0.0f, 0.0f, 1.0f},
51 { 0.5f, -0.5f, 0.0f, 1.0f, 1.0f},
52 { 0.5f, 0.5f, 0.0f, 1.0f, 0.0f},
53 {-0.5f, 0.5f, 0.0f, 0.0f, 0.0f}
140 m_pivot = math::vec2(0.5f, 0.5f);
179constexpr float k_min_particle_lifespan = 1.0e-4f;
180constexpr float k_emit_dir_zero_len_sq = 1.0e-12f;
210 pivot_ = math::vec2(0.5f, 0.5f);
241 float total_distance = 0.0f;
242 float total_time = 0.0f;
247 total_distance += math::length(delta);
251 if(total_time > 0.0f)
253 return total_distance / total_time;
263 const math::vec3 initialVelocity = particle.
end[0] - particle.
start;
264 const math::vec3 finalVelocity = particle.
end[1] - particle.
end[0];
267 const math::vec3 currentVelocity = math::mix(initialVelocity, finalVelocity, ttPos);
270 const math::vec3 velocityPerSecond = currentVelocity * (1.0f / particle.
lifeSpan);
272 return math::length(velocityPerSecond);
278 math::vec3& out_min, math::vec3& out_max)
const
283 const math::vec2 pivot_offset = pivot - math::vec2(0.5f, 0.5f);
284 const math::vec3 pivot_shift_local = math::vec3(
285 pivot_offset.x * half_extents.x * 2.0f,
286 pivot_offset.y * half_extents.y * 2.0f,
292 if(rot_len_sq < 0.01f)
295 out_min = pivot_shift_local - half_extents;
296 out_max = pivot_shift_local + half_extents;
301 const math::mat3 rot_matrix = math::mat3_cast(
rotation);
304 const math::vec3 pivot_shift_rotated = rot_matrix * pivot_shift_local;
308 math::vec3 aabb_half_extents(0.0f);
309 for(
int i = 0; i < 3; ++i)
311 aabb_half_extents.x += math::abs(rot_matrix[i].
x) * half_extents[i];
312 aabb_half_extents.y += math::abs(rot_matrix[i].
y) * half_extents[i];
313 aabb_half_extents.z += math::abs(rot_matrix[i].
z) * half_extents[i];
318 out_min = pivot_shift_rotated - aabb_half_extents;
319 out_max = pivot_shift_rotated + aabb_half_extents;
324 float avgSystemScale,
326 bool hasColorBySpeed,
328 const math::mat4& effectiveTransform)
330 const float ttPos = easePos(particle.
life);
341 const float speedFactor =
354 particle.
color = sampledColor;
366 const float speedFactor =
372 const float sizeMultiplier =
374 scale *= sizeMultiplier;
381 const math::vec3 p0 = math::mix(particle.
start, particle.
end[0], ttPos);
382 const math::vec3 p1 = math::mix(particle.
end[0], particle.
end[1], ttPos);
383 const math::vec3 localPos = math::mix(p0, p1, ttPos);
388 const math::vec4 worldPos4 = effectiveTransform * math::vec4(localPos, 1.0f);
389 particle.
position = math::vec3(worldPos4.x, worldPos4.y, worldPos4.z);
402 const math::vec3 velocity0 = particle.
end[0] - particle.
start;
403 const math::vec3 velocity1 = particle.
end[1] - particle.
end[0];
404 const math::vec3 current_velocity = math::mix(velocity0, velocity1, ttPos);
406 const float velocity_len_sq = math::dot(current_velocity, current_velocity);
407 if(velocity_len_sq > 0.0001f)
410 const math::vec3 direction = math::normalize(current_velocity);
414 math::vec3 up_ref(0.0f, 1.0f, 0.0f);
415 if(math::abs(math::dot(direction, up_ref)) > 0.99f)
417 up_ref = math::vec3(1.0f, 0.0f, 0.0f);
431 particle.
rotation = math::identity<math::quat>();
450 animProgress = math::fmod(animProgress, 1.0f);
451 const uint32_t currentFrame = uint32_t(animProgress *
float(totalFrames)) % totalFrames;
454 const uint32_t tileX = currentFrame % uint32_t(uniforms_.
m_texSheetTiles.x);
455 const uint32_t tileY = currentFrame / uint32_t(uniforms_.
m_texSheetTiles.x);
458 particle.
uv_offset = math::vec2(
float(tileX) * uvScaleX,
float(tileY) * uvScaleY);
459 particle.
uv_scale = math::vec2(uvScaleX, uvScaleY);
464 particle.
uv_offset = math::vec2(0.0f, 0.0f);
465 particle.
uv_scale = math::vec2(1.0f, 1.0f);
471 auto& uniforms_ = *_uniforms;
478 pivot_ = uniforms_.m_pivot;
482 uniforms_.m_prevTransform = uniforms_.m_transform;
486 bool was_loop =
loop_;
488 loop_ = uniforms_.m_loop;
507 if(uniforms_.m_paused)
512 const math::vec3 currentPos = uniforms_.m_transform.get_position();
513 if(!uniforms_.m_paused)
520 uniforms_.m_playing =
false;
525 math::vec3 effectivePosition, effectiveScale, effectiveEmissionShapeScale;
526 math::mat4 effectiveTransform;
527 getEffectiveTransform(uniforms_, effectivePosition, effectiveScale, effectiveEmissionShapeScale, effectiveTransform);
532 aabb.
add_point(effectivePosition - math::vec3(0.5f));
533 aabb.
add_point(effectivePosition + math::vec3(0.5f));
538 const float avgSystemScale = (effectiveScale.x + effectiveScale.y + effectiveScale.z) / 3.0f;
539 const bx::EaseFn easePos = bx::getEaseFunc(uniforms_.m_easePos);
542 const bool hasColorBySpeed =
543 (uniforms_.m_colorBySpeedVelocityRange.max > uniforms_.m_colorBySpeedVelocityRange.min);
544 const bool hasSizeBySpeed =
545 (uniforms_.m_sizeBySpeedVelocityRange.max > uniforms_.m_sizeBySpeedVelocityRange.min &&
546 uniforms_.m_sizeBySpeedRange.min != uniforms_.m_sizeBySpeedRange.max);
550 for(uint32_t ii = 0; ii < num; ++ii)
565 if(particle.
life > 1.0f)
578 updateParticleProperties(uniforms_, particle, avgSystemScale, easePos, hasColorBySpeed, hasSizeBySpeed, effectiveTransform);
582 math::vec3 aabb_min, aabb_max;
591 if(0.0f < uniforms_.m_emissionLifetime && uniforms_.m_playing)
599 if(start_delay_elapsed && (uniforms_.m_loop || !initial_emission_complete))
601 spawn(uniforms_, aabb,_dt);
622 math::vec3& outPosition,
623 math::vec3& outScale,
624 math::vec3& outEmissionShapeScale,
625 math::mat4& outTransformMatrix)
const
647 const uint32_t numParticlesToEmit = uint32_t(
dt_ / timePerParticle);
648 dt_ -= numParticlesToEmit * timePerParticle;
652 const uint32_t actualEmitCount = math::min(numParticlesToEmit, maxEmittable);
654 if(actualEmitCount == 0)
660 math::vec3 effectivePosition, effectiveScale, effectiveEmissionShapeScale;
661 math::mat4 effectiveTransform;
662 getEffectiveTransform(uniforms_, effectivePosition, effectiveScale, effectiveEmissionShapeScale, effectiveTransform);
665 const float avgSystemScale = (effectiveScale.x + effectiveScale.y + effectiveScale.z) / 3.0f;
666 const bx::EaseFn easePos = bx::getEaseFunc(uniforms_.
m_easePos);
667 const bool hasColorBySpeed =
669 const bool hasSizeBySpeed =
674 const bool hasLifetimeByEmitterSpeed =
676 float emitterSpeed = 0.0f;
677 float lifetimeMultiplier = 1.0f;
678 if(hasLifetimeByEmitterSpeed)
686 const float speedFactor =
695 const math::mat3 rotationMatrix = math::mat3(effectiveTransform);
698 const math::vec3 systemScale = effectiveScale;
699 const math::vec3 emissionShapeScale = effectiveEmissionShapeScale;
700 const float lifeSpan = uniforms_.
m_lifetime * lifetimeMultiplier;
701 const float lifeSpanSquared = lifeSpan * lifeSpan;
702 math::vec3 gravityVector = math::vec3(0.0f, -9.81f * uniforms_.
m_gravityScale * lifeSpanSquared, 0.0f);
707 gravityVector.y *= systemScale.y;
708 forceOverLifetimeVector *= systemScale;
713 const math::vec3 currentPos = effectivePosition;
725 const math::vec3 up = math::vec3(0.0f, 1.0f, 0.0f);
728 for(uint32_t ii = 0; ii < actualEmitCount; ++ii)
732 const float baseEmissionPhase = float(ii) / float(actualEmitCount);
751 pos = glm::sphericalRand(1.0f);
758 math::vec3 spherePos = glm::sphericalRand(1.0f);
759 if(spherePos.y < 0.0f)
760 spherePos.y = -spherePos.y;
768 math::vec2 circlePos = glm::circularRand(1.0f);
769 pos = math::vec3(circlePos.x, 0.0f, circlePos.y);
776 const float face = bx::frnd(&
rng_) * 6.0f;
777 const int faceIndex =
static_cast<int>(face);
778 const float u = bx::frnd(&
rng_) * 2.0f - 1.0f;
779 const float v = bx::frnd(&
rng_) * 2.0f - 1.0f;
784 pos = math::vec3(1.0f,
u,
v);
787 pos = math::vec3(-1.0f,
u,
v);
790 pos = math::vec3(
u, 1.0f,
v);
793 pos = math::vec3(
u, -1.0f,
v);
796 pos = math::vec3(
u,
v, 1.0f);
799 pos = math::vec3(
u,
v, -1.0f);
802 pos = math::vec3(1.0f,
u,
v);
811 const float edge = bx::frnd(&
rng_) * 4.0f;
812 const int edgeIndex =
static_cast<int>(edge);
813 const float t = bx::frnd(&
rng_) * 2.0f - 1.0f;
818 pos = math::vec3(t, 0.0f, 1.0f);
821 pos = math::vec3(1.0f, 0.0f, t);
824 pos = math::vec3(t, 0.0f, -1.0f);
827 pos = math::vec3(-1.0f, 0.0f, t);
830 pos = math::vec3(t, 0.0f, 1.0f);
844 pos = math::ballRand(1.0f);
849 math::vec3 spherePos = math::ballRand(1.0f);
850 if(math::dot(spherePos, up) < 0.0f)
851 spherePos = -spherePos;
858 math::vec2 circlePos = math::diskRand(1.0f);
859 pos = math::vec3(circlePos.x, 0.0f, circlePos.y);
864 pos = math::vec3(math::linearRand(-1.0f, 1.0f),
865 math::linearRand(-1.0f, 1.0f),
866 math::linearRand(-1.0f, 1.0f));
870 pos = math::vec3(math::linearRand(-1.0f, 1.0f), 0.0f, math::linearRand(-1.0f, 1.0f));
889 const float len_sq = math::dot(pos, pos);
890 dir = (len_sq > k_emit_dir_zero_len_sq) ? math::normalize(pos) : up;
896 const float len_sq = math::dot(pos, pos);
897 dir = (len_sq > k_emit_dir_zero_len_sq) ? math::normalize(pos) : up;
903 math::vec3
start = pos;
907 const float endVelocity = math::mix(endVelocityRange.
min, endVelocityRange.
max, bx::frnd(&
rng_));
908 const math::vec3 scaledDir = systemScale * dir;
909 const math::vec3 tmp1 = dir * endVelocity;
910 math::vec3 end = tmp1 +
start;
914 std::swap(
start, end);
918 particle->
lifeSpan = math::max(lifeSpan, k_min_particle_lifespan);
919 particle->
life = 0.0f;
928 math::vec3 interpolatedEmitterPos = math::mix(prevPos, currentPos, emissionPhase);
935 particle->
end[0] = end;
940 particle->
start = rotationMatrix *
start + interpolatedEmitterPos;
941 particle->
end[0] = rotationMatrix * end + interpolatedEmitterPos;
947 const math::vec3 velocity = particle->
end[0] - particle->
start;
948 const math::vec3 dampedVelocity = velocity * velocityDampingFactor;
949 particle->
end[0] = particle->
start + dampedVelocity;
953 const math::vec3 totalForce = gravityVector + forceOverLifetimeVector;
954 particle->
end[1] = particle->
end[0] + totalForce;
968 updateParticleProperties(uniforms_, *particle, avgSystemScale, easePos, hasColorBySpeed, hasSizeBySpeed, effectiveTransform);
972 math::vec3 aabb_min, aabb_max;
1026 void init(uint16_t _maxEmitters, bx::AllocatorI* _allocator)
1030 if(
nullptr == _allocator)
1032 static bx::DefaultAllocator allocator;
1052 s_texColor = bgfx::createUniform(
"s_texColor", bgfx::UniformType::Sampler);
1053 u_viewCamera = bgfx::createUniform(
"u_viewCamera", bgfx::UniformType::Mat4);
1054 u_eyePos = bgfx::createUniform(
"u_eyePos", bgfx::UniformType::Vec4);
1082 bgfx::ProgramHandle _program,
1083 const float* _mtxView,
1084 const math::vec3& _eye,
1085 bgfx::TextureHandle _texture,
1086 uint64_t _blend_state,
1087 bool _sort_by_depth)
1089 if(_count == 0 || !bgfx::isValid(_texture))
1093 APP_SCOPE_PERF(
"Rendering/Particle Pass/Render Batched Emitters");
1100 float* pos =
reinterpret_cast<float*
>(row);
1104 pos[3] = emitter.
pivot_.x;
1105 float* rot =
reinterpret_cast<float*
>(row + 16);
1110 float* scale3d =
reinterpret_cast<float*
>(row + 32);
1114 scale3d[3] = emitter.
pivot_.y;
1115 float* uvData =
reinterpret_cast<float*
>(row + 48);
1120 float*
color =
reinterpret_cast<float*
>(row + 64);
1125 float* facing =
reinterpret_cast<float*
>(row + 80);
1136 for(uint32_t emitterIdx = 0; emitterIdx < _count; ++emitterIdx)
1139 if(
isValid(_handles[emitterIdx]))
1141 n =
m_emitter[_handles[emitterIdx].
idx].num_particles_;
1149 uint8_t _view, bgfx::ProgramHandle _program,
1150 const float* _mtxView,
const math::vec3& _eye,
1151 bgfx::TextureHandle _texture, uint64_t _blendState,
1152 bool _sort_by_depth)
1155 if(totalParticles == 0)
1159 const uint16_t instanceStride = 96;
1172 float viewCamera[16];
1173 viewCamera[0] = _mtxView[0];
1174 viewCamera[1] = _mtxView[4];
1175 viewCamera[2] = _mtxView[8];
1176 viewCamera[3] = 0.0f;
1177 viewCamera[4] = _mtxView[1];
1178 viewCamera[5] = _mtxView[5];
1179 viewCamera[6] = _mtxView[9];
1180 viewCamera[7] = 0.0f;
1181 viewCamera[8] = _mtxView[2];
1182 viewCamera[9] = _mtxView[6];
1183 viewCamera[10] = _mtxView[10];
1184 viewCamera[11] = 0.0f;
1185 viewCamera[12] = 0.0f;
1186 viewCamera[13] = 0.0f;
1187 viewCamera[14] = 0.0f;
1188 viewCamera[15] = 1.0f;
1189 float eyePosVec4[4] = { _eye.x, _eye.y, _eye.z, 0.0f };
1193 bgfx::setState(0 | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A |
1194 BGFX_STATE_DEPTH_TEST_LESS | BGFX_STATE_CULL_CW |
1198 bgfx::setUniform(
u_eyePos, eyePosVec4);
1201 const auto writeChunk = [&](uint8_t* data, uint32_t
start, uint32_t
count)
1213 const uint32_t availAll = bgfx::getAvailInstanceDataBuffer(totalParticles, instanceStride);
1214 if(availAll >= totalParticles)
1216 APP_SCOPE_PERF(
"Rendering/Particle Pass/Allocate Instance Data Buffer (single batch)");
1217 bgfx::InstanceDataBuffer idb{};
1218 bgfx::allocInstanceDataBuffer(&idb, totalParticles, instanceStride);
1219 writeChunk(idb.data, 0, totalParticles);
1220 bgfx::setInstanceDataBuffer(&idb);
1223 bgfx::submit(_view, _program);
1225 return totalParticles;
1228 uint32_t renderedTotal = 0;
1229 uint32_t offset = 0;
1230 while(offset < totalParticles)
1232 APP_SCOPE_PERF(
"Rendering/Particle Pass/Allocate Instance Data Buffer");
1234 const uint32_t remaining = totalParticles - offset;
1235 uint32_t chunk = bgfx::getAvailInstanceDataBuffer(remaining, instanceStride);
1238 BX_WARN(
false,
"No instance buffer space available for batch rendering.");
1241 chunk = math::min(chunk, remaining);
1242 bgfx::InstanceDataBuffer idb{};
1243 bgfx::allocInstanceDataBuffer(&idb, chunk, instanceStride);
1244 writeChunk(idb.data, offset, chunk);
1245 bgfx::setInstanceDataBuffer(&idb);
1248 bgfx::submit(_view, _program);
1251 renderedTotal += chunk;
1254 return renderedTotal;
1259 const math::vec3& _eye,
1260 uint32_t totalParticles)
1262 APP_SCOPE_PERF(
"Rendering/Particle Pass/Build Sorted Batched Particles");
1265 if(_count == 0 || totalParticles == 0)
1275 const auto fillEmitterKeys = [&](uint32_t emitterIdx)
1279 const uint32_t n = end -
start;
1285 for(uint32_t p = 0; p < n; ++p)
1288 const math::vec3 tmp0 = _eye - particle.
position;
1289 const float distSquared = math::dot(tmp0, tmp0);
1295 constexpr uint32_t kParallelSortKeysEmitterThreshold = 16;
1296 constexpr uint32_t kMinEmittersPerParallelJob = 16;
1297 if(_count < kParallelSortKeysEmitterThreshold)
1299 for(uint32_t emitterIdx = 0; emitterIdx < _count; ++emitterIdx)
1301 fillEmitterKeys(emitterIdx);
1306 const uint32_t numJobs = (_count + kMinEmittersPerParallelJob - 1) / kMinEmittersPerParallelJob;
1309 for(uint32_t emitterIdx = 0; emitterIdx < _count; ++emitterIdx)
1311 fillEmitterKeys(emitterIdx);
1316 std::for_each(poolstl::par,
1317 poolstl::iota_iter<uint32_t>(0),
1318 poolstl::iota_iter<uint32_t>(numJobs),
1321 const uint32_t emitBegin = job * kMinEmittersPerParallelJob;
1322 const uint32_t emitEnd = math::min(emitBegin + kMinEmittersPerParallelJob, _count);
1323 for(uint32_t emitterIdx = emitBegin; emitterIdx < emitEnd; ++emitterIdx)
1325 fillEmitterKeys(emitterIdx);
1333 constexpr uint32_t kParallelSortParticleThreshold = 2048;
1336 return a.dist >
b.dist;
1339 if(totalParticles < kParallelSortParticleThreshold)
1346 APP_SCOPE_PERF(
"Rendering/Particle Pass/Particle Sort Parallel");
1347 std::sort(poolstl::par,
1355 uint32_t globalStart,
1358 uint32_t _emitterCount,
1359 uint16_t instanceStride)
1361 APP_SCOPE_PERF(
"Rendering/Particle Pass/Write Direct Instance Chunk");
1365 constexpr uint32_t kParallelEmitterThreshold = 16;
1366 constexpr uint32_t kMinEmittersPerParallelJob = 16;
1368 const auto writeEmitterRange = [&](uint32_t emitBegin, uint32_t emitEnd)
1370 for(uint32_t emitterIdx = emitBegin; emitterIdx < emitEnd; ++emitterIdx)
1374 const uint32_t rangeBegin = math::max(emitBeginGlobal, globalStart);
1375 const uint32_t rangeEnd = math::min(emitEndGlobal, globalStart +
count);
1376 if(rangeBegin >= rangeEnd)
1382 for(uint32_t g = rangeBegin; g < rangeEnd; ++g)
1384 const uint32_t particleIdx = g - emitBeginGlobal;
1385 const uint32_t outIdx = g - globalStart;
1393 if(_emitterCount < kParallelEmitterThreshold)
1395 writeEmitterRange(0, _emitterCount);
1399 const uint32_t numJobs = (_emitterCount + kMinEmittersPerParallelJob - 1) / kMinEmittersPerParallelJob;
1402 writeEmitterRange(0, _emitterCount);
1406 std::for_each(poolstl::par,
1407 poolstl::iota_iter<uint32_t>(0),
1408 poolstl::iota_iter<uint32_t>(numJobs),
1411 const uint32_t emitBegin = job * kMinEmittersPerParallelJob;
1412 const uint32_t emitEnd = math::min(emitBegin + kMinEmittersPerParallelJob, _emitterCount);
1413 writeEmitterRange(emitBegin, emitEnd);
1420 APP_SCOPE_PERF(
"Rendering/Particle Pass/Write Batched Instance Chunk Parallel");
1422 const auto writeRow = [&](uint32_t i)
1431 constexpr uint32_t kParallelWriteThreshold = 128;
1432 constexpr uint32_t kMinRowsPerParallelJob = 128;
1434 if(sortedCount < kParallelWriteThreshold)
1436 for(uint32_t i = 0; i < sortedCount; ++i)
1442 const uint32_t numJobs = (sortedCount + kMinRowsPerParallelJob - 1) / kMinRowsPerParallelJob;
1445 for(uint32_t i = 0; i < sortedCount; ++i)
1452 std::for_each(poolstl::par,
1453 poolstl::iota_iter<uint32_t>(0),
1454 poolstl::iota_iter<uint32_t>(numJobs),
1457 const uint32_t rowBegin = job * kMinRowsPerParallelJob;
1458 const uint32_t rowEnd = math::min(rowBegin + kMinRowsPerParallelJob, sortedCount);
1459 for(uint32_t i = rowBegin; i < rowEnd; ++i)
1470 if(UINT16_MAX !=
handle.idx)
1480 BX_ASSERT(
isValid(_handle),
"destroyEmitter handle %d is not valid.", _handle.
idx);
1484 if(
nullptr == _uniforms)
1491 emitter.
update(_uniforms, _dt);
1497 BX_ASSERT(
isValid(_handle),
"getAabb handle %d is not valid.", _handle.
idx);
1502 BX_ASSERT(
isValid(_handle),
"getNumParticles handle %d is not valid.", _handle.
idx);
1508 BX_ASSERT(
isValid(_handle),
"hasUpdated handle %d is not valid.", _handle.
idx);
1514 BX_ASSERT(
isValid(_handle),
"destroyEmitter handle %d is not valid.", _handle.
idx);
1558void psInit(uint16_t _maxEmitters, bx::AllocatorI* _allocator)
1582 BX_ASSERT(
isValid(_handle),
"psResetEmitter handle %d is not valid.", _handle.
idx);
1610 bgfx::ProgramHandle _program,
1611 const float* _mtxView,
1612 const math::vec3& _eye,
1613 bgfx::TextureHandle _texture,
1614 uint64_t _blend_state,
1615 bool _sort_by_depth)
auto add_point(const T &element, float progress) -> size_t
void generate_lut(size_t lut_size=256)
auto sample(float progress) const -> T
auto look_rotation(const glm::vec3 &forward, const glm::vec3 &upwards) -> glm::quat
transform_t< float > transform
static ParticleSystem s_ctx
std::vector< math::color > color
std::vector< float > scale
std::vector< math::quat > rotation
std::vector< math::vec3 > start
void psResetEmitter(EmitterHandle _handle)
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, uint64_t _blend_state, bool _sort_by_depth)
bool psHasUpdated(EmitterHandle _handle)
static const uint16_t s_quadIndices[6]
static ParticleVertex s_quadVertices[4]
EmitterHandle psCreateEmitter(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles)
uint32_t psGetNumParticles(EmitterHandle _handle)
void psGetAabb(EmitterHandle _handle, math::bbox &_outAabb)
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_literal)
Create a scoped performance timer that records to the timeline profiler. Only accepts string literals...
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.
math::vec3 particle_scale_3d_
float calculateParticleSpeed(const Particle &particle, float ttPos) const
RenderMode::Enum render_mode_
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)
BlendMode::Enum blend_mode_
void updateParticleProperties(EmitterUniforms &uniforms_, Particle &particle, float avgSystemScale, bx::EaseFn easePos, bool hasColorBySpeed, bool hasSizeBySpeed, const math::mat4 &effectiveTransform)
uint32_t total_particles_spawned_
float start_delay_elapsed_
void update(EmitterUniforms *_uniforms, float _dt)
void calculateRotatedAABBBounds(const math::quat &rotation, const math::vec3 &half_extents, const math::vec2 &pivot, math::vec3 &out_min, math::vec3 &out_max) const
EmitterShape::Enum shape_
std::vector< math::vec3 > temporal_position_buffer_
float calculate_smoothed_emitter_speed(float max_speed) const
static constexpr size_t TEMPORAL_BUFFER_SIZE
TextureMode::Enum texture_mode_
void update_temporal_buffer(const math::vec3 &position, float dt)
math::vec3 billboard_right_
EmitterDirection::Enum direction_
std::vector< float > temporal_time_buffer_
float texsheet_random_offset
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)
std::vector< uint32_t > emitter_batch_prefix_scratch_
bgfx::UniformHandle u_viewCamera
bgfx::VertexBufferHandle m_quadVBH
void updateEmitter(EmitterHandle _handle, float _dt, EmitterUniforms *_uniforms)
bgfx::UniformHandle u_eyePos
std::vector< BatchedParticle > batched_particles_scratch_
uint32_t buildEmitterBatchPrefixes(const EmitterHandle *_handles, uint32_t _count)
static void writeParticleInstanceRow(uint8_t *row, const Emitter &emitter, const Particle &particle)
bx::AllocatorI * m_allocator
void writeDirectInstanceChunk(uint8_t *data, uint32_t globalStart, uint32_t count, const EmitterHandle *_handles, uint32_t _emitterCount, uint16_t instanceStride)
uint32_t renderEmitterBatchByMode(const EmitterHandle *_handles, uint32_t _count, uint8_t _view, bgfx::ProgramHandle _program, const float *_mtxView, const math::vec3 &_eye, bgfx::TextureHandle _texture, uint64_t _blendState, bool _sort_by_depth)
void init(uint16_t _maxEmitters, bx::AllocatorI *_allocator)
void destroyEmitter(EmitterHandle _handle)
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, uint64_t _blend_state, bool _sort_by_depth)
bx::HandleAlloc * m_emitterAlloc
void writeBatchedInstanceChunk(uint8_t *data, uint32_t sortedStart, uint32_t sortedCount, const EmitterHandle *_handles, uint16_t instanceStride)
bgfx::IndexBufferHandle m_quadIBH
void buildSortedBatchedParticles(const EmitterHandle *_handles, uint32_t _count, const math::vec3 &_eye, uint32_t totalParticles)
std::vector< Emitter > m_emitter
bgfx::UniformHandle s_texColor
static const uint16_t s_quadIndices[6]
static DebugShapeVertex s_quadVertices[4]
bool isValid(SpriteHandle _handle)