46 _buffersDirty.fill(
false);
49ParticleEmitter::~ParticleEmitter()
51 assert(_particles ==
nullptr);
54bool ParticleEmitter::load( PlatformContext& context )
56 for (
U8 i = 0u; i < s_MaxPlayerBuffers; ++i )
60 _particleGPUBuffers[i][j] = context.gfx().newGVD( g_particleBufferSizeFactor,
Util::StringFormat(
"{}_buffer_{}_{}", resourceName(), i, j ).c_str() );
69 return *_particleGPUBuffers[idx % s_MaxPlayerBuffers][
to_U32(stage)];
72bool ParticleEmitter::initData(
const std::shared_ptr<ParticleData>& particleData)
75 DIVIDE_ASSERT(particleData !=
nullptr,
"ParticleEmitter::updateData error: Invalid particle data!");
76 _particles = particleData;
77 const vector<vec3<F32>>& geometry = particleData->particleGeometryVertices();
78 const vector<U32>& indices = particleData->particleGeometryIndices();
80 for (
U8 i = 0u; i < s_MaxPlayerBuffers; ++i)
84 GenericVertexData& buffer = getDataBuffer(
static_cast<RenderStage>(j), i);
86 GenericVertexData::SetBufferParams params = {};
88 params._bufferParams._elementCount =
to_U32(geometry.size());
89 params._bufferParams._elementSize =
sizeof(vec3<F32>);
92 params._initialData = { (
bufferPtr)geometry.data(), geometry.size() * params._bufferParams._elementSize};
93 params._useRingBuffer =
false;
96 const BufferLock lock = buffer.setBuffer(params);
100 if (!indices.empty())
102 GenericVertexData::IndexBuffer idxBuff{};
103 idxBuff.smallIndices =
false;
104 idxBuff.count =
to_U32(indices.size());
105 idxBuff.data = (
bufferPtr)indices.data();
106 idxBuff.dynamic =
false;
108 const BufferLock lock = buffer.setIndexBuffer(idxBuff);
114 const U32 particleCount = _particles->totalCount();
116 for (
U8 i = 0; i < s_MaxPlayerBuffers; ++i )
120 GenericVertexData& buffer = getDataBuffer(
static_cast<RenderStage>(j), i );
122 GenericVertexData::SetBufferParams params = {};
124 params._useRingBuffer =
true;
126 params._bufferParams._elementCount = particleCount;
127 params._bufferParams._elementSize =
sizeof( vec4<F32> );
130 BufferLock lock = buffer.setBuffer( params );
134 params._bufferParams._elementCount = particleCount;
135 params._bufferParams._elementSize =
sizeof(
UColour4 );
137 lock = buffer.setBuffer( params );
142 for (
U32 i = 0; i < particleCount; ++i )
145 _particles->_misc[i].w = -1.0f;
149 AttributeMap vertexFormat{};
153 desc._componentsPerElement = 3u;
156 auto& vertBinding = vertexFormat._vertexBindings.emplace_back();
157 vertBinding._bufferBindIndex = desc._vertexBindingIndex;
158 vertBinding._strideInBytes = 3 *
sizeof(
F32);
163 desc._componentsPerElement = 4u;
165 desc._normalized =
false;
166 desc._strideInBytes = 0u;
168 auto& vertBinding = vertexFormat._vertexBindings.emplace_back();
169 vertBinding._bufferBindIndex = desc._vertexBindingIndex;
170 vertBinding._strideInBytes = 3 *
sizeof(
F32 );
171 vertBinding._perVertexInputRate =
false;
176 desc._componentsPerElement = 4u;
178 desc._normalized =
true;
179 desc._strideInBytes = 0u;
181 auto& vertBinding = vertexFormat._vertexBindings.emplace_back();
182 vertBinding._bufferBindIndex = desc._vertexBindingIndex;
183 vertBinding._strideInBytes = 3 *
sizeof(
F32 );
186 const bool useTexture = !_particles->_textureFileName.empty();
187 Handle<Material> matHandle =
CreateResource(ResourceDescriptor<Material>(useTexture ?
"Material_particles_Texture" :
"Material_particles"));
188 Material* mat =
Get(matHandle);
190 mat->setPipelineLayout(topology, vertexFormat);
191 mat->computeRenderStateCBK([]([[maybe_unused]] Material* material, [[maybe_unused]]
const RenderStagePass stagePass, RenderStateBlock& blockInOut)
196 mat->computeShaderCBK([useTexture]([[maybe_unused]] Material* material,
const RenderStagePass stagePass)
198 ShaderModuleDescriptor vertModule{
ShaderType::VERTEX,
"particles.glsl", useTexture ?
"WithTexture" :
"NoTexture" };
199 ShaderModuleDescriptor fragModule{
ShaderType::FRAGMENT,
"particles.glsl", useTexture ?
"WithTexture" :
"NoTexture" };
203 fragModule._defines.emplace_back(
"HAS_TEXTURE");
207 particleShaderDescriptor.
_name = useTexture ?
"particles_WithTexture" :
"particles_NoTexture";
208 particleShaderDescriptor._modules.push_back(vertModule);
212 if (IsDepthPass(stagePass))
214 fragModule._variant =
"PrePass";
215 fragModule._defines.emplace_back(
"PRE_PASS");
216 particleShaderDescriptor._name = useTexture ?
"particles_prePass_WithTexture" :
"particles_prePass_NoTexture";
218 particleShaderDescriptor._modules.push_back(fragModule);
224 fragModule._variant =
"Shadow.VSM";
225 particleShaderDescriptor._modules.push_back(fragModule);
226 particleShaderDescriptor._name =
"particles_VSM";
230 particleShaderDescriptor._name =
"particles_DepthPass";
234 return particleShaderDescriptor;
239 ResourceDescriptor<Texture> texture( _particles->_textureFileName );
247 setMaterialTpl(matHandle);
252bool ParticleEmitter::unload()
257 return SceneNode::unload();
260void ParticleEmitter::buildDrawCommands(SceneGraphNode* sgn, GenericDrawCommandContainer& cmdsOut)
262 const U32 idxCount =
to_U32( _particles->particleGeometryIndices().size() );
265 GenericDrawCommand& cmd = cmdsOut.emplace_back();
268 cmd._cmd.indexCount = idxCount;
271 SceneNode::buildDrawCommands(sgn, cmdsOut);
274void ParticleEmitter::prepareRender(SceneGraphNode* sgn,
275 RenderingComponent& rComp,
277 GFX::MemoryBarrierCommand& postDrawMemCmd,
278 const RenderStagePass renderStagePass,
279 const CameraSnapshot& cameraSnapshot,
280 const bool refreshData)
282 if ( _enabled && getAliveParticleCount() > 0)
284 Wait(*_bufferUpdate, sgn->context().taskPool(TaskPoolType::HIGH_PRIORITY));
285 if (refreshData && _buffersDirty[
to_U32(renderStagePass._stage)])
287 GenericVertexData& buffer = getDataBuffer(renderStagePass._stage, 0);
288 postDrawMemCmd._bufferLocks.emplace_back(buffer.updateBuffer(g_particlePositionBuffer, 0u,
to_U32(_particles->_renderingPositions.size()), _particles->_renderingPositions.data()));
289 postDrawMemCmd._bufferLocks.emplace_back(buffer.updateBuffer(g_particleColourBuffer, 0u,
to_U32(_particles->_renderingColours.size()), _particles->_renderingColours.data()));
292 _buffersDirty[
to_U32(renderStagePass._stage)] =
false;
295 RenderingComponent::DrawCommands& cmds = rComp.drawCommands();
297 LockGuard<SharedMutex> w_lock(cmds._dataLock);
298 GenericDrawCommand& cmd = cmds._data.front();
299 cmd._cmd.instanceCount =
to_U32(_particles->_renderingPositions.size());
300 cmd._sourceBuffer = getDataBuffer(renderStagePass._stage, 0).handle();
303 if (renderStagePass._passType == RenderPassType::PRE_PASS)
305 const vec3<F32>& eyePos = cameraSnapshot._eye;
306 const U32 aliveCount = getAliveParticleCount();
308 vector<vec4<F32>>& misc = _particles->_misc;
309 vector<vec4<F32>>& pos = _particles->_position;
312 ParallelForDescriptor descriptor = {};
313 descriptor._iterCount = aliveCount;
314 descriptor._partitionSize = 1000u;
315 Parallel_For( sgn->context().taskPool( TaskPoolType::HIGH_PRIORITY ), descriptor, [&eyePos, &misc, &pos](
const Task*,
const U32 start,
const U32 end)
317 for (U32 i = start; i < end; ++i)
319 misc[i].w = pos[i].xyz.distanceSquared(eyePos);
324 [
this, &renderStagePass](
const Task&)
328 _buffersDirty[
to_U32(renderStagePass._stage)] =
true;
331 Start(*_bufferUpdate, sgn->context().taskPool(TaskPoolType::HIGH_PRIORITY));
335 SceneNode::prepareRender(sgn, rComp, pkg, postDrawMemCmd, renderStagePass, cameraSnapshot, refreshData);
340void ParticleEmitter::sceneUpdate(
const U64 deltaTimeUS,
342 SceneState& sceneState)
344 constexpr U32 s_particlesPerThread = 1024;
348 U32 aliveCount = getAliveParticleCount();
349 renderState().drawState(aliveCount > 0);
351 const TransformComponent* transform = sgn->get<TransformComponent>();
353 const vec3<F32>& pos = transform->getWorldPosition();
354 const Quaternion<F32>& rot = transform->getWorldOrientation();
356 F32 averageEmitRate = 0;
357 for (
const std::shared_ptr<ParticleSource>& source : _sources)
359 source->updateTransform(pos, rot);
360 source->emit(g_updateInterval, _particles);
361 averageEmitRate += source->emitRate();
363 averageEmitRate /= _sources.size();
365 aliveCount = getAliveParticleCount();
368 ParallelForDescriptor descriptor = {};
369 descriptor._iterCount = aliveCount;
370 descriptor._partitionSize = s_particlesPerThread;
371 Parallel_For( sgn->context().taskPool( TaskPoolType::HIGH_PRIORITY ), descriptor, [
this](
const Task*,
const U32 start,
const U32 end)
373 for (U32 i = start; i < end; ++i)
375 _particles->_position[i].w = _particles->_misc[i].z;
376 _particles->_acceleration[i].set(0.0f);
380 ParticleData& data = *_particles;
381 for (
const std::shared_ptr<ParticleUpdater>& up : _updaters)
383 up->update(g_updateInterval, data);
386 Wait(*_bbUpdate, sgn->context().taskPool(TaskPoolType::HIGH_PRIORITY));
388 _bbUpdate =
CreateTask([
this, aliveCount, averageEmitRate](
const Task&)
391 for (U32 i = 0; i < aliveCount; i +=
to_U32(averageEmitRate) / 4)
393 aabb.add(_particles->_position[i]);
397 Start(*_bbUpdate, sgn->context().taskPool(TaskPoolType::HIGH_PRIORITY));
400 SceneNode::sceneUpdate(deltaTimeUS, sgn, sceneState);
403U32 ParticleEmitter::getAliveParticleCount() const noexcept
405 return _particles ? _particles->aliveCount() : 0u;
#define WAIT_FOR_CONDITION(...)
bool load(PlatformContext &context) override
Loading and unloading interface.
constexpr T MillisecondsToMicroseconds(U a) noexcept
Str StringFormat(const char *fmt, Args &&...args)
constexpr U32 g_particleColourBuffer
constexpr U32 g_particleGeometryBuffer
constexpr U32 g_particlePositionBuffer
constexpr U32 g_particleBufferSizeFactor
constexpr U64 g_updateInterval
Handle console commands that start with a forward slash.
constexpr SceneNodeType GetSceneNodeType()
static constexpr bool IsDepthPass(const RenderStagePass stagePass) noexcept
constexpr U32 to_U32(const T value)
void Wait(const Task &task, TaskPool &pool)
Task * CreateTask(Predicate &&threadedFunction, bool allowedInIdle=true)
void toggleOption(GenericDrawCommand &cmd, CmdRenderOptions option) noexcept
FORCE_INLINE Handle< T > CreateResource(const ResourceDescriptor< T > &descriptor, bool &wasInCache, std::atomic_uint &taskCounter)
void Start(Task &task, TaskPool &pool, TaskPriority priority=TaskPriority::DONT_CARE, const DELEGATE< void > &onCompletionFunction={})
PropertyDescriptor< Texture > TextureDescriptor
void Parallel_For(TaskPool &pool, const ParallelForDescriptor &descriptor, const DELEGATE< void, const Task *, U32, U32 > &cbk)
PropertyDescriptor< ShaderProgram > ShaderProgramDescriptor
FORCE_INLINE T * Get(const Handle< T > handle)
constexpr auto to_base(const Type value) -> Type