450 lines
13 KiB
C++
450 lines
13 KiB
C++
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "Engine/Core/Types/String.h"
|
|
#include "Engine/Core/Types/StringView.h"
|
|
#include "Engine/Core/Collections/Array.h"
|
|
#include "Types.h"
|
|
|
|
// The maximum amount of particle attributes to use
|
|
#define PARTICLE_ATTRIBUTES_MAX_COUNT 32
|
|
|
|
// The maximum amount of particle ribbon modules used per context
|
|
#define PARTICLE_EMITTER_MAX_RIBBONS 4
|
|
|
|
class ParticleEmitter;
|
|
class ParticleManager;
|
|
class GPUBuffer;
|
|
class DynamicIndexBuffer;
|
|
|
|
/// <summary>
|
|
/// The particle attribute that defines a single particle layout component.
|
|
/// </summary>
|
|
struct ParticleAttribute
|
|
{
|
|
enum class ValueTypes
|
|
{
|
|
Float,
|
|
Vector2,
|
|
Vector3,
|
|
Vector4,
|
|
Int,
|
|
Uint,
|
|
};
|
|
|
|
/// <summary>
|
|
/// The attribute value container type.
|
|
/// </summary>
|
|
ValueTypes ValueType;
|
|
|
|
/// <summary>
|
|
/// The attribute offset from particle data start (in bytes).
|
|
/// </summary>
|
|
int32 Offset;
|
|
|
|
/// <summary>
|
|
/// The attribute name.
|
|
/// </summary>
|
|
String Name;
|
|
|
|
/// <summary>
|
|
/// Gets the size of the attribute (in bytes).
|
|
/// </summary>
|
|
/// <returns>The size (in bytes).</returns>
|
|
int32 GetSize() const
|
|
{
|
|
switch (ValueType)
|
|
{
|
|
case ValueTypes::Vector2:
|
|
return 8;
|
|
case ValueTypes::Vector3:
|
|
return 12;
|
|
case ValueTypes::Vector4:
|
|
return 16;
|
|
case ValueTypes::Float:
|
|
case ValueTypes::Int:
|
|
case ValueTypes::Uint:
|
|
return 4;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
/// <summary>
|
|
/// The particle attributes layout descriptor.
|
|
/// </summary>
|
|
class ParticleLayout
|
|
{
|
|
public:
|
|
|
|
/// <summary>
|
|
/// The total particle data stride size (in bytes). Defines the required memory amount for a single particle.
|
|
/// </summary>
|
|
int32 Size;
|
|
|
|
/// <summary>
|
|
/// The attributes set.
|
|
/// </summary>
|
|
Array<ParticleAttribute, FixedAllocation<PARTICLE_ATTRIBUTES_MAX_COUNT>> Attributes;
|
|
|
|
public:
|
|
|
|
/// <summary>
|
|
/// Clears the layout data.
|
|
/// </summary>
|
|
void Clear()
|
|
{
|
|
Size = 0;
|
|
Attributes.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the attributes layout (calculates offset) and updates the total size of the layout.
|
|
/// </summary>
|
|
void UpdateLayout()
|
|
{
|
|
Size = 0;
|
|
for (int32 i = 0; i < Attributes.Count(); i++)
|
|
{
|
|
Attributes[i].Offset = Size;
|
|
Size += Attributes[i].GetSize();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the attribute by the name.
|
|
/// </summary>
|
|
/// <param name="name">The name.</param>
|
|
/// <returns>The attribute index or -1 if cannot find it.</returns>
|
|
int32 FindAttribute(const StringView& name) const
|
|
{
|
|
for (int32 i = 0; i < Attributes.Count(); i++)
|
|
{
|
|
if (name == Attributes[i].Name)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the attribute by the name and type.
|
|
/// </summary>
|
|
/// <param name="name">The name.</param>
|
|
/// <param name="valueType">The type.</param>
|
|
/// <returns>The attribute index or -1 if cannot find it.</returns>
|
|
int32 FindAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType) const
|
|
{
|
|
for (int32 i = 0; i < Attributes.Count(); i++)
|
|
{
|
|
if (Attributes[i].ValueType == valueType && name == Attributes[i].Name)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the attribute offset by the name.
|
|
/// </summary>
|
|
/// <param name="name">The name.</param>
|
|
/// <param name="fallbackValue">The fallback value to return if attribute is missing.</param>
|
|
/// <returns>The attribute offset or fallback value if cannot find it.</returns>
|
|
int32 FindAttributeOffset(const StringView& name, int32 fallbackValue = 0) const
|
|
{
|
|
for (int32 i = 0; i < Attributes.Count(); i++)
|
|
{
|
|
if (name == Attributes[i].Name)
|
|
{
|
|
return Attributes[i].Offset;
|
|
}
|
|
}
|
|
return fallbackValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the attribute offset by the name.
|
|
/// </summary>
|
|
/// <param name="name">The name.</param>
|
|
/// <param name="valueType">The type.</param>
|
|
/// <param name="fallbackValue">The fallback value to return if attribute is missing.</param>
|
|
/// <returns>The attribute offset or fallback value if cannot find it.</returns>
|
|
int32 FindAttributeOffset(const StringView& name, ParticleAttribute::ValueTypes valueType, int32 fallbackValue = 0) const
|
|
{
|
|
for (int32 i = 0; i < Attributes.Count(); i++)
|
|
{
|
|
if (Attributes[i].ValueType == valueType && name == Attributes[i].Name)
|
|
{
|
|
return Attributes[i].Offset;
|
|
}
|
|
}
|
|
return fallbackValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the attribute offset by the attribute index.
|
|
/// </summary>
|
|
/// <param name="index">The attribute index.</param>
|
|
/// <param name="fallbackValue">The fallback value to return if attribute is missing.</param>
|
|
/// <returns>The attribute offset or fallback value if cannot find it.</returns>
|
|
FORCE_INLINE int32 GetAttributeOffset(int32 index, int32 fallbackValue = -1) const
|
|
{
|
|
return index != -1 ? Attributes[index].Offset : fallbackValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the attribute with the given name and value type.
|
|
/// </summary>
|
|
/// <param name="name">The name.</param>
|
|
/// <param name="valueType">The value type.</param>
|
|
/// <returns>The attribute index or -1 if cannot find it.</returns>
|
|
int32 AddAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType)
|
|
{
|
|
auto& a = Attributes.AddOne();
|
|
a.Name = String(*name, name.Length());
|
|
a.ValueType = valueType;
|
|
return Attributes.Count() - 1;
|
|
}
|
|
};
|
|
|
|
/// <summary>
|
|
/// The particles simulation data container. Can allocated and used many times. It's designed to hold CPU or GPU particles data for the simulation and rendering.
|
|
/// </summary>
|
|
class FLAXENGINE_API ParticleBuffer
|
|
{
|
|
public:
|
|
|
|
/// <summary>
|
|
/// The emitter graph version (cached on Init). Used to discard pooled buffers that has been created for older emitter graph.
|
|
/// </summary>
|
|
uint32 Version;
|
|
|
|
/// <summary>
|
|
/// The buffer capacity (maximum amount of particles it can hold).
|
|
/// </summary>
|
|
int32 Capacity;
|
|
|
|
/// <summary>
|
|
/// The stride (size of the particle structure data).
|
|
/// </summary>
|
|
int32 Stride;
|
|
|
|
/// <summary>
|
|
/// The simulation mode.
|
|
/// </summary>
|
|
ParticlesSimulationMode Mode;
|
|
|
|
/// <summary>
|
|
/// The emitter.
|
|
/// </summary>
|
|
ParticleEmitter* Emitter = nullptr;
|
|
|
|
/// <summary>
|
|
/// The layout descriptor.
|
|
/// </summary>
|
|
ParticleLayout* Layout = nullptr;
|
|
|
|
struct
|
|
{
|
|
/// <summary>
|
|
/// The active particles count.
|
|
/// </summary>
|
|
int32 Count;
|
|
|
|
/// <summary>
|
|
/// The particles data buffer (CPU side).
|
|
/// </summary>
|
|
Array<byte> Buffer;
|
|
|
|
/// <summary>
|
|
/// The sorted ribbon particles indices (CPU side). Cached after system update and reused during rendering (batched for all ribbon modules).
|
|
/// </summary>
|
|
Array<int32> RibbonOrder;
|
|
} CPU;
|
|
|
|
struct
|
|
{
|
|
/// <summary>
|
|
/// The particles data buffer (GPU side).
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Contains particles data for GPU simulation and rendering. If simulation mode is GPU then this buffer is dynamic and updated before rendering, otherwise it's in default allocation mode.
|
|
/// </remarks>
|
|
GPUBuffer* Buffer = nullptr;
|
|
|
|
/// <summary>
|
|
/// The particles secondary data buffer (GPU side). Used as a write destination for GPU particles and swapped with Buffer after simulation.
|
|
/// </summary>
|
|
GPUBuffer* BufferSecondary = nullptr;
|
|
|
|
/// <summary>
|
|
/// The indirect draw command arguments buffer used by the GPU particles to invoke drawing on a GPU based on the particles amount (instances count).
|
|
/// </summary>
|
|
GPUBuffer* IndirectDrawArgsBuffer = nullptr;
|
|
|
|
/// <summary>
|
|
/// The GPU particles sorting buffer. Contains structure of particle index and the sorting key for every particle. Used to sort particles.
|
|
/// </summary>
|
|
GPUBuffer* SortingKeysBuffer = nullptr;
|
|
|
|
/// <summary>
|
|
/// The particles indices buffer (GPU side).
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Contains sorted particles indices for GPU rendering. Each sorting module from the emitter uses a dedicated range of this buffer.
|
|
/// </remarks>
|
|
GPUBuffer* SortedIndices = nullptr;
|
|
|
|
/// <summary>
|
|
/// The ribbon particles rendering index buffer (dynamic GPU access).
|
|
/// </summary>
|
|
DynamicIndexBuffer* RibbonIndexBufferDynamic = nullptr;
|
|
|
|
/// <summary>
|
|
/// The ribbon particles rendering segment distances buffer. Stored one per ribbon module.
|
|
/// </summary>
|
|
GPUBuffer* RibbonSegmentDistances[PARTICLE_EMITTER_MAX_RIBBONS] = {};
|
|
|
|
/// <summary>
|
|
/// The flag used to indicate that GPU buffers data should be cleared before next simulation.
|
|
/// </summary>
|
|
bool PendingClear;
|
|
|
|
/// <summary>
|
|
/// The flag used to indicate that GPU buffers data contains a valid particles count.
|
|
/// </summary>
|
|
bool HasValidCount;
|
|
|
|
/// <summary>
|
|
/// The particle counter (uint type) offset. Located in Buffer and BufferSecondary if GPU particles simulation is used to store the particles count in the buffer. Placed after the particle attributes.
|
|
/// </summary>
|
|
uint32 ParticleCounterOffset;
|
|
|
|
/// <summary>
|
|
/// The maximum amount of particles that 'might' be in the buffer. During every simulation update we spawn a certain amount of particles and update existing ones. We can estimate limit for the current particles count to dispatch less threads for particles update.
|
|
/// </summary>
|
|
int32 ParticlesCountMax;
|
|
} GPU;
|
|
|
|
public:
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="ParticleBuffer"/> class.
|
|
/// </summary>
|
|
ParticleBuffer();
|
|
|
|
/// <summary>
|
|
/// Finalizes an instance of the <see cref="ParticleBuffer"/> class.
|
|
/// </summary>
|
|
~ParticleBuffer();
|
|
|
|
/// <summary>
|
|
/// Initializes the particle buffer for the specified emitter.
|
|
/// </summary>
|
|
/// <param name="emitter">The emitter.</param>
|
|
/// <returns>True if failed, otherwise false.</returns>
|
|
bool Init(ParticleEmitter* emitter);
|
|
|
|
/// <summary>
|
|
/// Allocates the particles sorting indices buffer.
|
|
/// </summary>
|
|
/// <returns>True if failed, otherwise false.</returns>
|
|
bool AllocateSortBuffer();
|
|
|
|
/// <summary>
|
|
/// Clears the particles from the buffer (prepares for the simulation).
|
|
/// </summary>
|
|
void Clear();
|
|
|
|
/// <summary>
|
|
/// Gets the pointer to the particle data.
|
|
/// </summary>
|
|
/// <param name="particleIndex">Index of the particle.</param>
|
|
/// <returns>The particle data start address.</returns>
|
|
FORCE_INLINE byte* GetParticleCPU(int32 particleIndex)
|
|
{
|
|
return CPU.Buffer.Get() + particleIndex * Stride;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the pointer to the particle data.
|
|
/// </summary>
|
|
/// <param name="particleIndex">Index of the particle.</param>
|
|
/// <returns>The particle data start address.</returns>
|
|
FORCE_INLINE const byte* GetParticleCPU(int32 particleIndex) const
|
|
{
|
|
return CPU.Buffer.Get() + particleIndex * Stride;
|
|
}
|
|
};
|
|
|
|
struct ParticleBufferCPUDataAccessorBase
|
|
{
|
|
protected:
|
|
|
|
ParticleBuffer* _buffer;
|
|
int32 _offset;
|
|
|
|
public:
|
|
|
|
ParticleBufferCPUDataAccessorBase()
|
|
: _buffer(nullptr)
|
|
, _offset(-1)
|
|
{
|
|
}
|
|
|
|
ParticleBufferCPUDataAccessorBase(ParticleBuffer* buffer, int32 offset)
|
|
: _buffer(buffer)
|
|
, _offset(offset)
|
|
{
|
|
}
|
|
|
|
public:
|
|
|
|
FORCE_INLINE bool IsValid() const
|
|
{
|
|
return _buffer != nullptr && _offset != -1;
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct ParticleBufferCPUDataAccessor : ParticleBufferCPUDataAccessorBase
|
|
{
|
|
public:
|
|
|
|
ParticleBufferCPUDataAccessor<T>()
|
|
{
|
|
}
|
|
|
|
ParticleBufferCPUDataAccessor(ParticleBuffer* buffer, int32 offset)
|
|
: ParticleBufferCPUDataAccessorBase(buffer, offset)
|
|
{
|
|
}
|
|
|
|
public:
|
|
|
|
FORCE_INLINE T operator[](int32 index) const
|
|
{
|
|
return Get(index);
|
|
}
|
|
|
|
T Get(int32 index) const
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT(index >= 0 && index < _buffer->CPU.Count);
|
|
return *(T*)(_buffer->CPU.Buffer.Get() + _offset + index * _buffer->Stride);
|
|
}
|
|
|
|
FORCE_INLINE T Get(int32 index, const T& defaultValue) const
|
|
{
|
|
if (IsValid())
|
|
{
|
|
return Get(index);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
};
|