diff --git a/Assets/Scenes/Scene2.shade b/Assets/Scenes/Scene2.shade index c45d8b51..b4d7abbd 100644 --- a/Assets/Scenes/Scene2.shade +++ b/Assets/Scenes/Scene2.shade @@ -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 @@ -145,4 +145,32 @@ End Alpha: 1 "Color Eval Rate ": 0.192000002 IsActive: true + Scripts: ~ +- EID: 7 + Name: ParticleTest + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 0, y: 0.823412895, z: -4.31447983} + Rotate: {x: -0, y: 0, z: -0} + Scale: {x: 1, y: 1, z: 1} + IsActive: true + classSHADE::SHParticleEmitterComponent: + Emission Count: 15 + Is Passive: true + Emission Interval: 0.0939999968 + Min Life: 2 + Max Life: 3 + Minimum Speed: 3 + Maximum Speed: 6 + Minimum Size: 0 + Maximum Size: 0.5 + Size Decay: 0.990999997 + Angular Ranges And Offset: {x: 6.19999981, y: 1.10000002, z: 0, w: 0.100000001} + Rotation Speed: 0.0309999995 + Rotation Decay: 0.0199999996 + Texture Asset ID: 63456868 + Custom Update Shader Asset ID: 0 + IsActive: true Scripts: ~ \ No newline at end of file diff --git a/Assets/Shaders/ParticleEmit_CS.glsl b/Assets/Shaders/ParticleEmit_CS.glsl index 421e2ce1..45cea313 100644 --- a/Assets/Shaders/ParticleEmit_CS.glsl +++ b/Assets/Shaders/ParticleEmit_CS.glsl @@ -1,24 +1,32 @@ #version 450 +#define PI 3.14159265f + layout(local_size_x = 128) in; struct EmitterParameters { - vec4 angularMin; - vec4 angularMax; + vec4 angularRangesAndOffsets; + float minSpeed; + float maxSpeed; + float rotationSpeed; + float rotationDecay; vec4 lifeAndSizeRange; // min life, max life, min size, max size -} + float sizeDecay; + uint textureIndex; + float padding[2]; +}; struct ParticleData { vec4 position; - vec4 rotation; + vec4 orientationSpeedDecay; vec4 velocity; vec4 acceleration; vec4 scaleAndDecay; float life; uint textureIndex; -} +}; struct GenericData { @@ -96,22 +104,86 @@ float rand(inout uint state) return float(x)*uintBitsToFloat(0x2f800004u); } +float map(float value, float inMin, float inMax, float outMin, float outMax) +{ + return outMin + (outMax - outMin) * (value - inMin) / (inMax - inMin); +} + void main() { uint emitterInvocationIndex = gl_GlobalInvocationID.x; + vec4 emitterPosition = emitterPushConstant.emitterPosition; + vec4 angularRangesAndOffsets = emitterParams.data.angularRangesAndOffsets; + float minSpeed = emitterParams.data.minSpeed; + float maxSpeed = emitterParams.data.maxSpeed; 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); ParticleData particle; - int index = freelist.freeIndices[freelistIndex]; - particle.position = emitterPosition; - particle.life = emitterParams.10.0f; + // Get seed for randomization + uint pixel_index = uint (emitterPosition.x + emitterPosition.y + floatBitsToUint(genericDataBuffer.data.elapsedTime) * (gl_GlobalInvocationID.x + 1)); + uint seed = pcg_hash (pixel_index); - particles[index] = particle; + int index = freelist.freeIndices[freelistIndex]; + + // emit particle from emitter position + particle.position = vec4 (emitterPosition.xyz, 1.0f); + + vec2 eulerAngles = vec2 (rand(seed) * angularRangesAndOffsets.x + angularRangesAndOffsets.z, + rand(seed) * angularRangesAndOffsets.y + angularRangesAndOffsets.w); + + // Set its velocity + // particle.velocity.xyz = vec3 (cos(eulerAngles.x) * cos(eulerAngles.y), + // sin(eulerAngles.x) * cos(eulerAngles.y), + // sin(eulerAngles.y)); + + float bank = eulerAngles.y; + float cb = cos(bank); + float sb = sin(bank); + float ch = cos (eulerAngles.x); + float sh = sin (eulerAngles.x); + float cp = cos (0.0f); + float sp = sin (0.0f); + + particle.velocity.xyz = mat3 ( + (ch * cb + sh * sp * sb), (sb * cp), (-sh * cb + ch * sp * sb), + (-ch * sb + sh * sp * cb), (cb * cp), ( sb * sh + ch * sp * cb), + (sh * cp), (-sp), (ch * cp) + ) * vec3 (1.0f, 0.0f, 0.0f); + + + particle.velocity *= map (rand (seed), 0.0f, 1.0f, minSpeed.x, maxSpeed.x); + + // 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); + + float particleSize = map (rand(seed), 0.0f, 1.0f, emitterParams.data.lifeAndSizeRange.z, emitterParams.data.lifeAndSizeRange.w); + + // Set size of particle + particle.scaleAndDecay.x = particleSize; + particle.scaleAndDecay.y = particleSize; + particle.scaleAndDecay.z = emitterParams.data.sizeDecay; + particle.scaleAndDecay.w = emitterParams.data.sizeDecay; + + // 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, emitterParams.data.rotationDecay, 0.0f); + else + particle.orientationSpeedDecay = vec4 (0.0f); + + + particle.acceleration = vec4 (0.0f, -0.058f, 0.0f, 0.0f); + + + inputParticles.data[index] = particle; } \ No newline at end of file diff --git a/Assets/Shaders/ParticleEmit_CS.shshaderb b/Assets/Shaders/ParticleEmit_CS.shshaderb new file mode 100644 index 00000000..b003ba77 Binary files /dev/null and b/Assets/Shaders/ParticleEmit_CS.shshaderb differ diff --git a/Assets/Shaders/ParticleEmit_CS.shshaderb.shmeta b/Assets/Shaders/ParticleEmit_CS.shshaderb.shmeta new file mode 100644 index 00000000..f7c25e30 --- /dev/null +++ b/Assets/Shaders/ParticleEmit_CS.shshaderb.shmeta @@ -0,0 +1,3 @@ +Name: ParticleEmit_CS +ID: 49959611 +Type: 2 diff --git a/Assets/Shaders/ParticleUpdateRandomAcc_CS.glsl b/Assets/Shaders/ParticleUpdateRandomAcc_CS.glsl new file mode 100644 index 00000000..bf500255 --- /dev/null +++ b/Assets/Shaders/ParticleUpdateRandomAcc_CS.glsl @@ -0,0 +1,146 @@ +#version 450 + +layout(local_size_x = 128) in; + +struct DrawArraysIndirectArgs +{ + uint count; + uint instanceCount; + uint first; + uint baseInstance; +}; + +struct ParticleData +{ + vec4 position; + vec4 orientationSpeedDecay; + vec4 velocity; + vec4 acceleration; + vec4 scaleAndDecay; + float life; + uint textureIndex; +}; + +struct GenericData +{ + //! Delta time + float dt; + + //! Elapsed time of the application + float elapsedTime; + + //! Viewport width of the scene (excluding imgui, that means smaller than window) + uint viewportWidth; + + //! Ditto but for height + uint viewportHeight; +}; + +layout(set = 1, binding = 0) uniform CameraData +{ + vec4 position; + mat4 vpMat; + mat4 viewMat; + mat4 projMat; +} cameraData; + + +layout (set = 0, binding = 0) uniform GenericDataBuffer +{ + GenericData data; +} genericDataBuffer; + +layout (std430, set = 2, binding = 1) coherent restrict readonly buffer ParticlesInputBuffer +{ + ParticleData data[]; +} inputParticles; + +// output buffer not needed +layout (std430, set = 2, binding = 2) coherent restrict buffer ParticlesOutputBuffer +{ + ParticleData data[]; +} outputParticles; + +layout (std430, set = 2, binding = 3) coherent restrict buffer ParticlesFreelistBuffer +{ + int freeCount; + int freeIndices[]; + +} freelist; + +layout (std430, set = 2, binding = 4) coherent restrict buffer IndicesData +{ + uint indices[]; +}; + +layout (std140, set = 2, binding = 5) coherent restrict buffer IndirectDrawArgs +{ + DrawArraysIndirectArgs indirectArgs; +}; + +// push constants +layout(std140, push_constant) uniform EmitterPushConstant +{ + vec4 emitterPosition; + uint emissionCount; + +} emitterPushConstant; + +uint pcg_hash(uint seed) +{ + uint state = seed * 747796405u + 2891336453u; + uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; + return (word >> 22u) ^ word; +} + +// Used to advance the PCG state. +uint rand_pcg(inout uint rng_state) +{ + uint state = rng_state; + rng_state = rng_state * 747796405u + 2891336453u; + uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; + return (word >> 22u) ^ word; +} + +// Advances the prng state and returns the corresponding random float. +float rand(inout uint state) +{ + uint x = rand_pcg(state); + state = x; + return float(x)*uintBitsToFloat(0x2f800004u); +} + +void main() +{ + uint index = gl_GlobalInvocationID.x; + + ParticleData particle = inputParticles.data[index]; + + // Get seed for randomization + uint pixel_index = uint (particle.position.x + particle.position.y + floatBitsToUint(genericDataBuffer.data.elapsedTime) * (gl_GlobalInvocationID.x + 1)); + uint seed = pcg_hash (pixel_index); + + if (particle.life > 0.0f) + { + // update position from velocity + particle.position += particle.velocity * genericDataBuffer.data.dt; + particle.velocity += vec4 (rand(seed) * particle.acceleration.x, rand(seed) * particle.acceleration.y, rand(seed) * particle.acceleration.z, 1.0f); + particle.life -= genericDataBuffer.data.dt; + + if (particle.life < 0.0f || particle.scaleAndDecay.x < 0.0f || particle.scaleAndDecay.y < 0.0f) + { + particle.life = 0.0f; + particle.position.x = 9999.0f; + + outputParticles.data[index] = particle; + freelist.freeIndices[atomicAdd(freelist.freeCount, 1)] = int (index); + return; + } + + uint drawIndex = atomicAdd (indirectArgs.instanceCount, 1); + indices[drawIndex] = index; + + } + + outputParticles.data[index] = particle; +} \ No newline at end of file diff --git a/Assets/Shaders/ParticleUpdateRandomAcc_CS.shshaderb b/Assets/Shaders/ParticleUpdateRandomAcc_CS.shshaderb new file mode 100644 index 00000000..370f9965 Binary files /dev/null and b/Assets/Shaders/ParticleUpdateRandomAcc_CS.shshaderb differ diff --git a/Assets/Shaders/ParticleUpdateRandomAcc_CS.shshaderb.shmeta b/Assets/Shaders/ParticleUpdateRandomAcc_CS.shshaderb.shmeta new file mode 100644 index 00000000..ede00748 --- /dev/null +++ b/Assets/Shaders/ParticleUpdateRandomAcc_CS.shshaderb.shmeta @@ -0,0 +1,3 @@ +Name: ParticleUpdateRandomAcc_CS +ID: 35838633 +Type: 2 diff --git a/Assets/Shaders/ParticleUpdate_CS.glsl b/Assets/Shaders/ParticleUpdate_CS.glsl index 7649ee63..43d4893d 100644 --- a/Assets/Shaders/ParticleUpdate_CS.glsl +++ b/Assets/Shaders/ParticleUpdate_CS.glsl @@ -13,13 +13,13 @@ struct DrawArraysIndirectArgs struct ParticleData { vec4 position; - vec4 rotation; + vec4 orientationSpeedDecay; vec4 velocity; vec4 acceleration; vec4 scaleAndDecay; float life; uint textureIndex; -} +}; struct GenericData { @@ -73,10 +73,10 @@ layout (std430, set = 2, binding = 4) coherent restrict buffer IndicesData uint indices[]; }; -layout (std140, set = 2, binding = 5) coherent restrict uniform IndirectDrawArgs +layout (std140, set = 2, binding = 5) coherent restrict buffer IndirectDrawArgs { DrawArraysIndirectArgs indirectArgs; -}; +}; // push constants layout(std140, push_constant) uniform EmitterPushConstant @@ -116,26 +116,37 @@ void main() ParticleData particle = inputParticles.data[index]; - if (particle.lifetime > 0.0f) + if (particle.life > 0.0f) { - // particle.position += particle.velocity * dt; + // update position from velocity + particle.position += particle.velocity * genericDataBuffer.data.dt; + particle.velocity += particle.acceleration; + particle.life -= genericDataBuffer.data.dt; + particle.orientationSpeedDecay.x += particle.orientationSpeedDecay.y; + particle.scaleAndDecay.x *= particle.scaleAndDecay.z; + particle.scaleAndDecay.y *= particle.scaleAndDecay.w; - // particle.lifetime -= dt; - // particle.size -= 1.2f * dt; - // particle.color += 1.0f * dt; - - if (particle.lifetime < 0.0f || particle.size < 0.0f) + if (particle.orientationSpeedDecay.y > 0.0f) { - particle.lifetime = 0.0f; - particle.position.x = 99999.0f; + particle.orientationSpeedDecay.y -= particle.orientationSpeedDecay.z * genericDataBuffer.data.dt; + if (particle.orientationSpeedDecay.y < 0.0f) + particle.orientationSpeedDecay.y = 0.0f; + } + + if (particle.life < 0.0f || particle.scaleAndDecay.x < 0.0f || particle.scaleAndDecay.y < 0.0f) + { + particle.life = 0.0f; + particle.position.x = 9999.0f; outputParticles.data[index] = particle; freelist.freeIndices[atomicAdd(freelist.freeCount, 1)] = int (index); return; } - outputParticles.data[index] = particle; uint drawIndex = atomicAdd (indirectArgs.instanceCount, 1); indices[drawIndex] = index; + } + + outputParticles.data[index] = particle; } \ No newline at end of file diff --git a/Assets/Shaders/ParticleUpdate_CS.shshaderb b/Assets/Shaders/ParticleUpdate_CS.shshaderb new file mode 100644 index 00000000..63a79d5a Binary files /dev/null and b/Assets/Shaders/ParticleUpdate_CS.shshaderb differ diff --git a/Assets/Shaders/ParticleUpdate_CS.shshaderb.shmeta b/Assets/Shaders/ParticleUpdate_CS.shshaderb.shmeta new file mode 100644 index 00000000..cf9a5051 --- /dev/null +++ b/Assets/Shaders/ParticleUpdate_CS.shshaderb.shmeta @@ -0,0 +1,3 @@ +Name: ParticleUpdate_CS +ID: 36260925 +Type: 2 diff --git a/Assets/Shaders/Particle_FS.glsl b/Assets/Shaders/Particle_FS.glsl new file mode 100644 index 00000000..243baa2e --- /dev/null +++ b/Assets/Shaders/Particle_FS.glsl @@ -0,0 +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 (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 new file mode 100644 index 00000000..edd2dd6b Binary files /dev/null and b/Assets/Shaders/Particle_FS.shshaderb differ diff --git a/Assets/Shaders/Particle_FS.shshaderb.shmeta b/Assets/Shaders/Particle_FS.shshaderb.shmeta new file mode 100644 index 00000000..afe2e1e6 --- /dev/null +++ b/Assets/Shaders/Particle_FS.shshaderb.shmeta @@ -0,0 +1,3 @@ +Name: Particle_FS +ID: 42509714 +Type: 2 diff --git a/Assets/Shaders/Particle_VS.glsl b/Assets/Shaders/Particle_VS.glsl new file mode 100644 index 00000000..5004cea1 --- /dev/null +++ b/Assets/Shaders/Particle_VS.glsl @@ -0,0 +1,101 @@ +#version 460 core + +struct GenericData +{ + //! Delta time + float dt; + + //! Elapsed time of the application + float elapsedTime; + + //! Viewport width of the scene (excluding imgui, that means smaller than window) + uint viewportWidth; + + //! Ditto but for height + uint viewportHeight; +}; + + +struct ParticleData +{ + vec4 position; + vec4 orientationSpeedDecay; + vec4 velocity; + vec4 acceleration; + vec4 scaleAndDecay; + float life; + uint textureIndex; +}; + +layout (set = 0, binding = 0) uniform GenericDataBuffer +{ + GenericData data; +} genericDataBuffer; + + +layout(set = 1, binding = 0) uniform CameraData +{ + vec4 position; + mat4 vpMat; + mat4 viewMat; + mat4 projMat; +} cameraData; + +// output buffer not needed +layout (std430, set = 2, binding = 2) coherent restrict buffer ParticlesOutputBuffer +{ + ParticleData data[]; +} outputParticles; + +layout (std430, set = 2, binding = 4) coherent restrict buffer IndicesData +{ + uint indices[]; +}; + +// between shader stages +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; + return vec2 ((0x3 & b) != 0, (0x9 & b) != 0); +} + +void main() +{ + // Create a quad and its texture coordinates + Out.uv = CreateQuad (gl_VertexIndex); + vec3 vertexPos = vec3 (Out.uv - vec2(0.5f), 0.0f); + + ParticleData particle = outputParticles.data[indices[gl_InstanceIndex]]; + + vec3 normalized = normalize (particle.velocity.xyz); + float angle = particle.orientationSpeedDecay.x; + // float angle = atan (normalized.y, normalized.x); + vec2 particleScaleData = particle.scaleAndDecay.xy; // x and y + + mat3 rotate = mat3 (1.0f); + rotate[0][0] = cos(angle); + rotate[0][1] = sin(angle); + rotate[1][0] = -sin(angle); + rotate[1][1] = cos(angle); + + vec3 particlePos = rotate * vertexPos; + + vec3 viewRight = normalize (vec3 (cameraData.viewMat[0][0], cameraData.viewMat[1][0], cameraData.viewMat[2][0])); + 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 new file mode 100644 index 00000000..0a9be96d Binary files /dev/null and b/Assets/Shaders/Particle_VS.shshaderb differ diff --git a/Assets/Shaders/Particle_VS.shshaderb.shmeta b/Assets/Shaders/Particle_VS.shshaderb.shmeta new file mode 100644 index 00000000..8b44df47 --- /dev/null +++ b/Assets/Shaders/Particle_VS.shshaderb.shmeta @@ -0,0 +1,3 @@ +Name: Particle_VS +ID: 35035037 +Type: 2 diff --git a/Assets/Shaders/SSAO_CS.glsl b/Assets/Shaders/SSAO_CS.glsl index 42ab220c..7e818141 100644 --- a/Assets/Shaders/SSAO_CS.glsl +++ b/Assets/Shaders/SSAO_CS.glsl @@ -8,7 +8,7 @@ const int ROTATION_KERNEL_W = 4; const int ROTATION_KERNEL_H = 4; // can perhaps pass in as push constant. -const float RADIUS = 0.2f; +const float RADIUS = 0.35f; const float BIAS = 0.0025f; layout(local_size_x = 16, local_size_y = 16) in; diff --git a/Assets/Shaders/SSAO_CS.shshaderb b/Assets/Shaders/SSAO_CS.shshaderb index 36e627d6..06438d58 100644 Binary files a/Assets/Shaders/SSAO_CS.shshaderb and b/Assets/Shaders/SSAO_CS.shshaderb differ diff --git a/Assets/Shaders/ShinyHighlight_VS.shshaderb b/Assets/Shaders/ShinyHighlight_VS.shshaderb index 95eac304..3b7e8d72 100644 Binary files a/Assets/Shaders/ShinyHighlight_VS.shshaderb and b/Assets/Shaders/ShinyHighlight_VS.shshaderb differ diff --git a/SHADE_Engine/src/Common/SHAllComponents.h b/SHADE_Engine/src/Common/SHAllComponents.h index ee900736..32efee1f 100644 --- a/SHADE_Engine/src/Common/SHAllComponents.h +++ b/SHADE_Engine/src/Common/SHAllComponents.h @@ -15,3 +15,4 @@ #include "Graphics/MiddleEnd/TextRendering/SHTextRenderableComponent.h" #include "AudioSystem/SHAudioListenerComponent.h" #include "Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h" +#include "Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h" \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 03a37a69..2edadbbc 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -30,6 +30,7 @@ #include "../SHEditorWindowManager.h" #include "../AssetBrowser/SHAssetBrowser.h" #include "Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h" +#include "Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h" #include "Animation/SHAnimationClip.h" namespace SHADE @@ -804,4 +805,166 @@ namespace SHADE ImGui::PopID(); } + template<> + static void DrawComponent(SHParticleEmitterComponent* component) + { + if (!component) + return; + + auto gfxSystem = SHSystemManager::GetSystem(); + + ImGui::PushID(SHFamilyID::GetID()); + + const auto componentType = rttr::type::get(*component); + + SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }, "Is Component Active"); + + ImGui::SameLine(); + if (ImGui::CollapsingHeader(componentType.get_name().data(), ImGuiTreeNodeFlags_DefaultOpen)) + { + DrawContextMenu(component); + + SHEditorWidgets::DragInt("Emission Count", [comp = component]() {return comp->GetEmissionCount(); }, [comp = component](uint32_t count) {comp->SetEmissionCount(count);}); + 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("Max Size", [comp = component]() {return comp->GetMaxSize(); }, [comp = component](float val) {comp->SetMaxSize(val); }); + SHEditorWidgets::DragFloat("Size Decay", [comp = component]() {return comp->GetSizeDecayMult(); }, [comp = component](float val) {comp->SetSizeDecayMult(val); }, {}, 0.0001f); + + SHEditorWidgets::DragVec4("Angles and Offsets", {"yaw", "bank", "yaw off", "bank off"}, + [comp = component]() + { + return comp->GetAngularRangesAndOffsets(); + }, + [comp = component](SHVec4 const& val) + { + comp->SetAngularRangesAndOffsets(val); + }); + + + SHEditorWidgets::DragFloat("Min Speed", + [comp = component]() + { + return comp->GetMinSpeed(); + }, + [comp = component](float val) + { + comp->SetMinSpeed(val); + }); + + SHEditorWidgets::DragFloat("Max Speed", + [comp = component]() + { + return comp->GetMaxSpeed(); + }, + [comp = component](float val) + { + comp->SetMaxSpeed(val); + }); + + SHEditorWidgets::DragFloat("Rotation Speed", + [comp = component]() + { + return comp->GetRotationSpeed(); + }, + [comp = component](float val) + { + comp->SetRotationSpeed(val); + }); + + SHEditorWidgets::DragFloat("Rotation Decay", + [comp = component]() + { + return comp->GetRotationDecay(); + }, + [comp = component](float val) + { + comp->SetRotationDecay(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::InputText("Custom Update Shader", + [comp = component]() + { + auto customShader = comp->GetCustomUpdateShader(); + + if (customShader) + return customShader->GetName(); + else + return std::string{}; + + }, + [comp = component](std::string const& text) + { + }, {}, ImGuiSliderFlags_ReadOnly); + + if (SHDragDrop::BeginTarget()) + { + if (AssetID* payload = SHDragDrop::AcceptPayload(SHDragDrop::DRAG_RESOURCE)) + { + Handle shaderModule = SHResourceManager::LoadOrGet(*payload); + + if (shaderModule) + { + component->SetCustomUpdateShader(shaderModule); + component->SetCustomUpdateShaderAssetID(*payload); + } + else + { + SHLOG_WARNING("[] Attempted to load invalid shader! Custom update shader for particles not set. "); + } + + SHDragDrop::EndTarget(); + } + } + ImGui::SameLine(); + if (ImGui::Button("Reset")) + { + component->SetCustomUpdateShader({}); + component->SetCustomUpdateShaderAssetID(INVALID_ASSET_ID); + + } + + + + + SHEditorWidgets::CheckBox("Is Passive", [comp = component]() {return comp->GetPassive(); }, [comp = component](bool flag) {comp->SetPassive(flag); }); + + } + else + { + DrawContextMenu(component); + } + ImGui::PopID(); + } + } diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp index f331f499..e9da2f09 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp @@ -26,6 +26,7 @@ #include "SHEditorComponentView.h" #include "AudioSystem/SHAudioListenerComponent.h" #include "Graphics/MiddleEnd/TextRendering/SHTextRenderableComponent.h" +#include "Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h" #include "Camera/SHCameraSystem.h" #include "FRC/SHFramerateController.h" @@ -188,6 +189,10 @@ namespace SHADE { DrawComponent(trajectoryComponent); } + if (auto particleComponent = SHComponentManager::GetComponent_s(eid)) + { + DrawComponent(particleComponent); + } ImGui::Separator(); // Render Scripts SHScriptEngine* scriptEngine = static_cast(SHSystemManager::GetSystem()); @@ -204,6 +209,7 @@ namespace SHADE DrawAddComponentButton(eid); DrawAddComponentButton(eid); DrawAddComponentButton(eid); + DrawAddComponentButton(eid); // Components that require Transforms @@ -213,6 +219,7 @@ namespace SHADE DrawAddComponentWithEnforcedComponentButton(eid); DrawAddComponentWithEnforcedComponentButton(eid); DrawAddComponentWithEnforcedComponentButton(eid); + //DrawAddComponentWithEnforcedComponentButton(eid); ImGui::EndMenu(); } diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp index 37b89883..2ba35ad7 100644 --- a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp +++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp @@ -450,7 +450,7 @@ namespace SHADE */ /***************************************************************************/ - void SHVkCommandBuffer::DrawMultiIndirect(Handle indirectDrawData, uint32_t drawCount) + void SHVkCommandBuffer::DrawMultiIndirectIndexed(Handle indirectDrawData, uint32_t drawCount) { if (cmdBufferState != SH_CMD_BUFFER_STATE::RECORDING) { @@ -462,6 +462,19 @@ namespace SHADE vkCommandBuffer.drawIndexedIndirect(indirectDrawData->GetVkBuffer(), 0, drawCount, sizeof(vk::DrawIndexedIndirectCommand)); } + void SHVkCommandBuffer::DrawMultiIndirect(Handle indirectDrawData, uint32_t drawCount, uint32_t offset/* = 0*/) + { + if (cmdBufferState != SH_CMD_BUFFER_STATE::RECORDING) + { + SHLOG_ERROR("Command buffer must have started recording before a pipeline can be bound."); + return; + } + + if (indirectDrawData) + vkCommandBuffer.drawIndirect(indirectDrawData->GetVkBuffer(), offset, drawCount, sizeof(vk::DrawIndirectCommand)); + + } + void SHVkCommandBuffer::ComputeDispatch(uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) noexcept { vkCommandBuffer.dispatch (groupCountX, groupCountY, groupCountZ); diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h index c6a17d2a..510dcc3b 100644 --- a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h +++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h @@ -128,9 +128,10 @@ namespace SHADE void BindDescriptorSet (Handle descSetGroup, SH_PIPELINE_TYPE bindPoint, uint32_t firstSet, std::span const dynamicOffsets); // Draw Commands - void DrawArrays (uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) const noexcept; - void DrawIndexed (uint32_t indexCount, uint32_t firstIndex, uint32_t vertexOffset) const noexcept; - void DrawMultiIndirect (Handle indirectDrawData, uint32_t drawCount); + void DrawArrays (uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) const noexcept; + void DrawIndexed (uint32_t indexCount, uint32_t firstIndex, uint32_t vertexOffset) const noexcept; + void DrawMultiIndirectIndexed (Handle indirectDrawData, uint32_t drawCount); + void DrawMultiIndirect (Handle indirectDrawData, uint32_t drawCount, uint32_t offset = 0); // Compute Commands void ComputeDispatch (uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) noexcept; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 4aa33de5..e2a6ec66 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -755,7 +755,7 @@ namespace SHADE } } - cmdBuffer->DrawMultiIndirect(drawDataBuffer[frameIndex], static_cast(drawData.size())); + cmdBuffer->DrawMultiIndirectIndexed(drawDataBuffer[frameIndex], static_cast(drawData.size())); cmdBuffer->EndLabeledSegment(); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp index 76d34ff7..2c51e5eb 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp @@ -251,7 +251,7 @@ namespace SHADE // particle draw call binding SHVkDescriptorSetLayout::Binding particleDrawDataBinding { - .Type = vk::DescriptorType::eUniformBufferDynamic, // UBO (Because lesser data), dynamic (1 set for each frame) + .Type = vk::DescriptorType::eStorageBufferDynamic, // SSBO, dynamic (1 set for each frame) .Stage = vk::ShaderStageFlagBits::eCompute, .BindPoint = SHGraphicsConstants::DescriptorSetBindings::PARTICLE_DRAW_DATA, .DescriptorCount = 1, diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index d9a740ac..1fb9babe 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -730,7 +730,7 @@ namespace SHADE cmdBuffer->BindVertexBuffer(COLOR_BIND_PT, batch.InstanceColorBuffer[frameIndex], 0); // Execute draw - cmdBuffer->DrawMultiIndirect(batch.MDIBuffer[frameIndex], static_cast(batch.MDIData.size())); + cmdBuffer->DrawMultiIndirectIndexed(batch.MDIBuffer[frameIndex], static_cast(batch.MDIData.size())); } void SHDebugDrawSystem::destroyBatch(MeshBatch& batch) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 69aa4188..c1938f60 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -48,6 +48,7 @@ of DigiPen Institute of Technology is prohibited. #include "Graphics/MiddleEnd/Lights/SHLightComponent.h" #include "Input/SHInputManager.h" #include "Assets/Events/SHAssetManagerEvents.h" +#include "Graphics/MiddleEnd/Particles/SHParticleSubSystem.h" namespace SHADE { @@ -144,6 +145,10 @@ namespace SHADE //SHAssetManager::CompileAsset("../../Assets/Shaders/Trajectory_FS.glsl", false); //SHAssetManager::CompileAsset("../../Assets/Shaders/ShadowMapBlur_CS.glsl", false); //SHAssetManager::CompileAsset("../../Assets/Shaders/Anim_VS.glsl", false); + //SHAssetManager::CompileAsset("../../Assets/Shaders/Particle_VS.glsl", false); + //SHAssetManager::CompileAsset("../../Assets/Shaders/Particle_FS.glsl", false); + //SHAssetManager::CompileAsset("../../Assets/Shaders/ParticleEmit_CS.glsl", false); + //SHAssetManager::CompileAsset("../../Assets/Shaders/ParticleUpdate_CS.glsl", false); // Load Built In Shaders static constexpr AssetID VS_DEFAULT = 39210065; defaultVertShader = SHResourceManager::LoadOrGet(VS_DEFAULT); @@ -165,6 +170,10 @@ namespace SHADE static constexpr AssetID TRAJECTORY_VS = 41042628; trajectoryVS = SHResourceManager::LoadOrGet(TRAJECTORY_VS); static constexpr AssetID TRAJECTORY_FS = 45635685; trajectoryFS = SHResourceManager::LoadOrGet(TRAJECTORY_FS); static constexpr AssetID SHADOW_BLUR_CS = 38004013; shadowMapBlurCS = SHResourceManager::LoadOrGet(SHADOW_BLUR_CS); + static constexpr AssetID PARTICLE_VS = 35035037; particleVS = SHResourceManager::LoadOrGet(PARTICLE_VS); + static constexpr AssetID PARTICLE_FS = 42509714; particleFS = SHResourceManager::LoadOrGet(PARTICLE_FS); + static constexpr AssetID PARTICLE_EMIT_CS = 49959611; particleEmitCS = SHResourceManager::LoadOrGet(PARTICLE_EMIT_CS); + static constexpr AssetID PARTICLE_UPDATE_CS = 36260925; particleUpdateCS = SHResourceManager::LoadOrGet(PARTICLE_UPDATE_CS); } @@ -343,11 +352,16 @@ namespace SHADE /*-----------------------------------------------------------------------*/ auto vfxPass = renderGraph->AddNode(SHGraphicsConstants::RenderGraphEntityNames::VFX_PASS.data(), { "Scene", "Depth Buffer" }, { SHGraphicsConstants::RenderGraphEntityNames::GBUFFER_PASS.data(), SHGraphicsConstants::RenderGraphEntityNames::DEFERRED_COMPOSITE_PASS.data() }); auto vfxSubpass = vfxPass->AddSubpass(SHGraphicsConstants::RenderGraphEntityNames::VFX_SUBPASS.data(), worldViewport, worldRenderer); + //vfxPass->AddPreBeginFunction([=](Handle cmdBuffer, uint32_t frameIndex) + // { + // particleSubSystem->Run(cmdBuffer, frameIndex); + // }); vfxSubpass->AddColorOutput("Scene"); vfxSubpass->AddDepthOutput("Depth Buffer"); vfxSubpass->AddExteriorDrawCalls([=](Handle cmdBuffer, Handle renderer, uint32_t frameIndex) { trajectoryRenderingSubSystem->Render(cmdBuffer, renderer, frameIndex); + particleSubSystem->Render(cmdBuffer, renderer, frameIndex); }); /*-----------------------------------------------------------------------*/ @@ -490,6 +504,12 @@ namespace SHADE trajectoryRenderingSubSystem = resourceManager.Create(); + auto vfxPass = renderGraph->GetNode(SHGraphicsConstants::RenderGraphEntityNames::VFX_PASS.data()); + + // particle sub system initialization + particleSubSystem = resourceManager.Create(); + particleSubSystem->Init(device, descPool, vfxPass->GetRenderpass(), vfxPass->GetSubpass(SHGraphicsConstants::RenderGraphEntityNames::VFX_SUBPASS), particleVS, particleFS, particleEmitCS, particleUpdateCS); + auto vfxNode = renderGraph->GetNode(SHGraphicsConstants::RenderGraphEntityNames::VFX_PASS.data()); trajectoryRenderingSubSystem->Init(device, vfxNode->GetRenderpass(), vfxNode->GetSubpass(SHGraphicsConstants::RenderGraphEntityNames::VFX_SUBPASS), trajectoryVS, trajectoryFS); @@ -669,7 +689,6 @@ namespace SHADE textRenderingSubSystem->Run(frameIndex); trajectoryRenderingSubSystem->Run(frameIndex); - for (auto renderer : renderers) { #ifdef SHEDITOR @@ -688,9 +707,12 @@ namespace SHADE #endif } + renderGraph->Begin(frameIndex); auto cmdBuffer = renderGraph->GetCommandBuffer(frameIndex); + particleSubSystem->ResetInstanceCounts(cmdBuffer, frameIndex); + particleSubSystem->Run(cmdBuffer, frameIndex); renderGraph->Execute(frameIndex, descPool, MESH_DATA); renderGraph->End(frameIndex); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index e674efb6..2627415c 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -59,6 +59,7 @@ namespace SHADE class SHMaterialInstance; class SHMousePickSystem; class SHTextRenderingSubSystem; + class SHParticleSubSystem; /*---------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -484,6 +485,10 @@ namespace SHADE Handle trajectoryVS; Handle trajectoryFS; Handle shadowMapBlurCS; + Handle particleVS; + Handle particleFS; + Handle particleEmitCS; + Handle particleUpdateCS; // Fonts Handle testFont; @@ -523,6 +528,7 @@ namespace SHADE Handle lightingSubSystem; Handle textRenderingSubSystem; Handle trajectoryRenderingSubSystem; + Handle particleSubSystem; Handle ssaoStorage; uint32_t resizeWidth = 1; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.cpp index 12690ece..5454cf31 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.cpp @@ -20,4 +20,178 @@ namespace SHADE toEmit = true; } + void SHParticleEmitterComponent::SetEmissionCount(uint32_t count) noexcept + { + emissionCount = count; + } + + void SHParticleEmitterComponent::SetPassive(bool flag) noexcept + { + isPassive = flag; + } + + void SHParticleEmitterComponent::SetEmissionInterval(float interval) noexcept + { + emissionInterval = interval; + timeBeforeEmission = emissionInterval; + } + + void SHParticleEmitterComponent::SetMinLife(float val) noexcept + { + cpuEmitterData.lifeAndSizeRange.x = val; + } + + void SHParticleEmitterComponent::SetMaxLife(float val) noexcept + { + cpuEmitterData.lifeAndSizeRange.y = val; + } + + void SHParticleEmitterComponent::SetAngularRangesAndOffsets(SHVec4 const& ranges) noexcept + { + cpuEmitterData.angularRangesAndOffsets = SHVec4 (ranges); + } + + + void SHParticleEmitterComponent::SetMinSpeed(float speed) noexcept + { + cpuEmitterData.minSpeed = speed; + } + + void SHParticleEmitterComponent::SetMaxSpeed(float speed) noexcept + { + cpuEmitterData.maxSpeed = speed; + } + + void SHParticleEmitterComponent::SetRotationSpeed(float speed) noexcept + { + cpuEmitterData.rotationSpeed = speed; + } + + void SHParticleEmitterComponent::SetRotationDecay(float decay) noexcept + { + cpuEmitterData.rotationDecay = decay; + } + + void SHParticleEmitterComponent::SetTextureIndex(uint32_t index) noexcept + { + cpuEmitterData.textureIndex = index; + } + + void SHParticleEmitterComponent::SetTextureAssetID(AssetID id) noexcept + { + textureAssetID = id; + } + + void SHParticleEmitterComponent::SetCustomUpdateShaderAssetID(AssetID id) noexcept + { + customUpdateShaderID = id; + } + + void SHParticleEmitterComponent::SetMinSize(float size) noexcept + { + cpuEmitterData.lifeAndSizeRange.z = size; + } + + void SHParticleEmitterComponent::SetMaxSize(float size) noexcept + { + cpuEmitterData.lifeAndSizeRange.w = size; + } + + void SHParticleEmitterComponent::SetSizeDecayMult(float decay) noexcept + { + cpuEmitterData.sizeDecayMult = decay; + } + + void SHParticleEmitterComponent::SetCustomUpdateShader(Handle shaderModule) noexcept + { + customUpdateShader = shaderModule; + } + + uint32_t SHParticleEmitterComponent::GetEmissionCount(void) const noexcept + { + return emissionCount; + } + + bool SHParticleEmitterComponent::GetPassive(void) const noexcept + { + return isPassive; + } + + float SHParticleEmitterComponent::GetEmissionInterval(void) const noexcept + { + return emissionInterval; + } + + float SHParticleEmitterComponent::GetMinLife(void) const noexcept + { + return cpuEmitterData.lifeAndSizeRange.x; + } + + float SHParticleEmitterComponent::GetMaxLife(void) const noexcept + { + return cpuEmitterData.lifeAndSizeRange.y; + + } + + SHVec4 const& SHParticleEmitterComponent::GetAngularRangesAndOffsets(void) const noexcept + { + return cpuEmitterData.angularRangesAndOffsets; + } + + float SHParticleEmitterComponent::GetMinSpeed(void) const noexcept + { + return cpuEmitterData.minSpeed; + } + + float SHParticleEmitterComponent::GetMaxSpeed(void) const noexcept + { + return cpuEmitterData.maxSpeed; + } + + float SHParticleEmitterComponent::GetRotationSpeed(void) const noexcept + { + return cpuEmitterData.rotationSpeed; + } + + float SHParticleEmitterComponent::GetRotationDecay(void) const noexcept + { + return cpuEmitterData.rotationDecay; + } + + uint32_t SHParticleEmitterComponent::GetTextureIndex(void) const noexcept + { + return cpuEmitterData.textureIndex; + } + + AssetID SHParticleEmitterComponent::GetTextureAssetID(void) const noexcept + { + return textureAssetID; + } + + AssetID SHParticleEmitterComponent::GetCustomUpdateShaderAssetID(void) const noexcept + { + return customUpdateShaderID; + } + + float SHParticleEmitterComponent::GetMinSize(void) const noexcept + { + return cpuEmitterData.lifeAndSizeRange.z; + } + + float SHParticleEmitterComponent::GetMaxSize(void) const noexcept + { + return cpuEmitterData.lifeAndSizeRange.w; + } + + + float SHParticleEmitterComponent::GetSizeDecayMult(void) const noexcept + { + return cpuEmitterData.sizeDecayMult; + } + + Handle SHParticleEmitterComponent::GetCustomUpdateShader(void) const noexcept + { + return customUpdateShader; + } + } \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h index 30f7f107..d9c26666 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h @@ -1,29 +1,53 @@ #pragma once #include "Resource/SHHandle.h" +#include "Math/Vector/SHVec2.h" #include "Math/Vector/SHVec4.h" #include "ECS_Base/Components/SHComponent.h" #include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h" +#include namespace SHADE { class SHVkBuffer; class SHVkDescriptorSetGroup; class SHVkDescriptorSetLayout; + class SHVkShaderModule; + class SHVkPipeline; class SHParticleEmitterComponent : public SHComponent { private: + struct GPUEmitterStruct { - //! Minimum emitting angular range - SHVec4 angularMin; + //! Angular ranges of emission + SHVec4 angularRangesAndOffsets; - //! Maximum emitting angular range - SHVec4 angularMax; + //! minimum starting velocity + float minSpeed; + + //! Maximum starting velocity + float maxSpeed; + + //! Rotational speed of the quad + float rotationSpeed; + + //! Rotation decay rate + float rotationDecay; //! Spawn lifetime and size range (min and max) SHVec4 lifeAndSizeRange; + + //! Size decay for particles + float sizeDecayMult; + + //! Texture used by the particle + uint32_t textureIndex; + + //! Padding for the shader struct + float padding[2]; + }; struct GPUParticleStruct @@ -84,6 +108,12 @@ namespace SHADE //! will contain 2 bindings that point to 2 buffers (input and output). Handle particleDescriptorSet; + //! Custom update shader for the particles in this component + Handle customUpdateShader; + + //! Internally the system will bind this pipeline when it detects that this is not a null handle + Handle customUpdatePipeline; + //! Emitter's data on the CPU side. To be copied to GPU. GPUEmitterStruct cpuEmitterData; @@ -99,12 +129,60 @@ 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; + + //! Custom update shaders, similarly with textures, will be identified through their AssetID + AssetID customUpdateShaderID; + public: void OnCreate(void) override final; void OnDestroy(void) override final; void Emit (void) noexcept; + void SetEmissionCount (uint32_t count) noexcept; + void SetPassive (bool flag) noexcept; + void SetEmissionInterval (float interval) noexcept; + void SetMinLife (float val) noexcept; + void SetMaxLife (float val) noexcept; + void SetAngularRangesAndOffsets (SHVec4 const& ranges) noexcept; + void SetMinSpeed (float speed) noexcept; + void SetMaxSpeed (float speed) noexcept; + void SetRotationSpeed (float speed) noexcept; + void SetRotationDecay (float decay) noexcept; + void SetTextureIndex (uint32_t index) noexcept; + void SetMinSize (float size) noexcept; + void SetMaxSize (float size) noexcept; + void SetSizeDecayMult (float decay) noexcept; + void SetCustomUpdateShader (Handle shaderModule) 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; + SHVec4 const& GetAngularRangesAndOffsets (void) const noexcept; + float GetMinSpeed (void) const noexcept; + float GetMaxSpeed (void) const noexcept; + float GetRotationSpeed (void) const noexcept; + float GetRotationDecay (void) const noexcept; + uint32_t GetTextureIndex (void) const noexcept; + float GetMinSize (void) const noexcept; + float GetMaxSize (void) const noexcept; + float GetSizeDecayMult (void) const noexcept; + Handle GetCustomUpdateShader (void) const noexcept; + + /*-----------------------------------------------------------------------*/ + /* NON-INTERFACE FUNCTIONS */ + /*-----------------------------------------------------------------------*/ + void SetTextureAssetID(AssetID id) noexcept; + void SetCustomUpdateShaderAssetID(AssetID id) noexcept; + + AssetID GetTextureAssetID(void) const noexcept; + AssetID GetCustomUpdateShaderAssetID(void) const noexcept; + + friend class SHParticleSubSystem; }; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.cpp index 7c3d9c74..d99832a5 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.cpp @@ -10,29 +10,39 @@ #include "Graphics/MiddleEnd/GlobalData/SHGlobalDescriptorSets.h" #include "Math/Transform/SHTransformComponent.h" #include "Graphics/Buffers/SHVkBuffer.h" +#include "FRC/SHFramerateController.h" +#include "Graphics/Pipeline/SHVkPipeline.h" +#include "Graphics/RenderGraph/SHSubpass.h" +#include "Graphics/SHVkUtil.h" +#include "Graphics/Synchronization/SHVkFence.h" + namespace SHADE { void SHParticleSubSystem::InitializeComponent(SHParticleEmitterComponent& comp) noexcept { - uint32_t emitterStructAligned = logicalDevice->PadSSBOSize(sizeof (SHParticleEmitterComponent::GPUEmitterStruct)); - uint32_t particleStructAligned = logicalDevice->PadUBOSize(sizeof (SHParticleEmitterComponent::GPUParticleStruct)); - uint32_t sizeofIndirectCmd = static_cast(sizeof(vk::DrawIndirectCommand)); - uint32_t sizeofUint = static_cast(sizeof(uint32_t)); - // TODO: temporary only. - static constexpr uint32_t NUM_PARTICLES = 500; + static constexpr uint32_t NUM_PARTICLES = 2000; comp.maxParticles = NUM_PARTICLES; + uint32_t sizeofUint = static_cast(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 indicesDataAligned = logicalDevice->PadSSBOSize(sizeofUint * comp.maxParticles); + + // offset into the buffer for input and output - uint32_t const PARTICLE_FRAME_CHUNK_SIZE = (particleStructAligned * comp.maxParticles); + uint32_t const PARTICLE_FRAME_CHUNK_SIZE = particleChunkStructAligned; // Buffer Initialization { - // count, value + // count, value. Initialize free count to max particles and indices to particle indices std::vector 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. @@ -40,14 +50,14 @@ namespace SHADE // buffer for particle data: pure GPU memory, no transfers, no flags. We want to triple buffer this so that we can submit work to the GPU // without having to wait for rendering to finish reading the data - comp.particleData = logicalDevice->CreateBuffer(SHGraphicsConstants::NUM_FRAME_BUFFERS * particleStructAligned * NUM_PARTICLES, nullptr, 0, vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, {}); + comp.particleData = logicalDevice->CreateBuffer(SHGraphicsConstants::NUM_FRAME_BUFFERS * particleChunkStructAligned, nullptr, 0, vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, {}); // Buffer for freelist data. 1 copy only, host-visible mapped. We only need 1 copy because it is only required in compute. If it was used or read in another // stage we would need more copies. comp.freelistData = logicalDevice->CreateBuffer(sizeofUint * (comp.maxParticles + 1), freelistInit.data(), sizeofUint * (comp.maxParticles + 1), vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT); // Buffer for indices. NUM_FRAME_BUFFERS copies since it's used in rendering. host-visible mapped. - comp.indicesData = logicalDevice->CreateBuffer(sizeofUint * comp.maxParticles, nullptr, 0, vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT); + comp.indicesData = logicalDevice->CreateBuffer(SHGraphicsConstants::NUM_FRAME_BUFFERS * indicesDataAligned, nullptr, 0, vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT); // Draw call data will not be tampered with after this initialization except for one variable: instanceCount, which will be modified from compute shader std::array indirectCommands{}; @@ -60,7 +70,7 @@ namespace SHADE } // buffer to store draw call data. Non-indexed, host-visible mapped, triple buffered. - comp.drawCallData = logicalDevice->CreateBuffer(SHGraphicsConstants::NUM_FRAME_BUFFERS * sizeofIndirectCmd, indirectCommands.data(), SHGraphicsConstants::NUM_FRAME_BUFFERS * sizeofIndirectCmd, vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eIndirectBuffer, VMA_MEMORY_USAGE_AUTO, VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT); + comp.drawCallData = logicalDevice->CreateBuffer(SHGraphicsConstants::NUM_FRAME_BUFFERS * sizeofIndirectCmdAligned, indirectCommands.data(), SHGraphicsConstants::NUM_FRAME_BUFFERS * sizeofIndirectCmdAligned, vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndirectBuffer, VMA_MEMORY_USAGE_AUTO, VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT); } @@ -73,7 +83,7 @@ namespace SHADE static constexpr uint32_t PARTICLE_DATA_SET_INDEX = 0; // Variable desc counts, all ignored anyway (This is required but its a dumb interface. You can only blame yourself, Brandon. ) - std::vector const VARIABLE_COUNTS = {0u,0u,0u,0u,0u}; + std::vector const VARIABLE_COUNTS = {0u}; // allocate new desc set comp.particleDescriptorSet = descPool->Allocate(descSetLayout, VARIABLE_COUNTS); @@ -83,11 +93,18 @@ 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_DRAW_DATA, { &comp.drawCallData, 1 }, 0, sizeofIndirectCmd); + 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); + set->UpdateDescriptorSetBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_INPUT_DATA); + set->UpdateDescriptorSetBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_OUTPUT_DATA); + set->UpdateDescriptorSetBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_FREELIST_DATA); + set->UpdateDescriptorSetBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_INDICES_DATA); + set->UpdateDescriptorSetBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_DRAW_DATA); } comp.initialized = true; @@ -110,8 +127,8 @@ namespace SHADE offsets[DYOFF_INDEX_EMITTER] = i * emitterStructAligned; offsets[DYOFF_INDEX_PARTICLE_INPUT] = inputOffset; offsets[DYOFF_INDEX_PARTICLE_OUTPUT] = outputOffset; - offsets[DYOFF_INDEX_INDICES_DATA] = i * sizeofUint * comp.maxParticles; - offsets[DYOFF_INDEX_DRAW_DATA] = i * sizeofIndirectCmd; + offsets[DYOFF_INDEX_INDICES_DATA] = i * indicesDataAligned; + offsets[DYOFF_INDEX_DRAW_DATA] = i * sizeofIndirectCmdAligned; } } @@ -126,6 +143,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); } @@ -134,13 +153,6 @@ namespace SHADE { auto const& mappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::PARTICLE_RENEDERING); - uint32_t instanceCountOffset = sizeof (vk::DrawIndirectCommand) * frameIndex + offsetof(vk::DrawIndirectCommand, instanceCount); - uint32_t ZERO = 0; - - // reset instance count to 0 - comp.drawCallData->WriteToMemory (&ZERO, sizeof(uint32_t), 0, instanceCountOffset); - - // bind the descriptor sets required for emitting particles cmdBuffer->BindDescriptorSet(comp.particleDescriptorSet, SH_PIPELINE_TYPE::COMPUTE, mappings.at(SHPredefinedDescriptorTypes::PARTICLES), comp.dynamicOffsets[frameIndex]); @@ -150,7 +162,110 @@ namespace SHADE void SHParticleSubSystem::RenderComponent(Handle cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept { + cmdBuffer->DrawMultiIndirect(comp.drawCallData, 1, static_cast(sizeof(vk::DrawIndirectCommand)) * frameIndex); + //cmdBuffer->DrawArrays(4, 1, 0, 0); + } + void SHParticleSubSystem::PreparePrePostUpdateBarriers(std::vector& preUpdateBarriers, std::vector& postUpdateBarriers, SHParticleEmitterComponent const& emitter, uint32_t const EMITTER_INDEX, uint32_t const FRAME_INDEX) noexcept + { + // pre-update particles data barrier. Note that this is for input because we want the input to be available before we use it. + vk::BufferMemoryBarrier inputParticleDataBarrierPreUpdate + { + .srcAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite, + .dstAccessMask = vk::AccessFlagBits::eShaderRead, + .buffer = emitter.particleData->GetVkBuffer(), + .offset = emitter.dynamicOffsets[FRAME_INDEX][DYOFF_INDEX_PARTICLE_INPUT], + .size = emitter.chunkSize + }; + + // pre-update free list data barrier. + vk::BufferMemoryBarrier freelistDataBarrierPreUpdate + { + .srcAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite, + .dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite, + .buffer = emitter.freelistData->GetVkBuffer(), + .offset = 0, // Only 1 copy of freelist data, so offset is at 0 + .size = static_cast(sizeof (uint32_t)) * (emitter.maxParticles + 1) + }; + + // pre update indices data barrier. + vk::BufferMemoryBarrier indicesDataBarrierPreUpdate + { + + .srcAccessMask = vk::AccessFlagBits::eShaderWrite | vk::AccessFlagBits::eShaderRead, + .dstAccessMask = vk::AccessFlagBits::eShaderWrite, + .buffer = emitter.indicesData->GetVkBuffer(), + .offset = emitter.dynamicOffsets[FRAME_INDEX][DYOFF_INDEX_INDICES_DATA], + .size = static_cast(sizeof(uint32_t)) * emitter.maxParticles + }; + + // ...copy assign barriers on heap + preUpdateBarriers.push_back(inputParticleDataBarrierPreUpdate); + preUpdateBarriers.push_back(freelistDataBarrierPreUpdate); + preUpdateBarriers.push_back(indicesDataBarrierPreUpdate); + + // make new barrier on stack... + vk::BufferMemoryBarrier outputParticleDataBarrierPostUpdate + { + .srcAccessMask = vk::AccessFlagBits::eShaderWrite, + .dstAccessMask = vk::AccessFlagBits::eShaderRead, + .buffer = emitter.particleData->GetVkBuffer(), + .offset = emitter.dynamicOffsets[FRAME_INDEX][DYOFF_INDEX_PARTICLE_OUTPUT], + .size = emitter.chunkSize + }; + + // make new barrier on stack... + vk::BufferMemoryBarrier indicesDataBarrierPostUpdate + { + .srcAccessMask = vk::AccessFlagBits::eShaderWrite, + .dstAccessMask = vk::AccessFlagBits::eShaderRead, + .buffer = emitter.indicesData->GetVkBuffer(), + .offset = emitter.dynamicOffsets[FRAME_INDEX][DYOFF_INDEX_INDICES_DATA], + .size = static_cast(sizeof(uint32_t)) * emitter.maxParticles + }; + + // make new barrier on stack... + vk::BufferMemoryBarrier drawDataBarrierPostUpdate + { + .srcAccessMask = vk::AccessFlagBits::eShaderWrite, + .dstAccessMask = vk::AccessFlagBits::eIndirectCommandRead, + .buffer = emitter.drawCallData->GetVkBuffer(), + .offset = emitter.dynamicOffsets[FRAME_INDEX][DYOFF_INDEX_DRAW_DATA], + .size = static_cast(sizeof(vk::DrawIndirectCommand)) + }; + + // ...copy assign barriers on heap + postUpdateBarriers.push_back(outputParticleDataBarrierPostUpdate); + postUpdateBarriers.push_back(indicesDataBarrierPostUpdate); + postUpdateBarriers.push_back(drawDataBarrierPostUpdate); + } + + Handle SHParticleSubSystem::GetCustomUpdatePipeline(Handle customUpdateShader) noexcept + { + if (!customUpdateShader) + return {}; + + if (!customUpdatePipelineCache.contains(customUpdateShader)) + { + SHPipelineLayoutParams plParams + { + .shaderModules = {customUpdateShader}, + .predefinedDescSetLayouts = SHGraphicsPredefinedData::GetSystemData(SHGraphicsPredefinedData::SystemType::PARTICLE_RENEDERING).descSetLayouts + }; + + auto pipelineLayout = logicalDevice->CreatePipelineLayout(plParams); + auto newPipeline = logicalDevice->CreateComputePipeline(pipelineLayout); + newPipeline->ConstructPipeline(); + + if (!newPipeline) + return {}; + + auto customUpdateShaderData = CustomPipeline{ newPipeline, pipelineLayout }; + + customUpdatePipelineCache.emplace (customUpdateShader, customUpdateShaderData); + } + + return customUpdatePipelineCache.at (customUpdateShader).customPipeline; } void SHParticleSubSystem::Init(Handle device, Handle inDescPool, Handle compatibleRenderpass, Handle subpass, Handle VS, Handle FS, Handle emitCS, Handle defaultUpdateCS) noexcept @@ -170,6 +285,40 @@ namespace SHADE renderingPipelineData.pipelineLayout = logicalDevice->CreatePipelineLayout(plParams); renderingPipelineData.pipeline = logicalDevice->CreateGraphicsPipeline(renderingPipelineData.pipelineLayout, nullptr, compatibleRenderpass, subpass); + SHColorBlendState colorBlendState{}; + colorBlendState.logic_op_enable = VK_FALSE; + colorBlendState.logic_op = vk::LogicOp::eCopy; + + auto const& subpassColorReferences = subpass->GetColorAttachmentReferences(); + colorBlendState.attachments.reserve(subpassColorReferences.size()); + + + for (auto& att : subpassColorReferences) + { + colorBlendState.attachments.push_back(vk::PipelineColorBlendAttachmentState + { + .blendEnable = SHVkUtil::IsBlendCompatible(subpass->GetFormatFromAttachmentReference(att.attachment)), + .srcColorBlendFactor = vk::BlendFactor::eSrcAlpha, + .dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha, + .colorBlendOp = vk::BlendOp::eAdd, + .srcAlphaBlendFactor = vk::BlendFactor::eSrcAlpha, + .dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha, + .alphaBlendOp = vk::BlendOp::eAdd, + .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA, + } + ); + } + + renderingPipelineData.pipeline->GetPipelineState().SetColorBlenState(colorBlendState); + + // Sets the input assembly state for rendering particles + SHInputAssemblyState inputAssemblyState{}; + inputAssemblyState.topology = vk::PrimitiveTopology::eTriangleFan; + renderingPipelineData.pipeline->GetPipelineState().SetInputAssemblyState(inputAssemblyState); + + + renderingPipelineData.pipeline->ConstructPipeline(); + SHPipelineLayoutParams emitPlParams { .shaderModules = {emitCS}, @@ -178,6 +327,7 @@ namespace SHADE emittingPipelineData.pipelineLayout = logicalDevice->CreatePipelineLayout(emitPlParams); emittingPipelineData.pipeline = logicalDevice->CreateComputePipeline(emittingPipelineData.pipelineLayout); + emittingPipelineData.pipeline->ConstructPipeline(); SHPipelineLayoutParams defaultUpdatePlParams { @@ -187,7 +337,7 @@ namespace SHADE defaultUpdatePipelineData.pipelineLayout = logicalDevice->CreatePipelineLayout(defaultUpdatePlParams); defaultUpdatePipelineData.pipeline = logicalDevice->CreateComputePipeline(defaultUpdatePipelineData.pipelineLayout); - + defaultUpdatePipelineData.pipeline->ConstructPipeline(); /*-----------------------------------------------------------------------*/ /* OTHER INITIALIZATION */ @@ -195,18 +345,33 @@ namespace SHADE SHComponentManager::CreateComponentSparseSet(); } - void SHParticleSubSystem::Run(Handle cmdBuffer, uint32_t frameIndex, float dt) noexcept + void SHParticleSubSystem::Run(Handle cmdBuffer, uint32_t frameIndex, Handle waitFence) noexcept { + float dt = static_cast(SHFrameRateController::GetRawDeltaTime()); + auto& emitters = SHComponentManager::GetDense(); auto const& mappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::PARTICLE_RENEDERING); // Get offset into GPU emitter data (for updating) uint32_t emitterDataOffset = frameIndex * sizeof (SHParticleEmitterComponent::GPUEmitterStruct); + // TODO: OPTIONAL but eventually these barriers can be moved to the system as member variables. This would require additional bookkeeping + // but it will be more efficient than populating a vector every frame. + // Barriers to make sure emitting shader is done completely before update is run. - // Every emitter will have its own barrier. + // 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()); + 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.reserve(emitters.size() * 3); + + std::vector preDrawBarriers{}; + 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 // the update compute. HOWEVER since a NUM_FRAME_BUFFERS frames would have passed by then, we will not insert 1 here. @@ -215,8 +380,6 @@ namespace SHADE /* BEGIN EMITTING PARTICES */ /*-----------------------------------------------------------------------*/ - // TODO: Might need to issue a barrier here for input particle data - // bind the pipeline for emitting particles cmdBuffer->BindPipeline(emittingPipelineData.pipeline); @@ -224,83 +387,135 @@ namespace SHADE SHGlobalDescriptorSets::BindGenericAndTextureData(logicalDevice, cmdBuffer, SH_PIPELINE_TYPE::COMPUTE, mappings.at(SHPredefinedDescriptorTypes::STATIC_DATA), frameIndex); uint32_t i = 0; + + if (waitFence) + waitFence->Wait(true); + 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); - } - - // make new barrier on stack... - vk::BufferMemoryBarrier barrier - { - .srcAccessMask = vk::AccessFlagBits::eShaderWrite, - .dstAccessMask = vk::AccessFlagBits::eShaderRead, - .buffer = emitter.particleData->GetVkBuffer(), - .offset = emitter.dynamicOffsets[frameIndex][DYOFF_INDEX_PARTICLE_INPUT], - .size = emitter.chunkSize - }; - - // ...copy assign barrier on heap - preUpdateBarriers[i] = barrier; - - emitter.toEmit = false; ++i; } + // issue the barrier to wait - cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eComputeShader, {}, {}, preUpdateBarriers, {}); + if (!preUpdateBarriers.empty()) + cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader | vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eDrawIndirect, vk::PipelineStageFlagBits::eComputeShader, {}, {}, preUpdateBarriers, {}); + + /*-----------------------------------------------------------------------*/ /* EMITTING PARTICLES DONE, BEGIN UPDATES.... */ /*-----------------------------------------------------------------------*/ + for (auto& emitter : emitters) + { + // If custom update shader is a valid handle in the component + if (emitter.customUpdateShader) + { + // Check if pipeline associated with shader is valid, if not create or get one from the cache + if (!emitter.customUpdatePipeline) + emitter.customUpdatePipeline = GetCustomUpdatePipeline(emitter.customUpdateShader); - // bind the pipeline for updating - cmdBuffer->BindPipeline(defaultUpdatePipelineData.pipeline); + // bind the custom pipeline for updating + cmdBuffer->BindPipeline(emitter.customUpdatePipeline); + } + else + { + // bind the default upddate pipeline for updating + cmdBuffer->BindPipeline(defaultUpdatePipelineData.pipeline); + } + + 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. + if (!postUpdateBarriers.empty()) + cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eDrawIndirect | vk::PipelineStageFlagBits::eVertexShader, {}, {}, postUpdateBarriers, {}); + + } + + void SHParticleSubSystem::ResetInstanceCounts(Handle cmdBuffer, uint32_t frameIndex) noexcept + { + auto& emitters = SHComponentManager::GetDense(); for (auto& emitter : emitters) { - UpdateCompoennt(cmdBuffer, emitter, frameIndex); - } + if (emitter.initialized) + { + //uint32_t instanceCountOffset = 20; + uint32_t instanceCountOffset = sizeof(vk::DrawIndirectCommand) * frameIndex + offsetof(vk::DrawIndirectCommand, instanceCount); + uint32_t ZERO = 0; - + // reset instance count to 0 + emitter.drawCallData->WriteToMemory(&ZERO, sizeof(uint32_t), 0, instanceCountOffset); + } + } } void SHParticleSubSystem::Render(Handle cmdBuffer, Handle renderer, uint32_t frameIndex) noexcept { auto& emitters = SHComponentManager::GetDense(); + 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) { - + 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); + } } } + void SHParticleSubSystem::Exit(void) noexcept { diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.h index ed697a59..a0675da6 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.h @@ -20,6 +20,7 @@ namespace SHADE class SHVkShaderModule; class SHRenderer; class SHParticleEmitterComponent; + class SHVkFence; @@ -33,6 +34,12 @@ namespace SHADE static constexpr uint32_t DYOFF_INDEX_INDICES_DATA = 3; static constexpr uint32_t DYOFF_INDEX_DRAW_DATA = 4; + struct CustomPipeline + { + Handle customPipeline; + Handle customPipelineLayout; + }; + // To hold data for a pipeline and pipeline layout. // We want this here because particles require 3 pipeline sets: @@ -72,19 +79,26 @@ namespace SHADE //! Pipeline data for updating particles PipelineData defaultUpdatePipelineData; - //! Desc pool for particle component desc set allocation + //! Desc pool for particle component desc set allocation Handle descPool; + std::unordered_map, CustomPipeline> customUpdatePipelineCache; + void InitializeComponent (SHParticleEmitterComponent& comp) noexcept; void EmitComponent (Handle cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept; void UpdateCompoennt(Handle cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept; void RenderComponent(Handle cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept; + void PreparePrePostUpdateBarriers (std::vector& preUpdateBarriers, std::vector& postUpdateBarriers, SHParticleEmitterComponent const& emitter, uint32_t const EMITTER_INDEX, uint32_t const FRAME_INDEX) noexcept; + + Handle GetCustomUpdatePipeline (Handle customUpdateShader) noexcept; + public: void Init(Handle device, Handle inDescPool, Handle compatibleRenderpass, Handle subpass, Handle VS, Handle FS, Handle emitCS, Handle defaultUpdateCS) noexcept; - void Run(Handle cmdBuffer, uint32_t frameIndex, float dt) noexcept; + void Run(Handle cmdBuffer, uint32_t frameIndex, Handle waitFence = {}) noexcept; + void ResetInstanceCounts (Handle cmdBuffer, uint32_t frameIndex) noexcept; void Render(Handle cmdBuffer, Handle renderer, uint32_t frameIndex) noexcept; void Exit(void) noexcept; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp index db5b08c3..f71c3f93 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp @@ -19,15 +19,16 @@ namespace SHADE // generate samples for (uint32_t i = 0; i < NUM_SAMPLES; ++i) { - //SHVec3 temp - //{ - // distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f - // distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f - // distrib(generator), // 0.0f - 1.0f so that sample space is a hemisphere - //}; + SHVec3 temp + { + distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f + distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f + distrib(generator), // 0.0f - 1.0f so that sample space is a hemisphere + }; - //temp = SHVec3::Normalise(temp); - //temp *= distrib(generator); + temp = SHVec3::Normalise(temp); + temp *= distrib(generator); + samples[i] = SHVec4 (temp.x, temp.y, temp.z, 0.0f); //// This makes sure that most points are closer to fragment's position //float scale = 1.0f / static_cast(NUM_SAMPLES); @@ -36,16 +37,16 @@ namespace SHADE //samples[i] = SHVec4 (temp.x, temp.y, temp.z, 0.0f); - samples[i] = SHVec4 - { - distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f - distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f - distrib(generator), // 0.0f - 1.0f so that sample space is a hemisphere - 0.0f - }; + //samples[i] = SHVec4 + //{ + // distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f + // distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f + // distrib(generator), // 0.0f - 1.0f so that sample space is a hemisphere + // 0.0f + //}; // This makes sure that most points are closer to fragment's position - float scale = 1.0f / static_cast(NUM_SAMPLES); + float scale = static_cast(i) / static_cast(NUM_SAMPLES); scale = std::lerp(0.1f, 1.0f, scale * scale); samples[i] *= scale; } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.cpp index 41678935..b7ef5e35 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.cpp @@ -14,6 +14,7 @@ namespace SHADE /***************************************************************************/ void SHTrajectoryRenderableComponent::OnCreate(void) { + ResetSimulationInfo(); } void SHTrajectoryRenderableComponent::OnDestroy(void) @@ -22,12 +23,13 @@ namespace SHADE } - void SHTrajectoryRenderableComponent::SimulateTrajectory(EntityID eid, SHVec3 force, float timestep, uint32_t maxSteps) noexcept + void SHTrajectoryRenderableComponent::SimulateTrajectory(EntityID eid, float mass, SHVec3 force, float timestep, uint32_t maxSteps) noexcept { entityToSimulate = eid; simulationForce = force; simulationTimestep = timestep; simulationMaxSteps = maxSteps; + objectMass = mass; } float SHTrajectoryRenderableComponent::GetSimulationTimestep(void) const noexcept @@ -35,6 +37,11 @@ namespace SHADE return simulationTimestep; } + float SHTrajectoryRenderableComponent::GetObjectMass(void) const noexcept + { + return objectMass; + } + void SHTrajectoryRenderableComponent::ResetSimulationInfo(void) noexcept { entityToSimulate = MAX_EID; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h b/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h index 95d40af1..51536107 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h @@ -36,6 +36,9 @@ namespace SHADE //! plotting a point in the simulation float simulationTimestep; + //! mass of the object to simulate + float objectMass; + //! Entity to simulate trajectory of EntityID entityToSimulate; @@ -68,12 +71,13 @@ namespace SHADE SHVec3 GetSimulationForce (void) const noexcept; uint32_t GetSimulationMaxSteps (void) const noexcept; float GetSimulationTimestep (void) const noexcept; + float GetObjectMass (void) const noexcept; void ResetSimulationInfo (void) noexcept; void OnCreate(void) override final; void OnDestroy(void) override final; - void SimulateTrajectory (EntityID eid, SHVec3 force, float timestep, uint32_t maxSteps) noexcept; + void SimulateTrajectory (EntityID eid, float mass, SHVec3 force, float timestep, uint32_t maxSteps) noexcept; RTTR_ENABLE() diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderingSubSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderingSubSystem.cpp index f2689209..dab94c5d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderingSubSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderingSubSystem.cpp @@ -1,7 +1,6 @@ #include "SHpch.h" #include "SHTrajectoryRenderingSubSystem.h" -#include "../../../../SHGhostBody.h" #include "ECS_Base/Managers/SHComponentManager.h" #include "Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h" #include "Graphics/Devices/SHVkLogicalDevice.h" @@ -15,6 +14,7 @@ #include "Graphics/MiddleEnd/Interface/SHRenderer.h" #include "Physics/System/SHPhysicsSystem.h" #include "ECS_Base/Managers/SHSystemManager.h" +#include namespace SHADE { @@ -88,7 +88,13 @@ namespace SHADE std::vector positions{}; std::vector quats{}; - SHGhostBody defaultGhostBody{}; + auto* rigidBodyComp = SHComponentManager::GetComponent_s(entityToSimulate); + if (!rigidBodyComp) + continue; + + SHGhostBody defaultGhostBody{*rigidBodyComp}; + + defaultGhostBody.mass = comp.GetObjectMass(); SHPhysicsSystem::SimulateBodyInfo simulateInfo { @@ -109,6 +115,8 @@ namespace SHADE comp.ResetSimulationInfo(); + std::cout << positions.size() << std::endl; + // If has positions, feed data to buffer. if (!positions.empty()) { @@ -208,7 +216,7 @@ namespace SHADE cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::TRAJECTORY_TRANSFORM, transformBuffer, 0); // call draw call - cmdBuffer->DrawMultiIndirect(drawDataBuffer, drawData.size()); + cmdBuffer->DrawMultiIndirectIndexed(drawDataBuffer, drawData.size()); // clear CPU transform and draw data transformData.clear(); diff --git a/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.cpp b/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.cpp index 38944882..f2f8db7a 100644 --- a/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.cpp +++ b/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.cpp @@ -299,6 +299,8 @@ namespace SHADE , created{ false } { + if (pipelineLayout) + pipelineLayout->AddCallback([this]() {ConstructPipeline(); }); } diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp index 53363a63..4a89290a 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp @@ -372,6 +372,7 @@ namespace SHADE , ISelfHandle{std::move(rhs)} , dynamicIsActive {rhs.dynamicIsActive} , isDynamic {rhs.isDynamic} + , preBeginFuncs{std::move(rhs.preBeginFuncs)} { rhs.renderpass = {}; @@ -399,6 +400,7 @@ namespace SHADE name = std::move(rhs.name); dynamicIsActive = rhs.dynamicIsActive; isDynamic = rhs.isDynamic; + preBeginFuncs = std::move(rhs.preBeginFuncs); rhs.renderpass = {}; @@ -493,6 +495,11 @@ namespace SHADE return nodeCompute; } + void SHRenderGraphNode::AddPreBeginFunction(PreBeginFunction const& func) noexcept + { + preBeginFuncs.push_back(func); + } + /***************************************************************************/ /*! @@ -664,6 +671,11 @@ namespace SHADE void SHRenderGraphNode::Execute(Handle commandBuffer, Handle descPool, uint32_t frameIndex) noexcept { + for (auto& func : preBeginFuncs) + { + func(commandBuffer, frameIndex); + } + if (renderpass) { uint32_t framebufferIndex = (framebuffers.size() > 1) ? frameIndex : 0; diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h index 971a84ae..72494b6a 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h @@ -27,6 +27,10 @@ namespace SHADE class SH_API SHRenderGraphNode : public ISelfHandle { + public: + using PreBeginFunction = std::function, uint32_t)>; + + private: /*-----------------------------------------------------------------------*/ /* PRIVATE MEMBER VARIABLES */ @@ -74,6 +78,9 @@ namespace SHADE //! of drawing objects on the image (i.e. compute). std::vector> nodeComputes; + //! Calls before renderpass begins + std::vector preBeginFuncs; + //! Whether or not the node has finished execution bool executed; @@ -118,6 +125,7 @@ namespace SHADE /*-----------------------------------------------------------------------*/ Handle AddSubpass(std::string subpassName, Handle viewport, Handle renderer) noexcept; Handle AddNodeCompute(std::string nodeName, Handle computeShaderModule, std::initializer_list resources, std::unordered_set&& dynamicBufferBindings = {}, uint32_t variableDescCount = 0, float numWorkGroupScale = 1.0f) noexcept; + void AddPreBeginFunction (PreBeginFunction const& func) noexcept; void Execute(Handle commandBuffer, Handle descPool, uint32_t frameIndex) noexcept; Handle GetOrCreatePipeline(std::pair, Handle> const& vsFsPair, Handle subpass) noexcept; diff --git a/SHADE_Engine/src/Graphics/Shaders/SHVkShaderModule.cpp b/SHADE_Engine/src/Graphics/Shaders/SHVkShaderModule.cpp index 49188181..8f41d24a 100644 --- a/SHADE_Engine/src/Graphics/Shaders/SHVkShaderModule.cpp +++ b/SHADE_Engine/src/Graphics/Shaders/SHVkShaderModule.cpp @@ -94,8 +94,8 @@ namespace SHADE Recompile(); - //for (auto& callback : onChangeCallbacks) - // callback(); + for (auto& callback : onChangeCallbacks) + callback(); } void SHVkShaderModule::AddCallback(ChangeCallback&& callback) noexcept diff --git a/SHADE_Engine/src/Graphics/Synchronization/SHVkFence.h b/SHADE_Engine/src/Graphics/Synchronization/SHVkFence.h index 7c48a5f6..9e552a85 100644 --- a/SHADE_Engine/src/Graphics/Synchronization/SHVkFence.h +++ b/SHADE_Engine/src/Graphics/Synchronization/SHVkFence.h @@ -30,7 +30,7 @@ namespace SHADE /*-----------------------------------------------------------------------*/ /* PUBLIC MEMBER FUNCTIONS */ /*-----------------------------------------------------------------------*/ - bool Wait (bool waitAll, uint64_t timer) noexcept; + bool Wait (bool waitAll, uint64_t timer = std::numeric_limits::max()) noexcept; void Reset (void) noexcept; /*-----------------------------------------------------------------------*/ diff --git a/SHADE_Engine/SHGhostBody.cpp b/SHADE_Engine/src/Physics/System/SHGhostBody.cpp similarity index 82% rename from SHADE_Engine/SHGhostBody.cpp rename to SHADE_Engine/src/Physics/System/SHGhostBody.cpp index ef260521..2a5cff3a 100644 --- a/SHADE_Engine/SHGhostBody.cpp +++ b/SHADE_Engine/src/Physics/System/SHGhostBody.cpp @@ -38,13 +38,13 @@ namespace SHADE localInvInertia.y = 1.0f / LOCAL_INERTIA.y; localInvInertia.z = 1.0f / LOCAL_INERTIA.z; - linearLock.x = rigidBody.GetFreezePositionX() ? 1.0f : 0.0f; - linearLock.y = rigidBody.GetFreezePositionY() ? 1.0f : 0.0f; - linearLock.z = rigidBody.GetFreezePositionZ() ? 1.0f : 0.0f; + linearLock.x = rigidBody.GetFreezePositionX() ? 0.0f : 1.0f; + linearLock.y = rigidBody.GetFreezePositionY() ? 0.0f : 1.0f; + linearLock.z = rigidBody.GetFreezePositionZ() ? 0.0f : 1.0f; - angularLock.x = rigidBody.GetFreezeRotationX() ? 1.0f : 0.0f; - angularLock.y = rigidBody.GetFreezeRotationY() ? 1.0f : 0.0f; - angularLock.z = rigidBody.GetFreezeRotationZ() ? 1.0f : 0.0f; + angularLock.x = rigidBody.GetFreezeRotationX() ? 0.0f : 1.0f; + angularLock.y = rigidBody.GetFreezeRotationY() ? 0.0f : 1.0f; + angularLock.z = rigidBody.GetFreezeRotationZ() ? 0.0f : 1.0f; } diff --git a/SHADE_Engine/SHGhostBody.h b/SHADE_Engine/src/Physics/System/SHGhostBody.h similarity index 100% rename from SHADE_Engine/SHGhostBody.h rename to SHADE_Engine/src/Physics/System/SHGhostBody.h diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index 75ed50f4..32d6f03e 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -14,7 +14,6 @@ #include "SHPhysicsSystem.h" // Project Headers -#include "../../../SHGhostBody.h" #include "Assets/SHAssetMacros.h" #include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/Managers/SHEntityManager.h" @@ -25,6 +24,7 @@ #include "Physics/Interface/SHColliderComponent.h" #include "Scene/SHSceneManager.h" #include "Scripting/SHScriptEngine.h" +#include "SHGhostBody.h" namespace SHADE { diff --git a/SHADE_Engine/src/Serialization/SHSerialization.cpp b/SHADE_Engine/src/Serialization/SHSerialization.cpp index b6455935..8dec9ad6 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.cpp +++ b/SHADE_Engine/src/Serialization/SHSerialization.cpp @@ -248,6 +248,7 @@ namespace SHADE AddComponentToComponentNode(components, eid); AddComponentToComponentNode(components, eid); AddComponentToComponentNode(components, eid); + AddComponentToComponentNode(components, eid); node[ComponentsNode] = components; @@ -310,6 +311,7 @@ namespace SHADE AddComponentID(componentIDList, componentsNode); AddComponentID(componentIDList, componentsNode); AddComponentID(componentIDList, componentsNode); + AddComponentID(componentIDList, componentsNode); return componentIDList; } @@ -398,5 +400,6 @@ namespace SHADE SHSerializationHelper::InitializeComponentFromNode(componentsNode, eid); SHSerializationHelper::InitializeComponentFromNode(componentsNode, eid); SHSerializationHelper::InitializeComponentFromNode(componentsNode, eid); + SHSerializationHelper::InitializeComponentFromNode(componentsNode, eid); } } diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index 0bf66a16..2550388e 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -17,6 +17,7 @@ #include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" #include "Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h" #include "Physics/Collision/Shapes/SHCapsule.h" +#include namespace YAML { @@ -35,6 +36,9 @@ namespace YAML struct HasYAMLConv : std::true_type {}; template<> struct HasYAMLConv : std::true_type {}; + template<> + struct HasYAMLConv : std::true_type {}; + template<> struct convert @@ -497,4 +501,120 @@ namespace YAML } }; + template<> + struct convert + { + 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_RANGES_OFFSET_TAG = "Angular Ranges And Offset"; + static constexpr std::string_view MIN_SIZE_TAG = "Minimum Size"; + static constexpr std::string_view MAX_SIZE_TAG = "Maximum Size"; + static constexpr std::string_view SIZE_DECAY_TAG = "Size Decay"; + 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 ROTATION_DECAY_TAG = "Rotation Decay"; + static constexpr std::string_view TEXTURE_ASSET_ID_TAG = "Texture Asset ID"; + static constexpr std::string_view CUSTOM_UPDATE_SHADER_ASSET_ID_TAG = "Custom Update Shader Asset ID"; + + 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[MIN_SPEED_TAG.data()] = rhs.GetMinSpeed(); + node[MAX_SPEED_TAG.data()] = rhs.GetMaxSpeed(); + node[MIN_SIZE_TAG.data()] = rhs.GetMinSize(); + node[MAX_SIZE_TAG.data()] = rhs.GetMaxSize(); + node[SIZE_DECAY_TAG.data()] = rhs.GetSizeDecayMult(); + node[ANGULAR_RANGES_OFFSET_TAG.data()] = rhs.GetAngularRangesAndOffsets(); + node[ROTATION_SPEED_TAG.data()] = rhs.GetRotationSpeed(); + node[ROTATION_DECAY_TAG.data()] = rhs.GetRotationDecay(); + node[TEXTURE_ASSET_ID_TAG.data()] = rhs.GetTextureAssetID(); + node[CUSTOM_UPDATE_SHADER_ASSET_ID_TAG.data()] = rhs.GetCustomUpdateShaderAssetID(); + + 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()); + + if (node[IS_PASSIVE_TAG.data()].IsDefined()) + rhs.SetPassive(node[IS_PASSIVE_TAG.data()].as()); + + if (node[EMISSION_INTERVAL_TAG.data()].IsDefined()) + rhs.SetEmissionInterval(node[EMISSION_INTERVAL_TAG.data()].as()); + + if (node[MIN_LIFE_TAG.data()].IsDefined()) + rhs.SetMinLife(node[MIN_LIFE_TAG.data()].as()); + + if (node[MAX_LIFE_TAG.data()].IsDefined()) + rhs.SetMaxLife(node[MAX_LIFE_TAG.data()].as()); + + if (node[SIZE_DECAY_TAG.data()].IsDefined()) + rhs.SetSizeDecayMult(node[SIZE_DECAY_TAG.data()].as()); + + if (node[ANGULAR_RANGES_OFFSET_TAG.data()].IsDefined()) + rhs.SetAngularRangesAndOffsets(node[ANGULAR_RANGES_OFFSET_TAG.data()].as()); + + if (node[MIN_SPEED_TAG.data()].IsDefined()) + rhs.SetMinSpeed(node[MIN_SPEED_TAG.data()].as()); + + if (node[MAX_SPEED_TAG.data()].IsDefined()) + rhs.SetMaxSpeed (node[MAX_SPEED_TAG.data()].as()); + + 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[ROTATION_DECAY_TAG.data()].IsDefined()) + rhs.SetRotationDecay(node[ROTATION_DECAY_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); + SHResourceManager::FinaliseChanges(); + //gfxSystem->BuildTextures(); + + if (texture) + { + rhs.SetTextureIndex(texture->TextureArrayIndex); + rhs.SetTextureAssetID(id); + } + else + { + SHLOG_WARNING ("Texture not set for particle emitter component: texture handle is null. "); + } + } + + if (node[CUSTOM_UPDATE_SHADER_ASSET_ID_TAG.data()].IsDefined()) + { + AssetID id = node[CUSTOM_UPDATE_SHADER_ASSET_ID_TAG.data()].as(); + + Handle shaderModule = SHResourceManager::LoadOrGet(id); + SHResourceManager::FinaliseChanges(); + //gfxSystem->BuildTextures(); + + rhs.SetCustomUpdateShader(shaderModule); + rhs.SetTextureAssetID(id); + } + + return true; + } + }; + } diff --git a/SHADE_Managed/src/Components/TrajectoryRenderable.cxx b/SHADE_Managed/src/Components/TrajectoryRenderable.cxx index 9eace4ab..7234147c 100644 --- a/SHADE_Managed/src/Components/TrajectoryRenderable.cxx +++ b/SHADE_Managed/src/Components/TrajectoryRenderable.cxx @@ -13,9 +13,9 @@ namespace SHADE } - void TrajectoryRenderable::SimulateTrajectory(EntityID eid, Vector3 force, float timestep, uint32_t maxSteps) + void TrajectoryRenderable::SimulateTrajectory(EntityID eid, float mass, Vector3 force, float timestep, uint32_t maxSteps) { - GetNativeComponent()->SimulateTrajectory(eid, Convert::ToNative(force), timestep, maxSteps); + GetNativeComponent()->SimulateTrajectory(eid, mass, Convert::ToNative(force), timestep, maxSteps); } MeshAsset TrajectoryRenderable::Mesh::get() diff --git a/SHADE_Managed/src/Components/TrajectoryRenderable.hxx b/SHADE_Managed/src/Components/TrajectoryRenderable.hxx index 78e3c0f1..e6f71788 100644 --- a/SHADE_Managed/src/Components/TrajectoryRenderable.hxx +++ b/SHADE_Managed/src/Components/TrajectoryRenderable.hxx @@ -83,7 +83,7 @@ namespace SHADE void set(float val); } - void SimulateTrajectory(EntityID eid, Vector3 force, float timestep, uint32_t maxSteps); + void SimulateTrajectory(EntityID eid, float mass, Vector3 force, float timestep, uint32_t maxSteps); }; }