diff --git a/Assets/Scripts/Gameplay/SC_GameManager.cs b/Assets/Scripts/Gameplay/SC_GameManager.cs index ef6a4666..6cec41f4 100644 --- a/Assets/Scripts/Gameplay/SC_GameManager.cs +++ b/Assets/Scripts/Gameplay/SC_GameManager.cs @@ -67,8 +67,13 @@ public class GameManager : Script stealFoodPopUpDone = false; PreviewLevelDone = false; - AudioHandler.audioClipHandlers["BGMWin"] = Audio.CreateAudioClip("event:/Music/stingers/game_win"); - AudioHandler.audioClipHandlers["BGMLose"] = Audio.CreateAudioClip("event:/Music/stingers/game_lose"); + var winAudio = Audio.CreateAudioClip("event:/Music/stingers/game_win"); + winAudio.DestroyOnSceneExit = false; + AudioHandler.audioClipHandlers["BGMWin"] = winAudio; + + var loseAudio = Audio.CreateAudioClip("event:/Music/stingers/game_lose"); + loseAudio.DestroyOnSceneExit = false; + AudioHandler.audioClipHandlers["BGMLose"] = loseAudio; goingToWin = false; goingToLose = false; 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/AudioSystem/SHAudioSystem.cpp b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp index 724c31a0..749f6180 100644 --- a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp +++ b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp @@ -822,6 +822,16 @@ namespace SHADE instance->setVolume(volume); } + bool AudioClip::GetDestroyOnSceneExit() + { + return destroyOnSceneExit; + } + + void AudioClip::SetDestroyOnSceneExit(bool value) + { + destroyOnSceneExit = value; + } + SHEventHandle SHAudioSystem::onStop(SHEventPtr onStopEvent) { StopAllSounds(); @@ -841,8 +851,11 @@ namespace SHADE auto [begin, end] = audioClipLibrary.GetDenseAccess(); for (auto& it = begin; it != end; ++it) { - it->instance->stop(FMOD_STUDIO_STOP_ALLOWFADEOUT); - it->instance->release(); + if(it->destroyOnSceneExit) + { + it->instance->stop(FMOD_STUDIO_STOP_ALLOWFADEOUT); + it->instance->release(); + } } return onSceneExitEvent->handle; diff --git a/SHADE_Engine/src/AudioSystem/SHAudioSystem.h b/SHADE_Engine/src/AudioSystem/SHAudioSystem.h index 7e2fac11..1c285b62 100644 --- a/SHADE_Engine/src/AudioSystem/SHAudioSystem.h +++ b/SHADE_Engine/src/AudioSystem/SHAudioSystem.h @@ -37,10 +37,13 @@ namespace SHADE float GetParameterValue(const char* paramName); float GetVolume(); void SetVolume(float volume); + bool GetDestroyOnSceneExit(); + void SetDestroyOnSceneExit(bool value); friend class SHAudioSystem; private: FMOD::Studio::EventInstance* instance = nullptr; EntityID transformRef = MAX_EID; + bool destroyOnSceneExit = true; }; class SH_API SHAudioSystem : public SHSystem 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 f923add4..dab94c5d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderingSubSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderingSubSystem.cpp @@ -1,5 +1,6 @@ #include "SHpch.h" #include "SHTrajectoryRenderingSubSystem.h" + #include "ECS_Base/Managers/SHComponentManager.h" #include "Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h" #include "Graphics/Devices/SHVkLogicalDevice.h" @@ -13,6 +14,7 @@ #include "Graphics/MiddleEnd/Interface/SHRenderer.h" #include "Physics/System/SHPhysicsSystem.h" #include "ECS_Base/Managers/SHSystemManager.h" +#include namespace SHADE { @@ -85,20 +87,36 @@ namespace SHADE { std::vector positions{}; std::vector quats{}; - physicsSystem->SimulateBody - (positions, quats, - SHPhysicsSystem::SimulateBodyInfo - { - .bodyEID = entityToSimulate, - .force = comp.GetSimulationForce(), - .continuousForce = false, + + auto* rigidBodyComp = SHComponentManager::GetComponent_s(entityToSimulate); + if (!rigidBodyComp) + continue; + + SHGhostBody defaultGhostBody{*rigidBodyComp}; + + defaultGhostBody.mass = comp.GetObjectMass(); + + SHPhysicsSystem::SimulateBodyInfo simulateInfo + { + .bodyEID = entityToSimulate, + .force = comp.GetSimulationForce(), + .continuousForce = false, .timeStep = comp.GetSimulationTimestep(), .maxSteps = static_cast(comp.GetSimulationMaxSteps()), - } - ); + }; + + SHPhysicsSystem::SimulateBodyOutput output + { + .positions = &positions + , .orientations = &quats + }; + + physicsSystem->SimulateBody(defaultGhostBody, simulateInfo, output); comp.ResetSimulationInfo(); + std::cout << positions.size() << std::endl; + // If has positions, feed data to buffer. if (!positions.empty()) { @@ -198,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/src/Physics/Interface/SHRigidBodyComponent.cpp b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp index b26f89d9..1017098f 100644 --- a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp +++ b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp @@ -150,6 +150,16 @@ namespace SHADE return rigidBody ? SHQuaternion{ rigidBody->getTransform().getOrientation() }.ToEuler() : SHVec3::Zero; } + SHVec3 SHRigidBodyComponent::GetLocalInertia() const noexcept + { + return rigidBody ? SHVec3{ rigidBody->getLocalInertiaTensor() } : SHVec3::Zero; + } + + SHVec3 SHRigidBodyComponent::GetLocalCentroid() const noexcept + { + return rigidBody ? SHVec3{ rigidBody->getLocalCenterOfMass() } : SHVec3::Zero; + } + /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h index a1cbe786..8f5fe3d4 100644 --- a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h +++ b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h @@ -103,6 +103,9 @@ namespace SHADE [[nodiscard]] SHVec3 GetPosition () const noexcept; [[nodiscard]] SHVec3 GetRotation () const noexcept; + [[nodiscard]] SHVec3 GetLocalInertia () const noexcept; + [[nodiscard]] SHVec3 GetLocalCentroid () const noexcept; + /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/System/SHGhostBody.cpp b/SHADE_Engine/src/Physics/System/SHGhostBody.cpp new file mode 100644 index 00000000..2a5cff3a --- /dev/null +++ b/SHADE_Engine/src/Physics/System/SHGhostBody.cpp @@ -0,0 +1,51 @@ +/**************************************************************************************** + * \file SHGhostBody.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for the Ghost Body meant for Simulation. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHGhostBody.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHGhostBody::SHGhostBody(const SHRigidBodyComponent& rigidBody) noexcept + : linearVelocity { rigidBody.GetLinearVelocity() } + , angularVelocity { rigidBody.GetAngularVelocity() } + , localCentroid { rigidBody.GetLocalCentroid() } + , accumulatedForce { rigidBody.GetForce() } + , accumulatedTorque{ rigidBody.GetTorque() } + , gravityScale { rigidBody.GetGravityScale() } + , mass { rigidBody.GetMass() } + , drag { rigidBody.GetDrag() } + , angularDrag { rigidBody.GetAngularDrag() } + , position { rigidBody.GetPosition() } + , orientation { SHQuaternion::FromEuler(rigidBody.GetRotation()) } + , useGravity { rigidBody.IsGravityEnabled() } + { + const SHVec3 LOCAL_INERTIA = rigidBody.GetLocalInertia(); + localInvInertia.x = 1.0f / LOCAL_INERTIA.x; + localInvInertia.y = 1.0f / LOCAL_INERTIA.y; + localInvInertia.z = 1.0f / LOCAL_INERTIA.z; + + 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() ? 0.0f : 1.0f; + angularLock.y = rigidBody.GetFreezeRotationY() ? 0.0f : 1.0f; + angularLock.z = rigidBody.GetFreezeRotationZ() ? 0.0f : 1.0f; + } + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHGhostBody.h b/SHADE_Engine/src/Physics/System/SHGhostBody.h new file mode 100644 index 00000000..ebc2b0db --- /dev/null +++ b/SHADE_Engine/src/Physics/System/SHGhostBody.h @@ -0,0 +1,64 @@ +/**************************************************************************************** + * \file SHGhostBody.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for the Ghost Body meant for Simulation. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "Physics/Interface/SHRigidBodyComponent.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates a rigid body that will be simulated in the world, but doesn't actually + * exist in the world. + */ + struct SHGhostBody + { + public: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + SHVec3 linearVelocity = SHVec3::Zero; + SHVec3 angularVelocity = SHVec3::Zero; + + SHVec3 localInvInertia = SHVec3::One; + SHVec3 localCentroid = SHVec3::Zero; + SHVec3 accumulatedForce = SHVec3::Zero; + SHVec3 accumulatedTorque = SHVec3::Zero; + + SHVec3 linearLock = SHVec3::One; + SHVec3 angularLock = SHVec3::One; + + float gravityScale = 1.0f; + float mass = 1.0f; + float drag = 0.01f; + float angularDrag = 0.01f; + + SHVec3 position = SHVec3::Zero; + SHQuaternion orientation = SHQuaternion::Identity; + bool useGravity = true; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHGhostBody () noexcept = default; + SHGhostBody (const SHRigidBodyComponent& rigidBody) noexcept; + + }; + + +} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index 2e5f5c2c..32d6f03e 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -24,6 +24,7 @@ #include "Physics/Interface/SHColliderComponent.h" #include "Scene/SHSceneManager.h" #include "Scripting/SHScriptEngine.h" +#include "SHGhostBody.h" namespace SHADE { @@ -236,46 +237,22 @@ namespace SHADE return IS_COLLIDING; } - void SHPhysicsSystem::SimulateBody(std::vector& positions, std::vector& orientations, const SimulateBodyInfo& simInfo) + void SHPhysicsSystem::SimulateBody(SHGhostBody& ghostBody, SimulateBodyInfo& simInfo, SimulateBodyOutput& output) { // Check for a valid rigidbody const auto* rigidBody = SHComponentManager::GetComponent_s(simInfo.bodyEID); if (!rigidBody) { - SHLOG_WARNING("Entity {} does not have a rigid body to simulate!", simInfo.bodyEID) - return; - } - - // Ignore non-dynamic bodies - if (rigidBody->type != SHRigidBodyComponent::Type::DYNAMIC) - { - SHLOG_WARNING("Entity {} is not a dynamic body. We cannot simulate non-dynamic bodies!", simInfo.bodyEID) - return; + SHLOG_WARNING("Entity {} does not have a rigid body to simulate! This body will collide with everything!", simInfo.bodyEID) } // Prepare simulation info (I'm basically declaring an entire body here) - SHVec3 bodyPosition = rigidBody->GetPosition(); - SHQuaternion bodyOrientation = SHQuaternion::FromEuler(rigidBody->GetRotation()); - SHVec3 linearVelocity = rigidBody->GetLinearVelocity(); - SHVec3 angularVelocity = rigidBody->GetAngularVelocity(); - float invMass = 1.0f / rigidBody->GetMass(); - SHVec3 localInvInertia = rigidBody->rigidBody->getLocalInertiaTensor(); - SHVec3 worldInvInertia = SHVec3::One; - SHVec3 localCentroid = rigidBody->rigidBody->getLocalCenterOfMass(); - SHVec3 worldCentroid = SHVec3::One; - SHVec3 appliedForce = simInfo.force; - SHVec3 appliedTorque = simInfo.torque; - SHVec3 accumulatedForce = SHVec3::Zero; - SHVec3 accumulatedTorque = SHVec3::Zero; + float invMass = 1.0f / ghostBody.mass; + SHVec3 worldInvInertia = SHVec3::One; + SHVec3 worldCentroid = SHVec3::One; - const SHVec3& LINEAR_LOCK = rigidBody->rigidBody->getLinearLockAxisFactor(); - const SHVec3& ANGULAR_LOCK = rigidBody->rigidBody->getAngularLockAxisFactor(); - - // Invert the inertia - for (size_t i = 0; i < SHVec3::SIZE; ++i) - localInvInertia[i] = 1.0f / localInvInertia[i]; - - + // Asserts. Don't be an idiot. + SHASSERT(invMass > 0, "GhostBody's mass in invalid") // Build raycast layer from colliders. If none exist....then this never stops simulating technically. // I'm too lazy to handle that case, so I'll just throw an error. @@ -302,24 +279,24 @@ namespace SHADE int iterationCounter = simInfo.maxSteps; do { - raycastInfo.distance = linearVelocity.Length(); - raycastInfo.ray.position = bodyPosition; - raycastInfo.ray.direction = SHVec3::Normalise(linearVelocity); + raycastInfo.distance = ghostBody.linearVelocity.Length() * simInfo.timeStep; // Do not take the entire velocity's length as that is for an entire second. + raycastInfo.ray.position = ghostBody.position; + raycastInfo.ray.direction = SHVec3::Normalise(ghostBody.linearVelocity); terminate = !Raycast(raycastInfo).empty() || iterationCounter == 0; if (terminate) return; // Compute world space data - const SHMatrix R = SHMatrix::Rotate(bodyOrientation); + const SHMatrix R = SHMatrix::Rotate(ghostBody.orientation); const SHMatrix RT = SHMatrix::Transpose(R); SHMatrix localInertiaTensor = SHMatrix::Identity; // Set the diagonals - localInertiaTensor.m[0][0] = localInvInertia.x; - localInertiaTensor.m[1][1] = localInvInertia.y; - localInertiaTensor.m[2][2] = localInvInertia.z; + localInertiaTensor.m[0][0] = ghostBody.localInvInertia.x; + localInertiaTensor.m[1][1] = ghostBody.localInvInertia.y; + localInertiaTensor.m[2][2] = ghostBody.localInvInertia.z; localInertiaTensor *= RT; const SHVec3 DIAGONALS { localInertiaTensor.m[0][0], localInertiaTensor.m[1][1], localInertiaTensor.m[2][2] }; @@ -327,42 +304,47 @@ namespace SHADE worldInvInertia = R * DIAGONALS; // Compute world centroid - worldCentroid = (R * localCentroid) + bodyPosition; + worldCentroid = (R * ghostBody.localCentroid) + ghostBody.position; // Apply forces - accumulatedForce += appliedForce; - angularVelocity += worldInvInertia * SHVec3::Cross(bodyPosition + simInfo.forceOffset, simInfo.force); - accumulatedTorque += appliedTorque; + ghostBody.accumulatedForce += simInfo.force; + ghostBody.angularVelocity += worldInvInertia * SHVec3::Cross(ghostBody.position + simInfo.forceOffset, simInfo.force); + ghostBody.accumulatedTorque += simInfo.torque; // Integrate Velocities // Integrate forces and gravity into linear velocity - const SHVec3 LINEAR_ACCELERATION = accumulatedForce * invMass; - const SHVec3 GRAVITATIONAL_ACCELERATION = rigidBody->IsGravityEnabled() ? worldState.settings.gravity * rigidBody->GetGravityScale() : SHVec3::Zero; + const SHVec3 LINEAR_ACCELERATION = ghostBody.accumulatedForce * invMass; + const SHVec3 GRAVITATIONAL_ACCELERATION = ghostBody.gravityScale ? worldState.settings.gravity * ghostBody.gravityScale : SHVec3::Zero; - linearVelocity += (LINEAR_ACCELERATION + GRAVITATIONAL_ACCELERATION) * simInfo.timeStep * LINEAR_LOCK; - angularVelocity += worldInvInertia * (accumulatedTorque * simInfo.timeStep); + ghostBody.linearVelocity += (LINEAR_ACCELERATION + GRAVITATIONAL_ACCELERATION) * simInfo.timeStep * ghostBody.linearLock; + ghostBody.angularVelocity += worldInvInertia * (ghostBody.accumulatedTorque * simInfo.timeStep); // Apply drag (exponentially applied) - linearVelocity *= 1.0f / (1.0f + simInfo.timeStep * rigidBody->drag); - angularVelocity *= 1.0f / (1.0f + simInfo.timeStep * rigidBody->angularDrag); + ghostBody.linearVelocity *= 1.0f / (1.0f + simInfo.timeStep * ghostBody.drag); + ghostBody.angularVelocity *= 1.0f / (1.0f + simInfo.timeStep * ghostBody.angularDrag); // Integrate Positions & Orientations - const SHQuaternion QV = SHQuaternion{ angularVelocity.x * simInfo.timeStep, angularVelocity.y * simInfo.timeStep, angularVelocity.z * simInfo.timeStep, 0.0f } * 0.5f; + const SHQuaternion QV = SHQuaternion{ ghostBody.angularVelocity.x * simInfo.timeStep, ghostBody.angularVelocity.y * simInfo.timeStep, ghostBody.angularVelocity.z * simInfo.timeStep, 0.0f } * 0.5f; - bodyPosition += linearVelocity * simInfo.timeStep; - bodyOrientation += bodyOrientation * QV * SHQuaternion::FromEuler(ANGULAR_LOCK); - bodyOrientation = SHQuaternion::Normalise(bodyOrientation); + ghostBody.position += ghostBody.linearVelocity * simInfo.timeStep; + ghostBody.orientation += ghostBody.orientation * QV * SHQuaternion::FromEuler(ghostBody.angularLock); + ghostBody.orientation = SHQuaternion::Normalise(ghostBody.orientation); + + // Clear forces + ghostBody.accumulatedForce = SHVec3::Zero; + ghostBody.accumulatedTorque = SHVec3::Zero; - // Clear forces after the first frame if (!simInfo.continuousForce) { - accumulatedForce = SHVec3::Zero; - accumulatedTorque = SHVec3::Zero; - appliedForce = SHVec3::Zero; - appliedTorque = SHVec3::Zero; + simInfo.force = SHVec3::Zero; + simInfo.torque = SHVec3::Zero; } - positions.emplace_back(bodyPosition); + if (output.positions) + output.positions->emplace_back(ghostBody.position); + + if (output.orientations) + output.orientations->emplace_back(ghostBody.orientation); --iterationCounter; diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index f466481d..c81bf3a9 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -28,6 +28,7 @@ namespace SHADE { + struct SHGhostBody; /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -51,40 +52,54 @@ namespace SHADE /** * @brief * Used to simulate the motion of a rigid body, ignoring collision detection and response. - * @param bodyEID - * The EntityID of the Rigid Body to simulate. - * @param force - * The force applied onto the Rigid Body. - * @param forceOffset - * The position to apply the force onto the body relative to it's local Center of Mass. - * @param torque - * The torque to apply onto the Rigid Body. - * @param continuousForce - * If the force should be applied every step throughout the simulation. Defaults to false.
- * True : The force indicated is added to the body every step, therefore it has constant acceleration. - * False: The force is applied only in the first step, therefore it has constant speed. - * @param timeStep - * The timestep for each step of the simulation. Defaults to 0.016s (The default Fixed DT) - * @param maxSteps - * The number of steps to run the simulation for. Defaults to -1. - * < 0 : Runs until the object may hit something. Hit detection is done through raycasting. - * = 0 : Runs only the current step and checks if it may hit something. - * > 0 : Runs for the given number of steps or until it may hit something. */ struct SimulateBodyInfo { + public: + + // The EntityID of the Actual Rigid Body to simulate. If none is passed it, + // the Ghost Body will attempt to collide with everything. EntityID bodyEID = MAX_EID; + // The force applied onto the Ghost Body. SHVec3 force = SHVec3::Zero; + + // The position where the force is applied offset from the local centroid. SHVec3 forceOffset = SHVec3::Zero; + + // The torque to apply onto the Ghost Body. SHVec3 torque = SHVec3::Zero; - // Whether or not to clear the force after the first iteration + /* + If the force should be applied every step throughout the simulation. Defaults to false. + True : The force indicated is added to the body every step, therefore it has constant acceleration. + False: The force is applied only in the first step, therefore it has constant speed. + */ bool continuousForce = false; + + // The timestep for each step of the simulation. Defaults to 0.016s (The default Fixed DT) float timeStep = static_cast(SHPhysicsConstants::DEFAULT_FIXED_DT); + + /* + The number of steps to run the simulation for. Defaults to -1. + < 0 : Runs until the object may hit something. Hit detection is done through raycasting. + = 0 : Runs only the current step and checks if it may hit something. + > 0 : Runs for the given number of steps or until it may hit something. + */ int maxSteps = -1; }; + /** + * @brief + * Contains the output for the simulate body method. + */ + struct SimulateBodyOutput + { + public: + std::vector* positions = nullptr; + std::vector* orientations = nullptr; + }; + /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ @@ -158,15 +173,16 @@ namespace SHADE /** * @brief - * Simulates the motion of a body until it collides with something. - * @param positions - * The output vector for the position of the body in each timestep. - * @param orientations - * The output vector for the orientations of the body in each timestep. + * Simulates a non-existent body in the physics world. + * The simulation will run based on the information passed in. + * @param ghostBody + * The definition of the body passed in. * @param simInfo - * The information for simulating the body. + * The information for how the simulation will run. + * @param output + * The transform results for position and orientations. */ - void SimulateBody(std::vector& positions, std::vector& orientations, const SimulateBodyInfo& simInfo); + void SimulateBody(SHGhostBody& ghostBody, SimulateBodyInfo& simInfo, SimulateBodyOutput& output); /*---------------------------------------------------------------------------------*/ /* System Routines */ 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/Audio/AudioClip.cxx b/SHADE_Managed/src/Audio/AudioClip.cxx index 062b543e..c4cad5cf 100644 --- a/SHADE_Managed/src/Audio/AudioClip.cxx +++ b/SHADE_Managed/src/Audio/AudioClip.cxx @@ -44,6 +44,16 @@ namespace SHADE return SHResourceManagerInterface::GetAssetID(Convert::ToNative(audioClipInstHandle)).value_or(INVALID_ASSET_ID); } + bool AudioClipHandler::DestroyOnSceneExit::get() + { + return NativeObject->GetDestroyOnSceneExit(); + } + + void AudioClipHandler::DestroyOnSceneExit::set(bool value) + { + NativeObject->SetDestroyOnSceneExit(value); + } + /*---------------------------------------------------------------------------------*/ /* Constructors/Destructor */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Audio/AudioClip.hxx b/SHADE_Managed/src/Audio/AudioClip.hxx index 317c5bad..7044041f 100644 --- a/SHADE_Managed/src/Audio/AudioClip.hxx +++ b/SHADE_Managed/src/Audio/AudioClip.hxx @@ -64,6 +64,12 @@ namespace SHADE AudioClipHandler(Handle audioclip); public: + + property bool DestroyOnSceneExit + { + bool get(); + void set(bool value); + } //to comment ltr void Play(); 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); }; }