Improved particles and trajectory rendering #430

Merged
Xenosas1337 merged 19 commits from SP3-1-Rendering into main 2023-03-20 16:55:29 +08:00
10 changed files with 253 additions and 20 deletions
Showing only changes of commit 070f01bf67 - Show all commits

View File

@ -22,18 +22,18 @@
Scripts: ~
- EID: 1
Name: Raccoon
IsActive: true
IsActive: false
NumberOfChildren: 1
Components:
Transform Component:
Translate: {x: 0, y: 0.201105013, z: 0}
Rotate: {x: 0.00523597933, y: -2.96353412, z: -6.40293041e-10}
Scale: {x: 1.00000191, y: 1, z: 1.00000191}
IsActive: true
IsActive: false
Renderable Component:
Mesh: 149697411
Material: 126974645
IsActive: true
IsActive: false
Scripts: ~
- EID: 3
Name: Bag
@ -87,18 +87,18 @@
Scripts: ~
- EID: 5
Name: Floor
IsActive: true
IsActive: false
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 0, y: 0.0810000002, z: 0}
Rotate: {x: -1.57079625, y: 0, z: -0}
Scale: {x: 50, y: 49.9999924, z: 49.9999924}
IsActive: true
IsActive: false
Renderable Component:
Mesh: 141771688
Material: 124370424
IsActive: true
IsActive: false
Scripts: ~
- EID: 6
Name: TrajectoryTest
@ -156,4 +156,17 @@
Rotate: {x: 0, y: 0, z: 0}
Scale: {x: 1, y: 1, z: 1}
IsActive: true
classSHADE::SHParticleEmitterComponent:
Emission Count: 1
Is Passive: false
Emission Interval: 3
Min Life: 7
Max Life: 20
Angular Min: {x: 2.29999995, y: 1.70000005, z: 2.9000001}
Angular Max: {x: 0, y: 0, z: 0}
Minimum Velocity: {x: 2, y: 3, z: 4}
Maximum Velocity: {x: 0, y: 0, z: 0}
Minimum Size: 5
Maximum Size: 10
IsActive: true
Scripts: ~

View File

@ -6,6 +6,8 @@ struct EmitterParameters
{
vec4 angularMin;
vec4 angularMax;
vec4 minVel;
vec4 maxVel;
vec4 lifeAndSizeRange; // min life, max life, min size, max size
};
@ -109,6 +111,7 @@ void main()
if (emitterInvocationIndex >= emitterPushConstant.emissionCount)
return;
// Freecount will start at max particles. Here we subtract every time we emit.
int freelistIndex = atomicAdd (freelist.freeCount, -1) - 1;
if (freelistIndex < 0)
atomicAdd (freelist.freeCount, 1);
@ -124,9 +127,18 @@ void main()
// emit particle from emitter position
particle.position = vec4 (emitterPosition.xyz, 1.0f);
// randomize life value that ranges from minLife to maxLife
particle.life = map (rand(seed), -1.0f, 1.0f, emitterParams.data.lifeAndSizeRange.x, emitterParams.data.lifeAndSizeRange.y);
// Set its velocity
particle.velocity = emitterParams.data.minVel;
// randomize life value that ranges from minLife to maxLife
particle.life = map (rand(seed), 0.0f, 1.0f, emitterParams.data.lifeAndSizeRange.x, emitterParams.data.lifeAndSizeRange.y);
// Set size of particle
particle.scaleAndDecay.x = emitterParams.data.lifeAndSizeRange.z;
particle.scaleAndDecay.y = emitterParams.data.lifeAndSizeRange.z;
particle.rotation = vec4 (5.0f);
particle.acceleration = vec4 (0.01f);
inputParticles.data[index] = particle;
}

View File

@ -826,6 +826,48 @@ namespace SHADE
SHEditorWidgets::DragFloat("Emission Interval", [comp = component]() {return comp->GetEmissionInterval(); }, [comp = component](float interval) {comp->SetEmissionInterval(interval); });
SHEditorWidgets::DragFloat("Min Life", [comp = component]() {return comp->GetMinLife(); }, [comp = component](float val) {comp->SetMinLife(val); });
SHEditorWidgets::DragFloat("Max Life", [comp = component]() {return comp->GetMaxLife(); }, [comp = component](float val) {comp->SetMaxLife(val); });
SHEditorWidgets::DragFloat("Min Size", [comp = component]() {return comp->GetMinSize(); }, [comp = component](float val) {comp->SetMinSize(val); });
SHEditorWidgets::DragFloat("Man Size", [comp = component]() {return comp->GetMaxSize(); }, [comp = component](float val) {comp->SetMaxSize(val); });
SHEditorWidgets::DragVec3("Angular Min", {"x", "y", "z"},
[comp = component]()
{
return comp->GetAngularMin();
},
[comp = component](SHVec3 const& val)
{
comp->SetAngularMin(val);
});
SHEditorWidgets::DragVec3("Angular Max", { "x", "y", "z" },
[comp = component]()
{
return comp->GetAngularMax();
},
[comp = component](SHVec3 const& val)
{
comp->SetAngularMax(val);
});
SHEditorWidgets::DragVec3("Min Vel", { "x", "y", "z" },
[comp = component]()
{
return comp->GetMinVelocity();
},
[comp = component](SHVec3 const& val)
{
comp->SetMinVelocity(val);
});
SHEditorWidgets::DragVec3("Max Vel", { "x", "y", "z" },
[comp = component]()
{
return comp->GetMaxVelocity();
},
[comp = component](SHVec3 const& val)
{
comp->SetMaxVelocity(val);
});
SHEditorWidgets::CheckBox("Is Passive", [comp = component]() {return comp->GetPassive(); }, [comp = component](bool flag) {comp->SetPassive(flag); });

View File

@ -361,7 +361,7 @@ namespace SHADE
vfxSubpass->AddExteriorDrawCalls([=](Handle<SHVkCommandBuffer> cmdBuffer, Handle<SHRenderer> renderer, uint32_t frameIndex)
{
trajectoryRenderingSubSystem->Render(cmdBuffer, renderer, frameIndex);
//particleSubSystem->Render(cmdBuffer, renderer, frameIndex);
particleSubSystem->Render(cmdBuffer, renderer, frameIndex);
});
/*-----------------------------------------------------------------------*/

View File

@ -46,6 +46,36 @@ namespace SHADE
cpuEmitterData.lifeAndSizeRange.y = val;
}
void SHParticleEmitterComponent::SetAngularMin(SHVec3 const& min) noexcept
{
cpuEmitterData.angularMin = SHVec4 (min);
}
void SHParticleEmitterComponent::SetAngularMax(SHVec3 const& max) noexcept
{
cpuEmitterData.angularMax = SHVec4(max);
}
void SHParticleEmitterComponent::SetMinVelocity(SHVec3 const& vel) noexcept
{
cpuEmitterData.minVel = vel;
}
void SHParticleEmitterComponent::SetMaxVelocity(SHVec3 const& vel) noexcept
{
cpuEmitterData.maxVel = vel;
}
void SHParticleEmitterComponent::SetMinSize(float size) noexcept
{
cpuEmitterData.lifeAndSizeRange.z = size;
}
void SHParticleEmitterComponent::SetMaxSize(float size) noexcept
{
cpuEmitterData.lifeAndSizeRange.w = size;
}
uint32_t SHParticleEmitterComponent::GetEmissionCount(void) const noexcept
{
return emissionCount;
@ -72,4 +102,34 @@ namespace SHADE
}
SHVec3 SHParticleEmitterComponent::GetAngularMin(void) const noexcept
{
return SHVec3 (cpuEmitterData.angularMin.x, cpuEmitterData.angularMin.y, cpuEmitterData.angularMin.z);
}
SHVec3 SHParticleEmitterComponent::GetMinVelocity(void) const noexcept
{
return SHVec3(cpuEmitterData.minVel.x, cpuEmitterData.minVel.y, cpuEmitterData.minVel.z);
}
SHVec3 SHParticleEmitterComponent::GetMaxVelocity(void) const noexcept
{
return SHVec3(cpuEmitterData.maxVel.x, cpuEmitterData.maxVel.y, cpuEmitterData.maxVel.z);
}
float SHParticleEmitterComponent::GetMinSize(void) const noexcept
{
return cpuEmitterData.lifeAndSizeRange.z;
}
float SHParticleEmitterComponent::GetMaxSize(void) const noexcept
{
return cpuEmitterData.lifeAndSizeRange.w;
}
SHVec3 SHParticleEmitterComponent::GetAngularMax(void) const noexcept
{
return SHVec3(cpuEmitterData.angularMax.x, cpuEmitterData.angularMax.y, cpuEmitterData.angularMax.z);
}
}

View File

@ -22,6 +22,12 @@ namespace SHADE
//! Maximum emitting angular range
SHVec4 angularMax;
//! minimum starting velocity
SHVec4 minVel;
//! Maximum starting velocity
SHVec4 maxVel;
//! Spawn lifetime and size range (min and max)
SHVec4 lifeAndSizeRange;
};
@ -110,12 +116,25 @@ namespace SHADE
void SetEmissionInterval (float interval) noexcept;
void SetMinLife (float val) noexcept;
void SetMaxLife (float val) noexcept;
void SetAngularMin (SHVec3 const& min) noexcept;
void SetAngularMax (SHVec3 const& max) noexcept;
void SetMinVelocity (SHVec3 const& vel) noexcept;
void SetMaxVelocity (SHVec3 const& vel) noexcept;
void SetMinSize (float size) noexcept;
void SetMaxSize (float size) noexcept;
uint32_t GetEmissionCount (void) const noexcept;
bool GetPassive (void) const noexcept;
float GetEmissionInterval (void) const noexcept;
float GetMinLife (void) const noexcept;
float GetMaxLife (void) const noexcept;
uint32_t GetEmissionCount (void) const noexcept;
bool GetPassive (void) const noexcept;
float GetEmissionInterval (void) const noexcept;
float GetMinLife (void) const noexcept;
float GetMaxLife (void) const noexcept;
SHVec3 GetAngularMax (void) const noexcept;
SHVec3 GetAngularMin (void) const noexcept;
SHVec3 GetMinVelocity (void) const noexcept;
SHVec3 GetMaxVelocity (void) const noexcept;
float GetMinSize (void) const noexcept;
float GetMaxSize (void) const noexcept;
friend class SHParticleSubSystem;

View File

@ -27,7 +27,7 @@ namespace SHADE
uint32_t sizeofUint = static_cast<uint32_t>(sizeof(uint32_t));
uint32_t sizeofIndirectCmdAligned = logicalDevice->PadSSBOSize(sizeof(vk::DrawIndirectCommand));
uint32_t emitterStructAligned = logicalDevice->PadSSBOSize(sizeof (SHParticleEmitterComponent::GPUEmitterStruct));
uint32_t particleChunkStructAligned = logicalDevice->PadSSBOSize(sizeof (SHParticleEmitterComponent::GPUParticleStruct) * comp.maxParticles);
uint32_t particleChunkStructAligned = logicalDevice->PadSSBOSize(sizeof (SHParticleEmitterComponent::GPUParticleStruct)) * comp.maxParticles;
uint32_t indicesDataAligned = logicalDevice->PadSSBOSize(sizeofUint * comp.maxParticles);
@ -36,9 +36,11 @@ namespace SHADE
// Buffer Initialization
{
// count, value
// count, value. Initialize free count to max particles and indices to particle indices
std::vector<uint32_t> freelistInit(comp.maxParticles + 1, 0);
freelistInit[0] = comp.maxParticles;
for (uint32_t i = 0; i < comp.maxParticles; ++i)
freelistInit[i + 1] = i;
// Particle emitter buffer. Multiple copies, Host-visible mapped. We want multiple copies because we'll be writing to it from the CPU. We don't want to do that while the GPU
// is using it during compute operations so we write to another portion.
@ -89,10 +91,10 @@ namespace SHADE
// After buffers are created, we want to populate all bindings(6) with the buffers
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_EMITTER_DATA, { &comp.emitterData, 1 }, 0, emitterStructAligned);
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_INPUT_DATA, { &comp.particleData, 1 }, 0, PARTICLE_FRAME_CHUNK_SIZE); // input and output will be th same until we bind using dynamic offsets
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_OUTPUT_DATA, { &comp.particleData, 1 }, 0, PARTICLE_FRAME_CHUNK_SIZE);
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_INPUT_DATA, { &comp.particleData, 1 }, 0, particleChunkStructAligned); // input and output will be th same until we bind using dynamic offsets
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_OUTPUT_DATA, { &comp.particleData, 1 }, 0, particleChunkStructAligned);
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_FREELIST_DATA, { &comp.freelistData, 1 }, 0, sizeofUint * (comp.maxParticles + 1));
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_INDICES_DATA, { &comp.indicesData, 1 }, 0, sizeofUint * (comp.maxParticles));
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_INDICES_DATA, { &comp.indicesData, 1 }, 0, indicesDataAligned);
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_DRAW_DATA, { &comp.drawCallData, 1 }, 0, sizeofIndirectCmdAligned);
set->UpdateDescriptorSetBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_EMITTER_DATA);
@ -139,6 +141,8 @@ namespace SHADE
cmdBuffer->SetPushConstantVariable("EmitterPushConstant.emitterPosition", transform->GetWorldPosition(), SH_PIPELINE_TYPE::COMPUTE);
cmdBuffer->SetPushConstantVariable("EmitterPushConstant.emissionCount", comp.emissionCount, SH_PIPELINE_TYPE::COMPUTE);
cmdBuffer->SubmitPushConstants(SH_PIPELINE_TYPE::COMPUTE);
// emit particles
cmdBuffer->ComputeDispatch((comp.emissionCount / EMITTER_WORKGROUP_SIZE) + 1, 1, 1);
}
@ -388,17 +392,24 @@ namespace SHADE
/* AFTER UPDATING, RENDER PARTICLES */
/*-----------------------------------------------------------------------*/
// issue the barrier to wait for output particles to be done updating and indices data to finish being modified.
//cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eComputeShader, {}, {}, postUpdateBarriers, {});
cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eComputeShader, {}, {}, postUpdateBarriers, {});
}
void SHParticleSubSystem::Render(Handle<SHVkCommandBuffer> cmdBuffer, Handle<SHRenderer> renderer, uint32_t frameIndex) noexcept
{
auto& emitters = SHComponentManager::GetDense<SHParticleEmitterComponent>();
auto const& mappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::PARTICLE_RENEDERING);
// bind the pipeline for updating
cmdBuffer->BindPipeline(renderingPipelineData.pipeline);
// TODO: Issue barrier for output particle data. Semaphore should also be issued outside in SHGraphicsSystem
for (auto& emitter : emitters)
{
// bind the descriptor sets required for emitting particles
cmdBuffer->BindDescriptorSet(emitter.particleDescriptorSet, SH_PIPELINE_TYPE::GRAPHICS, mappings.at(SHPredefinedDescriptorTypes::PARTICLES), emitter.dynamicOffsets[frameIndex]);
RenderComponent(cmdBuffer, emitter, frameIndex);
}
}

View File

@ -311,6 +311,7 @@ namespace SHADE
AddComponentID<SHUIComponent>(componentIDList, componentsNode);
AddComponentID<SHAudioListenerComponent>(componentIDList, componentsNode);
AddComponentID<SHTrajectoryRenderableComponent>(componentIDList, componentsNode);
AddComponentID<SHParticleEmitterComponent>(componentIDList, componentsNode);
return componentIDList;
}

View File

@ -17,6 +17,7 @@
#include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h"
#include "Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h"
#include "Physics/Collision/Shapes/SHCapsule.h"
#include <Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h>
namespace YAML
{
@ -35,6 +36,9 @@ namespace YAML
struct HasYAMLConv<SHAnimatorComponent> : std::true_type {};
template<>
struct HasYAMLConv<SHTrajectoryRenderableComponent> : std::true_type {};
template<>
struct HasYAMLConv<SHParticleEmitterComponent> : std::true_type {};
template<>
struct convert<SHVec4>
@ -497,4 +501,75 @@ namespace YAML
}
};
template<>
struct convert<SHParticleEmitterComponent>
{
static constexpr std::string_view EMISSION_COUNT_TAG = "Emission Count";
static constexpr std::string_view IS_PASSIVE_TAG = "Is Passive";
static constexpr std::string_view EMISSION_INTERVAL_TAG = "Emission Interval";
static constexpr std::string_view MIN_LIFE_TAG = "Min Life";
static constexpr std::string_view MAX_LIFE_TAG = "Max Life";
static constexpr std::string_view ANGULAR_MIN_TAG = "Angular Min";
static constexpr std::string_view ANGULAR_MAX_TAG = "Angular Max";
static constexpr std::string_view MIN_VEL_TAG = "Minimum Velocity";
static constexpr std::string_view MAX_VEL_TAG = "Maximum Velocity";
static constexpr std::string_view MIN_SIZE_TAG = "Minimum Size";
static constexpr std::string_view MAX_SIZE_TAG = "Maximum Size";
static YAML::Node encode(SHParticleEmitterComponent const& rhs)
{
YAML::Node node;
node[EMISSION_COUNT_TAG.data()] = rhs.GetEmissionCount();
node[IS_PASSIVE_TAG.data()] = rhs.GetPassive();
node[EMISSION_INTERVAL_TAG.data()] = rhs.GetEmissionInterval();
node[MIN_LIFE_TAG.data()] = rhs.GetMinLife();
node[MAX_LIFE_TAG.data()] = rhs.GetMaxLife();
node[ANGULAR_MIN_TAG.data()] = rhs.GetAngularMin();
node[ANGULAR_MAX_TAG.data()] = rhs.GetAngularMax();
node[MIN_VEL_TAG.data()] = rhs.GetMinVelocity();
node[MAX_VEL_TAG.data()] = rhs.GetMaxVelocity();
node[MIN_SIZE_TAG.data()] = rhs.GetMinSize();
node[MAX_SIZE_TAG.data()] = rhs.GetMaxSize();
return node;
}
static bool decode(YAML::Node const& node, SHParticleEmitterComponent& rhs)
{
if (node[EMISSION_COUNT_TAG.data()].IsDefined())
rhs.SetEmissionCount(node[EMISSION_COUNT_TAG.data()].as<uint32_t>());
if (node[IS_PASSIVE_TAG.data()].IsDefined())
rhs.SetPassive(node[IS_PASSIVE_TAG.data()].as<bool>());
if (node[EMISSION_INTERVAL_TAG.data()].IsDefined())
rhs.SetEmissionInterval(node[EMISSION_INTERVAL_TAG.data()].as<float>());
if (node[MIN_LIFE_TAG.data()].IsDefined())
rhs.SetMinLife(node[MIN_LIFE_TAG.data()].as<float>());
if (node[MAX_LIFE_TAG.data()].IsDefined())
rhs.SetMaxLife(node[MAX_LIFE_TAG.data()].as<float>());
if (node[ANGULAR_MIN_TAG.data()].IsDefined())
rhs.SetAngularMin(node[ANGULAR_MIN_TAG.data()].as<SHVec3>());
if (node[ANGULAR_MAX_TAG.data()].IsDefined())
rhs.SetAngularMax(node[ANGULAR_MAX_TAG.data()].as<SHVec3>());
if (node[MIN_VEL_TAG.data()].IsDefined())
rhs.SetMinVelocity(node[MIN_VEL_TAG.data()].as<SHVec3>());
if (node[MAX_VEL_TAG.data()].IsDefined())
rhs.SetMaxVelocity(node[MAX_VEL_TAG.data()].as<SHVec3>());
if (node[MIN_SIZE_TAG.data()].IsDefined())
rhs.SetMinSize(node[MIN_SIZE_TAG.data()].as<float>());
if (node[MAX_SIZE_TAG.data()].IsDefined())
rhs.SetMaxSize(node[MAX_SIZE_TAG.data()].as<float>());
return true;
}
};
}