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
14 changed files with 205 additions and 54 deletions
Showing only changes of commit a08e538895 - Show all commits

View File

@ -157,15 +157,17 @@
Scale: {x: 1, y: 1, z: 1} Scale: {x: 1, y: 1, z: 1}
IsActive: true IsActive: true
classSHADE::SHParticleEmitterComponent: classSHADE::SHParticleEmitterComponent:
Emission Count: 1 Emission Count: 7
Is Passive: true Is Passive: true
Emission Interval: 0.200000003 Emission Interval: 0.0939999968
Min Life: 2 Min Life: 2
Max Life: 3 Max Life: 3
Minimum Speed: 3 Minimum Speed: 3
Maximum Speed: 6 Maximum Speed: 6
Minimum Size: 0 Minimum Size: 0
Maximum Size: 0.5 Maximum Size: 0.5
Angular Ranges And Offset: {x: 0, y: 0, z: 0, w: 0} Angular Ranges And Offset: {x: 0.699999988, y: 0.469999999, z: 0, w: 0}
Rotation Speed: 0
Texture Asset ID: 63456868
IsActive: true IsActive: true
Scripts: ~ Scripts: ~

View File

@ -1,5 +1,7 @@
#version 450 #version 450
#define PI 3.14159265f
layout(local_size_x = 128) in; layout(local_size_x = 128) in;
struct EmitterParameters struct EmitterParameters
@ -7,14 +9,15 @@ struct EmitterParameters
vec4 angularRangesAndOffsets; vec4 angularRangesAndOffsets;
float minSpeed; float minSpeed;
float maxSpeed; float maxSpeed;
float padding[2]; float rotationSpeed;
uint textureIndex;
vec4 lifeAndSizeRange; // min life, max life, min size, max size vec4 lifeAndSizeRange; // min life, max life, min size, max size
}; };
struct ParticleData struct ParticleData
{ {
vec4 position; vec4 position;
vec4 rotation; vec4 orientationSpeedDecay;
vec4 velocity; vec4 velocity;
vec4 acceleration; vec4 acceleration;
vec4 scaleAndDecay; vec4 scaleAndDecay;
@ -149,8 +152,18 @@ void main()
particle.scaleAndDecay.x = particleSize; particle.scaleAndDecay.x = particleSize;
particle.scaleAndDecay.z = particleSize; particle.scaleAndDecay.z = particleSize;
particle.rotation = vec4 (5.0f); // Set the texture for the particle
particle.textureIndex = emitterParams.data.textureIndex;
// Set orientation and rotation speed
if (emitterParams.data.rotationSpeed != 0.0f)
particle.orientationSpeedDecay = vec4 (rand(seed) * PI, emitterParams.data.rotationSpeed, 0.0f, 0.0f);
else
particle.orientationSpeedDecay = vec4 (0.0f);
particle.acceleration = vec4 (0.01f); particle.acceleration = vec4 (0.01f);
inputParticles.data[index] = particle; inputParticles.data[index] = particle;
} }

View File

@ -13,7 +13,7 @@ struct DrawArraysIndirectArgs
struct ParticleData struct ParticleData
{ {
vec4 position; vec4 position;
vec4 rotation; vec4 orientationSpeedDecay;
vec4 velocity; vec4 velocity;
vec4 acceleration; vec4 acceleration;
vec4 scaleAndDecay; vec4 scaleAndDecay;

View File

@ -1,15 +1,26 @@
#version 460 core #version 460 core
#extension GL_EXT_nonuniform_qualifier : require
layout (location = 0) out vec4 fragColor; layout (location = 0) out vec4 fragColor;
layout (set = 0, binding = 1) uniform sampler2D textures[]; // for textures (global)
// between shader stages // between shader stages
layout(location = 0) in struct layout(location = 0) in struct
{ {
vec2 uv; // location = 0 vec2 uv; // location = 0
} In; } In;
// material stuff
layout(location = 1) flat in struct
{
uint textureIndex;
} InFlat;
void main () void main ()
{ {
fragColor = vec4 (1.0f); fragColor = vec4 (texture(textures [nonuniformEXT(InFlat.textureIndex)], In.uv));
if (fragColor.a < 0.01f)
discard;
} }

Binary file not shown.

View File

@ -19,7 +19,7 @@ struct GenericData
struct ParticleData struct ParticleData
{ {
vec4 position; vec4 position;
vec4 rotation; vec4 orientationSpeedDecay;
vec4 velocity; vec4 velocity;
vec4 acceleration; vec4 acceleration;
vec4 scaleAndDecay; vec4 scaleAndDecay;
@ -56,9 +56,14 @@ layout (std430, set = 2, binding = 4) coherent restrict buffer IndicesData
layout(location = 0) out struct layout(location = 0) out struct
{ {
vec2 uv; // location = 0 vec2 uv; // location = 0
} Out; } Out;
// material stuff
layout(location = 1) out struct
{
uint textureIndex; // location = 1
} OutFlat;
vec2 CreateQuad (in uint vertexID) vec2 CreateQuad (in uint vertexID)
{ {
uint b = 1 << vertexID; uint b = 1 << vertexID;
@ -90,6 +95,7 @@ void main()
vec3 viewUp = normalize(vec3 (cameraData.viewMat[0][1], cameraData.viewMat[1][1], cameraData.viewMat[2][1])); vec3 viewUp = normalize(vec3 (cameraData.viewMat[0][1], cameraData.viewMat[1][1], cameraData.viewMat[2][1]));
particlePos = particle.position.xyz + (viewRight * particlePos.x * particleScaleData.x) + (viewUp * particlePos.y * particleScaleData.y); particlePos = particle.position.xyz + (viewRight * particlePos.x * particleScaleData.x) + (viewUp * particlePos.y * particleScaleData.y);
OutFlat.textureIndex = particle.textureIndex;
gl_Position = cameraData.vpMat * vec4(particlePos, 1.0f); gl_Position = cameraData.vpMat * vec4(particlePos, 1.0f);
} }

Binary file not shown.

View File

@ -811,6 +811,8 @@ namespace SHADE
if (!component) if (!component)
return; return;
auto gfxSystem = SHSystemManager::GetSystem<SHGraphicsSystem>();
ImGui::PushID(SHFamilyID<SHComponent>::GetID<SHParticleEmitterComponent>()); ImGui::PushID(SHFamilyID<SHComponent>::GetID<SHParticleEmitterComponent>());
const auto componentType = rttr::type::get(*component); const auto componentType = rttr::type::get(*component);
@ -860,6 +862,47 @@ namespace SHADE
comp->SetMaxSpeed(val); comp->SetMaxSpeed(val);
}); });
SHEditorWidgets::DragFloat("Rotation Speed",
[comp = component]()
{
return comp->GetMaxSpeed();
},
[comp = component](float val)
{
comp->SetMaxSpeed(val);
});
SHEditorWidgets::DragInt("Texture Index",
[comp = component]()
{
return comp->GetTextureAssetID();
},
[comp = component](AssetID val)
{
comp->SetTextureAssetID(val);
});
if (SHDragDrop::BeginTarget())
{
if (AssetID* payload = SHDragDrop::AcceptPayload<AssetID>(SHDragDrop::DRAG_RESOURCE))
{
Handle<SHTexture> texture = SHResourceManager::LoadOrGet<SHTexture>(*payload);
gfxSystem->BuildTextures();
if (texture)
{
component->SetTextureIndex(texture->TextureArrayIndex);
component->SetTextureAssetID(*payload);
}
else
{
SHLOG_WARNING("[] Attempted to load invalid texture! Texture for particle not set. ");
}
SHDragDrop::EndTarget();
}
}
SHEditorWidgets::CheckBox("Is Passive", [comp = component]() {return comp->GetPassive(); }, [comp = component](bool flag) {comp->SetPassive(flag); }); SHEditorWidgets::CheckBox("Is Passive", [comp = component]() {return comp->GetPassive(); }, [comp = component](bool flag) {comp->SetPassive(flag); });
} }

View File

@ -62,6 +62,21 @@ namespace SHADE
cpuEmitterData.maxSpeed = speed; cpuEmitterData.maxSpeed = speed;
} }
void SHParticleEmitterComponent::SetRotationSpeed(float speed) noexcept
{
cpuEmitterData.rotationSpeed = speed;
}
void SHParticleEmitterComponent::SetTextureIndex(uint32_t index) noexcept
{
cpuEmitterData.textureIndex = index;
}
void SHParticleEmitterComponent::SetTextureAssetID(AssetID id)
{
textureAssetID = id;
}
void SHParticleEmitterComponent::SetMinSize(float size) noexcept void SHParticleEmitterComponent::SetMinSize(float size) noexcept
{ {
cpuEmitterData.lifeAndSizeRange.z = size; cpuEmitterData.lifeAndSizeRange.z = size;
@ -113,6 +128,21 @@ namespace SHADE
return cpuEmitterData.maxSpeed; return cpuEmitterData.maxSpeed;
} }
float SHParticleEmitterComponent::GetRotationSpeed(void) const noexcept
{
return cpuEmitterData.rotationSpeed;
}
uint32_t SHParticleEmitterComponent::GetTextureIndex(void) const noexcept
{
return cpuEmitterData.textureIndex;
}
AssetID SHParticleEmitterComponent::GetTextureAssetID(void) const noexcept
{
return textureAssetID;
}
float SHParticleEmitterComponent::GetMinSize(void) const noexcept float SHParticleEmitterComponent::GetMinSize(void) const noexcept
{ {
return cpuEmitterData.lifeAndSizeRange.z; return cpuEmitterData.lifeAndSizeRange.z;

View File

@ -4,6 +4,7 @@
#include "Math/Vector/SHVec4.h" #include "Math/Vector/SHVec4.h"
#include "ECS_Base/Components/SHComponent.h" #include "ECS_Base/Components/SHComponent.h"
#include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h" #include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h"
#include <Assets/SHAssetMacros.h>
namespace SHADE namespace SHADE
{ {
@ -25,7 +26,11 @@ namespace SHADE
//! Maximum starting velocity //! Maximum starting velocity
float maxSpeed; float maxSpeed;
float padding[2]; //! Rotational speed of the quad
float rotationSpeed;
//! Texture used by the particle
uint32_t textureIndex;
//! Spawn lifetime and size range (min and max) //! Spawn lifetime and size range (min and max)
SHVec4 lifeAndSizeRange; SHVec4 lifeAndSizeRange;
@ -104,6 +109,9 @@ namespace SHADE
//! For all the dynamic SSBOs in the descriptor set //! For all the dynamic SSBOs in the descriptor set
std::array<std::array<uint32_t, 5>, SHGraphicsConstants::NUM_FRAME_BUFFERS> dynamicOffsets{}; std::array<std::array<uint32_t, 5>, SHGraphicsConstants::NUM_FRAME_BUFFERS> dynamicOffsets{};
//! For the emitter to use to give particles their texture
AssetID textureAssetID;
public: public:
void OnCreate(void) override final; void OnCreate(void) override final;
void OnDestroy(void) override final; void OnDestroy(void) override final;
@ -118,6 +126,9 @@ namespace SHADE
void SetAngularRangesAndOffsets (SHVec4 const& ranges) noexcept; void SetAngularRangesAndOffsets (SHVec4 const& ranges) noexcept;
void SetMinSpeed (float speed) noexcept; void SetMinSpeed (float speed) noexcept;
void SetMaxSpeed (float speed) noexcept; void SetMaxSpeed (float speed) noexcept;
void SetRotationSpeed (float speed) noexcept;
void SetTextureIndex (uint32_t index) noexcept;
void SetTextureAssetID (AssetID id);
void SetMinSize (float size) noexcept; void SetMinSize (float size) noexcept;
void SetMaxSize (float size) noexcept; void SetMaxSize (float size) noexcept;
@ -130,6 +141,9 @@ namespace SHADE
SHVec4 const& GetAngularRangesAndOffsets (void) const noexcept; SHVec4 const& GetAngularRangesAndOffsets (void) const noexcept;
float GetMinSpeed (void) const noexcept; float GetMinSpeed (void) const noexcept;
float GetMaxSpeed (void) const noexcept; float GetMaxSpeed (void) const noexcept;
float GetRotationSpeed (void) const noexcept;
uint32_t GetTextureIndex (void) const noexcept;
AssetID GetTextureAssetID (void) const noexcept;
float GetMinSize (void) const noexcept; float GetMinSize (void) const noexcept;
float GetMaxSize (void) const noexcept; float GetMaxSize (void) const noexcept;

View File

@ -200,9 +200,9 @@ namespace SHADE
}; };
// ...copy assign barriers on heap // ...copy assign barriers on heap
preUpdateBarriers[EMITTER_INDEX * 3] = inputParticleDataBarrierPreUpdate; preUpdateBarriers.push_back(inputParticleDataBarrierPreUpdate);
preUpdateBarriers[(EMITTER_INDEX * 3) + 1] = freelistDataBarrierPreUpdate; preUpdateBarriers.push_back(freelistDataBarrierPreUpdate);
preUpdateBarriers[(EMITTER_INDEX * 3) + 2] = indicesDataBarrierPreUpdate; preUpdateBarriers.push_back(indicesDataBarrierPreUpdate);
// make new barrier on stack... // make new barrier on stack...
vk::BufferMemoryBarrier outputParticleDataBarrierPostUpdate vk::BufferMemoryBarrier outputParticleDataBarrierPostUpdate
@ -235,9 +235,9 @@ namespace SHADE
}; };
// ...copy assign barriers on heap // ...copy assign barriers on heap
postUpdateBarriers[EMITTER_INDEX * 3] = outputParticleDataBarrierPostUpdate; postUpdateBarriers.push_back(outputParticleDataBarrierPostUpdate);
postUpdateBarriers[(EMITTER_INDEX * 3) + 1] = indicesDataBarrierPostUpdate; postUpdateBarriers.push_back(indicesDataBarrierPostUpdate);
postUpdateBarriers[(EMITTER_INDEX * 3) + 2] = drawDataBarrierPostUpdate; postUpdateBarriers.push_back(drawDataBarrierPostUpdate);
} }
void SHParticleSubSystem::Init(Handle<SHVkLogicalDevice> device, Handle<SHVkDescriptorPool> inDescPool, Handle<SHVkRenderpass> compatibleRenderpass, Handle<SHSubpass> subpass, Handle<SHVkShaderModule> VS, Handle<SHVkShaderModule> FS, Handle<SHVkShaderModule> emitCS, Handle<SHVkShaderModule> defaultUpdateCS) noexcept void SHParticleSubSystem::Init(Handle<SHVkLogicalDevice> device, Handle<SHVkDescriptorPool> inDescPool, Handle<SHVkRenderpass> compatibleRenderpass, Handle<SHSubpass> subpass, Handle<SHVkShaderModule> VS, Handle<SHVkShaderModule> FS, Handle<SHVkShaderModule> emitCS, Handle<SHVkShaderModule> defaultUpdateCS) noexcept
@ -334,15 +334,15 @@ namespace SHADE
// Every emitter will have its own barrier for its particle data and freelist data. Indices data is not needed since // Every emitter will have its own barrier for its particle data and freelist data. Indices data is not needed since
// it's mainly used in update and rendering so a barrier for it is NOT needed here. // it's mainly used in update and rendering so a barrier for it is NOT needed here.
std::vector<vk::BufferMemoryBarrier> preUpdateBarriers{}; std::vector<vk::BufferMemoryBarrier> preUpdateBarriers{};
preUpdateBarriers.resize(emitters.size() * 3); preUpdateBarriers.reserve(emitters.size() * 3);
// After we invoke the updates for the emitters, we need to make sure all particles and indices data are done updating // After we invoke the updates for the emitters, we need to make sure all particles and indices data are done updating
// before we issue them for rendering. // before we issue them for rendering.
std::vector<vk::BufferMemoryBarrier> postUpdateBarriers{}; std::vector<vk::BufferMemoryBarrier> postUpdateBarriers{};
postUpdateBarriers.resize(emitters.size() * 3); postUpdateBarriers.reserve(emitters.size() * 3);
std::vector<vk::BufferMemoryBarrier> preDrawBarriers{}; std::vector<vk::BufferMemoryBarrier> preDrawBarriers{};
preDrawBarriers.resize(emitters.size()); preDrawBarriers.reserve(emitters.size());
// If we wanted to be VERY safe, a barrier would be good here to make sure output particles have finish reading input particles in // If we wanted to be VERY safe, a barrier would be good here to make sure output particles have finish reading input particles in
@ -364,6 +364,8 @@ namespace SHADE
waitFence->Wait(true); waitFence->Wait(true);
for (auto& emitter : emitters) for (auto& emitter : emitters)
{
if (emitter.isActive)
{ {
if (!emitter.initialized) if (!emitter.initialized)
InitializeComponent(emitter); InitializeComponent(emitter);
@ -399,10 +401,14 @@ namespace SHADE
// Emitter will emit once and stop emitting next frame (unless specified before reaching here to do so). // Emitter will emit once and stop emitting next frame (unless specified before reaching here to do so).
emitter.toEmit = false; emitter.toEmit = false;
}
++i; ++i;
} }
// issue the barrier to wait // issue the barrier to wait
if (!preUpdateBarriers.empty())
cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader | vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eDrawIndirect, vk::PipelineStageFlagBits::eComputeShader, {}, {}, preUpdateBarriers, {}); cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader | vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eDrawIndirect, vk::PipelineStageFlagBits::eComputeShader, {}, {}, preUpdateBarriers, {});
@ -417,13 +423,16 @@ namespace SHADE
for (auto& emitter : emitters) for (auto& emitter : emitters)
{ {
if (emitter.isActive)
UpdateCompoennt(cmdBuffer, emitter, frameIndex); UpdateCompoennt(cmdBuffer, emitter, frameIndex);
} }
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
/* AFTER UPDATING, RENDER PARTICLES */ /* AFTER UPDATING, RENDER PARTICLES */
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
// issue the barrier to wait for output particles to be done updating and indices data to finish being modified. // issue the barrier to wait for output particles to be done updating and indices data to finish being modified.
if (!postUpdateBarriers.empty())
cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eDrawIndirect | vk::PipelineStageFlagBits::eVertexShader, {}, {}, postUpdateBarriers, {}); cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eDrawIndirect | vk::PipelineStageFlagBits::eVertexShader, {}, {}, postUpdateBarriers, {});
} }
@ -456,6 +465,8 @@ namespace SHADE
// TODO: Issue barrier for output particle data. Semaphore should also be issued outside in SHGraphicsSystem // TODO: Issue barrier for output particle data. Semaphore should also be issued outside in SHGraphicsSystem
for (auto& emitter : emitters) for (auto& emitter : emitters)
{
if (emitter.isActive)
{ {
// bind the descriptor sets required for emitting particles // bind the descriptor sets required for emitting particles
cmdBuffer->BindDescriptorSet(emitter.particleDescriptorSet, SH_PIPELINE_TYPE::GRAPHICS, mappings.at(SHPredefinedDescriptorTypes::PARTICLES), emitter.dynamicOffsets[frameIndex]); cmdBuffer->BindDescriptorSet(emitter.particleDescriptorSet, SH_PIPELINE_TYPE::GRAPHICS, mappings.at(SHPredefinedDescriptorTypes::PARTICLES), emitter.dynamicOffsets[frameIndex]);
@ -463,6 +474,7 @@ namespace SHADE
RenderComponent(cmdBuffer, emitter, frameIndex); RenderComponent(cmdBuffer, emitter, frameIndex);
} }
} }
}
void SHParticleSubSystem::Exit(void) noexcept void SHParticleSubSystem::Exit(void) noexcept

View File

@ -514,6 +514,8 @@ namespace YAML
static constexpr std::string_view MAX_SIZE_TAG = "Maximum Size"; static constexpr std::string_view MAX_SIZE_TAG = "Maximum Size";
static constexpr std::string_view MIN_SPEED_TAG = "Minimum Speed"; static constexpr std::string_view MIN_SPEED_TAG = "Minimum Speed";
static constexpr std::string_view MAX_SPEED_TAG = "Maximum Speed"; static constexpr std::string_view MAX_SPEED_TAG = "Maximum Speed";
static constexpr std::string_view ROTATION_SPEED_TAG = "Rotation Speed";
static constexpr std::string_view TEXTURE_ASSET_ID_TAG = "Texture Asset ID";
static YAML::Node encode(SHParticleEmitterComponent const& rhs) static YAML::Node encode(SHParticleEmitterComponent const& rhs)
{ {
@ -528,11 +530,15 @@ namespace YAML
node[MIN_SIZE_TAG.data()] = rhs.GetMinSize(); node[MIN_SIZE_TAG.data()] = rhs.GetMinSize();
node[MAX_SIZE_TAG.data()] = rhs.GetMaxSize(); node[MAX_SIZE_TAG.data()] = rhs.GetMaxSize();
node[ANGULAR_RANGES_OFFSET_TAG.data()] = rhs.GetAngularRangesAndOffsets(); node[ANGULAR_RANGES_OFFSET_TAG.data()] = rhs.GetAngularRangesAndOffsets();
node[ROTATION_SPEED_TAG.data()] = rhs.GetRotationSpeed();
node[TEXTURE_ASSET_ID_TAG.data()] = rhs.GetTextureAssetID();
return node; return node;
} }
static bool decode(YAML::Node const& node, SHParticleEmitterComponent& rhs) static bool decode(YAML::Node const& node, SHParticleEmitterComponent& rhs)
{ {
auto gfxSystem = SHSystemManager::GetSystem<SHGraphicsSystem>();
if (node[EMISSION_COUNT_TAG.data()].IsDefined()) if (node[EMISSION_COUNT_TAG.data()].IsDefined())
rhs.SetEmissionCount(node[EMISSION_COUNT_TAG.data()].as<uint32_t>()); rhs.SetEmissionCount(node[EMISSION_COUNT_TAG.data()].as<uint32_t>());
@ -560,6 +566,20 @@ namespace YAML
if (node[MAX_SIZE_TAG.data()].IsDefined()) if (node[MAX_SIZE_TAG.data()].IsDefined())
rhs.SetMaxSize(node[MAX_SIZE_TAG.data()].as<float>()); rhs.SetMaxSize(node[MAX_SIZE_TAG.data()].as<float>());
if (node[ROTATION_SPEED_TAG.data()].IsDefined())
rhs.SetRotationSpeed(node[ROTATION_SPEED_TAG.data()].as<float>());
if (node[TEXTURE_ASSET_ID_TAG.data()].IsDefined())
{
AssetID id = node[TEXTURE_ASSET_ID_TAG.data()].as<AssetID>();
Handle<SHTexture> texture = SHResourceManager::LoadOrGet<SHTexture>(id);
gfxSystem->BuildTextures();
rhs.SetTextureIndex(texture->TextureArrayIndex);
rhs.SetTextureAssetID(id);
}
return true; return true;
} }
}; };