diff --git a/Assets/Scenes/Scene2.shade b/Assets/Scenes/Scene2.shade index 76595523..4b3d5118 100644 --- a/Assets/Scenes/Scene2.shade +++ b/Assets/Scenes/Scene2.shade @@ -157,15 +157,17 @@ Scale: {x: 1, y: 1, z: 1} IsActive: true classSHADE::SHParticleEmitterComponent: - Emission Count: 1 + Emission Count: 7 Is Passive: true - Emission Interval: 0.200000003 + Emission Interval: 0.0939999968 Min Life: 2 Max Life: 3 Minimum Speed: 3 Maximum Speed: 6 Minimum Size: 0 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 Scripts: ~ \ No newline at end of file diff --git a/Assets/Shaders/ParticleEmit_CS.glsl b/Assets/Shaders/ParticleEmit_CS.glsl index 1e2a0882..c9e6cc8b 100644 --- a/Assets/Shaders/ParticleEmit_CS.glsl +++ b/Assets/Shaders/ParticleEmit_CS.glsl @@ -1,5 +1,7 @@ #version 450 +#define PI 3.14159265f + layout(local_size_x = 128) in; struct EmitterParameters @@ -7,14 +9,15 @@ struct EmitterParameters vec4 angularRangesAndOffsets; float minSpeed; float maxSpeed; - float padding[2]; + float rotationSpeed; + uint textureIndex; vec4 lifeAndSizeRange; // min life, max life, min size, max size }; struct ParticleData { vec4 position; - vec4 rotation; + vec4 orientationSpeedDecay; vec4 velocity; vec4 acceleration; vec4 scaleAndDecay; @@ -149,8 +152,18 @@ void main() particle.scaleAndDecay.x = 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); + inputParticles.data[index] = particle; } \ No newline at end of file diff --git a/Assets/Shaders/ParticleEmit_CS.shshaderb b/Assets/Shaders/ParticleEmit_CS.shshaderb index 125d5621..6f340a27 100644 Binary files a/Assets/Shaders/ParticleEmit_CS.shshaderb and b/Assets/Shaders/ParticleEmit_CS.shshaderb differ diff --git a/Assets/Shaders/ParticleUpdate_CS.glsl b/Assets/Shaders/ParticleUpdate_CS.glsl index 56b0470c..762ff01a 100644 --- a/Assets/Shaders/ParticleUpdate_CS.glsl +++ b/Assets/Shaders/ParticleUpdate_CS.glsl @@ -13,7 +13,7 @@ struct DrawArraysIndirectArgs struct ParticleData { vec4 position; - vec4 rotation; + vec4 orientationSpeedDecay; vec4 velocity; vec4 acceleration; vec4 scaleAndDecay; diff --git a/Assets/Shaders/ParticleUpdate_CS.shshaderb b/Assets/Shaders/ParticleUpdate_CS.shshaderb index 8f0de701..d36fa560 100644 Binary files a/Assets/Shaders/ParticleUpdate_CS.shshaderb and b/Assets/Shaders/ParticleUpdate_CS.shshaderb differ diff --git a/Assets/Shaders/Particle_FS.glsl b/Assets/Shaders/Particle_FS.glsl index d365b110..243baa2e 100644 --- a/Assets/Shaders/Particle_FS.glsl +++ b/Assets/Shaders/Particle_FS.glsl @@ -1,15 +1,26 @@ #version 460 core +#extension GL_EXT_nonuniform_qualifier : require layout (location = 0) out vec4 fragColor; +layout (set = 0, binding = 1) uniform sampler2D textures[]; // for textures (global) + // between shader stages layout(location = 0) in struct { vec2 uv; // location = 0 - } In; +// material stuff +layout(location = 1) flat in struct +{ + uint textureIndex; +} InFlat; + + void main () { - fragColor = vec4 (1.0f); + fragColor = vec4 (texture(textures [nonuniformEXT(InFlat.textureIndex)], In.uv)); + if (fragColor.a < 0.01f) + discard; } diff --git a/Assets/Shaders/Particle_FS.shshaderb b/Assets/Shaders/Particle_FS.shshaderb index 19e503b2..edd2dd6b 100644 Binary files a/Assets/Shaders/Particle_FS.shshaderb and b/Assets/Shaders/Particle_FS.shshaderb differ diff --git a/Assets/Shaders/Particle_VS.glsl b/Assets/Shaders/Particle_VS.glsl index d74acdd9..211bb4de 100644 --- a/Assets/Shaders/Particle_VS.glsl +++ b/Assets/Shaders/Particle_VS.glsl @@ -19,7 +19,7 @@ struct GenericData struct ParticleData { vec4 position; - vec4 rotation; + vec4 orientationSpeedDecay; vec4 velocity; vec4 acceleration; vec4 scaleAndDecay; @@ -56,9 +56,14 @@ layout (std430, set = 2, binding = 4) coherent restrict buffer IndicesData layout(location = 0) out struct { vec2 uv; // location = 0 - } Out; +// material stuff +layout(location = 1) out struct +{ + uint textureIndex; // location = 1 +} OutFlat; + vec2 CreateQuad (in uint 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])); 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); } \ No newline at end of file diff --git a/Assets/Shaders/Particle_VS.shshaderb b/Assets/Shaders/Particle_VS.shshaderb index d79ee1d1..492a926c 100644 Binary files a/Assets/Shaders/Particle_VS.shshaderb and b/Assets/Shaders/Particle_VS.shshaderb differ diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 0e1f4fd4..a46d76c4 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -811,6 +811,8 @@ namespace SHADE if (!component) return; + auto gfxSystem = SHSystemManager::GetSystem(); + ImGui::PushID(SHFamilyID::GetID()); const auto componentType = rttr::type::get(*component); @@ -860,6 +862,47 @@ namespace SHADE 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(SHDragDrop::DRAG_RESOURCE)) + { + Handle texture = SHResourceManager::LoadOrGet(*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); }); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.cpp index b095f26b..2427ade9 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.cpp @@ -62,6 +62,21 @@ namespace SHADE 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 { cpuEmitterData.lifeAndSizeRange.z = size; @@ -113,6 +128,21 @@ namespace SHADE 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 { return cpuEmitterData.lifeAndSizeRange.z; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h index e345c24d..dea510f2 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h @@ -4,6 +4,7 @@ #include "Math/Vector/SHVec4.h" #include "ECS_Base/Components/SHComponent.h" #include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h" +#include namespace SHADE { @@ -25,7 +26,11 @@ namespace SHADE //! Maximum starting velocity 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) SHVec4 lifeAndSizeRange; @@ -104,6 +109,9 @@ namespace SHADE //! For all the dynamic SSBOs in the descriptor set std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS> dynamicOffsets{}; + //! For the emitter to use to give particles their texture + AssetID textureAssetID; + public: void OnCreate(void) override final; void OnDestroy(void) override final; @@ -118,6 +126,9 @@ namespace SHADE void SetAngularRangesAndOffsets (SHVec4 const& ranges) noexcept; void SetMinSpeed (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 SetMaxSize (float size) noexcept; @@ -130,6 +141,9 @@ namespace SHADE SHVec4 const& GetAngularRangesAndOffsets (void) const noexcept; float GetMinSpeed (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 GetMaxSize (void) const noexcept; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.cpp index 665f43aa..bd08b48f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.cpp @@ -200,9 +200,9 @@ namespace SHADE }; // ...copy assign barriers on heap - preUpdateBarriers[EMITTER_INDEX * 3] = inputParticleDataBarrierPreUpdate; - preUpdateBarriers[(EMITTER_INDEX * 3) + 1] = freelistDataBarrierPreUpdate; - preUpdateBarriers[(EMITTER_INDEX * 3) + 2] = indicesDataBarrierPreUpdate; + preUpdateBarriers.push_back(inputParticleDataBarrierPreUpdate); + preUpdateBarriers.push_back(freelistDataBarrierPreUpdate); + preUpdateBarriers.push_back(indicesDataBarrierPreUpdate); // make new barrier on stack... vk::BufferMemoryBarrier outputParticleDataBarrierPostUpdate @@ -235,9 +235,9 @@ namespace SHADE }; // ...copy assign barriers on heap - postUpdateBarriers[EMITTER_INDEX * 3] = outputParticleDataBarrierPostUpdate; - postUpdateBarriers[(EMITTER_INDEX * 3) + 1] = indicesDataBarrierPostUpdate; - postUpdateBarriers[(EMITTER_INDEX * 3) + 2] = drawDataBarrierPostUpdate; + postUpdateBarriers.push_back(outputParticleDataBarrierPostUpdate); + postUpdateBarriers.push_back(indicesDataBarrierPostUpdate); + postUpdateBarriers.push_back(drawDataBarrierPostUpdate); } void SHParticleSubSystem::Init(Handle device, Handle inDescPool, Handle compatibleRenderpass, Handle subpass, Handle VS, Handle FS, Handle emitCS, Handle 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 // it's mainly used in update and rendering so a barrier for it is NOT needed here. std::vector 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 // before we issue them for rendering. std::vector postUpdateBarriers{}; - postUpdateBarriers.resize(emitters.size() * 3); + postUpdateBarriers.reserve(emitters.size() * 3); std::vector 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 @@ -365,45 +365,51 @@ namespace SHADE for (auto& emitter : emitters) { - if (!emitter.initialized) - InitializeComponent(emitter); - - // Set emitter emit flag to true here if there are ready to be emitted - if (emitter.isPassive) + if (emitter.isActive) { - // decrement emission timer - emitter.timeBeforeEmission -= dt; + if (!emitter.initialized) + InitializeComponent(emitter); - // Check if time to emit - if (emitter.timeBeforeEmission <= 0.0f) + // Set emitter emit flag to true here if there are ready to be emitted + if (emitter.isPassive) { - // reset timer - emitter.timeBeforeEmission = emitter.emissionInterval; + // decrement emission timer + emitter.timeBeforeEmission -= dt; - // Emit later - emitter.toEmit = true; + // Check if time to emit + if (emitter.timeBeforeEmission <= 0.0f) + { + // reset timer + emitter.timeBeforeEmission = emitter.emissionInterval; + + // Emit later + emitter.toEmit = true; + } } + + if (emitter.toEmit) // take note that if emitter is not passive, this can also be set to true outside of this function + { + // Copy data to host visible buffer of emitter + emitter.emitterData->WriteToMemory (&emitter.cpuEmitterData, sizeof (SHParticleEmitterComponent::GPUEmitterStruct), 0, emitterDataOffset); + + // Call emit function here + EmitComponent(cmdBuffer, emitter, frameIndex); + } + + // prepare barriers + PreparePrePostUpdateBarriers(preUpdateBarriers, postUpdateBarriers, emitter, i, frameIndex); + + // Emitter will emit once and stop emitting next frame (unless specified before reaching here to do so). + emitter.toEmit = false; } - if (emitter.toEmit) // take note that if emitter is not passive, this can also be set to true outside of this function - { - // Copy data to host visible buffer of emitter - emitter.emitterData->WriteToMemory (&emitter.cpuEmitterData, sizeof (SHParticleEmitterComponent::GPUEmitterStruct), 0, emitterDataOffset); - - // Call emit function here - EmitComponent(cmdBuffer, emitter, frameIndex); - } - - // prepare barriers - PreparePrePostUpdateBarriers(preUpdateBarriers, postUpdateBarriers, emitter, i, frameIndex); - - // Emitter will emit once and stop emitting next frame (unless specified before reaching here to do so). - emitter.toEmit = false; ++i; } + // issue the barrier to wait - cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader | vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eDrawIndirect, vk::PipelineStageFlagBits::eComputeShader, {}, {}, preUpdateBarriers, {}); + if (!preUpdateBarriers.empty()) + cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader | vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eDrawIndirect, vk::PipelineStageFlagBits::eComputeShader, {}, {}, preUpdateBarriers, {}); @@ -417,14 +423,17 @@ namespace SHADE for (auto& emitter : emitters) { - UpdateCompoennt(cmdBuffer, emitter, frameIndex); + if (emitter.isActive) + UpdateCompoennt(cmdBuffer, emitter, frameIndex); } /*-----------------------------------------------------------------------*/ /* 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::eVertexShader | vk::PipelineStageFlagBits::eDrawIndirect | vk::PipelineStageFlagBits::eVertexShader, {}, {}, postUpdateBarriers, {}); + if (!postUpdateBarriers.empty()) + cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eDrawIndirect | vk::PipelineStageFlagBits::eVertexShader, {}, {}, postUpdateBarriers, {}); } @@ -457,10 +466,13 @@ namespace SHADE // 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]); + if (emitter.isActive) + { + // 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); + RenderComponent(cmdBuffer, emitter, frameIndex); + } } } diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index b5f0ffdf..daf3d4ca 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -514,6 +514,8 @@ namespace YAML 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 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) { @@ -528,11 +530,15 @@ namespace YAML node[MIN_SIZE_TAG.data()] = rhs.GetMinSize(); node[MAX_SIZE_TAG.data()] = rhs.GetMaxSize(); 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; } static bool decode(YAML::Node const& node, SHParticleEmitterComponent& rhs) { + auto gfxSystem = SHSystemManager::GetSystem(); + if (node[EMISSION_COUNT_TAG.data()].IsDefined()) rhs.SetEmissionCount(node[EMISSION_COUNT_TAG.data()].as()); @@ -560,6 +566,20 @@ namespace YAML if (node[MAX_SIZE_TAG.data()].IsDefined()) rhs.SetMaxSize(node[MAX_SIZE_TAG.data()].as()); + if (node[ROTATION_SPEED_TAG.data()].IsDefined()) + rhs.SetRotationSpeed(node[ROTATION_SPEED_TAG.data()].as()); + + if (node[TEXTURE_ASSET_ID_TAG.data()].IsDefined()) + { + AssetID id = node[TEXTURE_ASSET_ID_TAG.data()].as(); + + Handle texture = SHResourceManager::LoadOrGet(id); + gfxSystem->BuildTextures(); + + rhs.SetTextureIndex(texture->TextureArrayIndex); + rhs.SetTextureAssetID(id); + } + return true; } };