Merge branch 'main' into PlayerController

This commit is contained in:
Glence 2023-03-20 17:34:26 +08:00
commit 66986e179b
56 changed files with 1638 additions and 235 deletions

View File

@ -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;

View File

@ -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;
}

Binary file not shown.

View File

@ -0,0 +1,3 @@
Name: ParticleEmit_CS
ID: 49959611
Type: 2

View File

@ -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;
}

Binary file not shown.

View File

@ -0,0 +1,3 @@
Name: ParticleUpdateRandomAcc_CS
ID: 35838633
Type: 2

View File

@ -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,7 +73,7 @@ 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;
};
@ -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;
}

Binary file not shown.

View File

@ -0,0 +1,3 @@
Name: ParticleUpdate_CS
ID: 36260925
Type: 2

View File

@ -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;
}

Binary file not shown.

View File

@ -0,0 +1,3 @@
Name: Particle_FS
ID: 42509714
Type: 2

View File

@ -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);
}

Binary file not shown.

View File

@ -0,0 +1,3 @@
Name: Particle_VS
ID: 35035037
Type: 2

View File

@ -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;

Binary file not shown.

View File

@ -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();
@ -840,10 +850,13 @@ namespace SHADE
{
auto [begin, end] = audioClipLibrary.GetDenseAccess();
for (auto& it = begin; it != end; ++it)
{
if(it->destroyOnSceneExit)
{
it->instance->stop(FMOD_STUDIO_STOP_ALLOWFADEOUT);
it->instance->release();
}
}
return onSceneExitEvent->handle;
}

View File

@ -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

View File

@ -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"

View File

@ -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<SHGraphicsSystem>();
ImGui::PushID(SHFamilyID<SHComponent>::GetID<SHParticleEmitterComponent>());
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<AssetID>(SHDragDrop::DRAG_RESOURCE))
{
Handle<SHTexture> texture = SHResourceManager::LoadOrGet<SHTexture>(*payload);
gfxSystem->BuildTextures();
if (texture)
{
component->SetTextureIndex(texture->TextureArrayIndex);
component->SetTextureAssetID(*payload);
}
else
{
SHLOG_WARNING("[] Attempted to load invalid texture! Texture for particle not set. ");
}
SHDragDrop::EndTarget();
}
}
SHEditorWidgets::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<AssetID>(SHDragDrop::DRAG_RESOURCE))
{
Handle<SHVkShaderModule> shaderModule = SHResourceManager::LoadOrGet<SHVkShaderModule>(*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();
}
}

View File

@ -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<SHParticleEmitterComponent>(eid))
{
DrawComponent(particleComponent);
}
ImGui::Separator();
// Render Scripts
SHScriptEngine* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>());
@ -204,6 +209,7 @@ namespace SHADE
DrawAddComponentButton<SHToggleButtonComponent>(eid);
DrawAddComponentButton<SHSliderComponent>(eid);
DrawAddComponentButton<SHTrajectoryRenderableComponent>(eid);
DrawAddComponentButton<SHParticleEmitterComponent>(eid);
// Components that require Transforms
@ -213,6 +219,7 @@ namespace SHADE
DrawAddComponentWithEnforcedComponentButton<SHTextRenderableComponent, SHTransformComponent>(eid);
DrawAddComponentWithEnforcedComponentButton<SHAnimatorComponent, SHTransformComponent, SHRenderable>(eid);
DrawAddComponentWithEnforcedComponentButton<SHAudioListenerComponent, SHTransformComponent>(eid);
//DrawAddComponentWithEnforcedComponentButton<SHParticleEmitterComponent, SHTransformComponent>(eid);
ImGui::EndMenu();
}

View File

@ -450,7 +450,7 @@ namespace SHADE
*/
/***************************************************************************/
void SHVkCommandBuffer::DrawMultiIndirect(Handle<SHVkBuffer> indirectDrawData, uint32_t drawCount)
void SHVkCommandBuffer::DrawMultiIndirectIndexed(Handle<SHVkBuffer> 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<SHVkBuffer> 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);

View File

@ -130,7 +130,8 @@ namespace SHADE
// 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<SHVkBuffer> indirectDrawData, uint32_t drawCount);
void DrawMultiIndirectIndexed (Handle<SHVkBuffer> indirectDrawData, uint32_t drawCount);
void DrawMultiIndirect (Handle<SHVkBuffer> indirectDrawData, uint32_t drawCount, uint32_t offset = 0);
// Compute Commands
void ComputeDispatch (uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) noexcept;

View File

@ -755,7 +755,7 @@ namespace SHADE
}
}
cmdBuffer->DrawMultiIndirect(drawDataBuffer[frameIndex], static_cast<uint32_t>(drawData.size()));
cmdBuffer->DrawMultiIndirectIndexed(drawDataBuffer[frameIndex], static_cast<uint32_t>(drawData.size()));
cmdBuffer->EndLabeledSegment();
}

View File

@ -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,

View File

@ -730,7 +730,7 @@ namespace SHADE
cmdBuffer->BindVertexBuffer(COLOR_BIND_PT, batch.InstanceColorBuffer[frameIndex], 0);
// Execute draw
cmdBuffer->DrawMultiIndirect(batch.MDIBuffer[frameIndex], static_cast<uint32_t>(batch.MDIData.size()));
cmdBuffer->DrawMultiIndirectIndexed(batch.MDIBuffer[frameIndex], static_cast<uint32_t>(batch.MDIData.size()));
}
void SHDebugDrawSystem::destroyBatch(MeshBatch& batch)

View File

@ -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<SHVkShaderModule>(VS_DEFAULT);
@ -165,6 +170,10 @@ namespace SHADE
static constexpr AssetID TRAJECTORY_VS = 41042628; trajectoryVS = SHResourceManager::LoadOrGet<SHVkShaderModule>(TRAJECTORY_VS);
static constexpr AssetID TRAJECTORY_FS = 45635685; trajectoryFS = SHResourceManager::LoadOrGet<SHVkShaderModule>(TRAJECTORY_FS);
static constexpr AssetID SHADOW_BLUR_CS = 38004013; shadowMapBlurCS = SHResourceManager::LoadOrGet<SHVkShaderModule>(SHADOW_BLUR_CS);
static constexpr AssetID PARTICLE_VS = 35035037; particleVS = SHResourceManager::LoadOrGet<SHVkShaderModule>(PARTICLE_VS);
static constexpr AssetID PARTICLE_FS = 42509714; particleFS = SHResourceManager::LoadOrGet<SHVkShaderModule>(PARTICLE_FS);
static constexpr AssetID PARTICLE_EMIT_CS = 49959611; particleEmitCS = SHResourceManager::LoadOrGet<SHVkShaderModule>(PARTICLE_EMIT_CS);
static constexpr AssetID PARTICLE_UPDATE_CS = 36260925; particleUpdateCS = SHResourceManager::LoadOrGet<SHVkShaderModule>(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<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex)
// {
// particleSubSystem->Run(cmdBuffer, frameIndex);
// });
vfxSubpass->AddColorOutput("Scene");
vfxSubpass->AddDepthOutput("Depth Buffer");
vfxSubpass->AddExteriorDrawCalls([=](Handle<SHVkCommandBuffer> cmdBuffer, Handle<SHRenderer> renderer, uint32_t frameIndex)
{
trajectoryRenderingSubSystem->Render(cmdBuffer, renderer, frameIndex);
particleSubSystem->Render(cmdBuffer, renderer, frameIndex);
});
/*-----------------------------------------------------------------------*/
@ -490,6 +504,12 @@ namespace SHADE
trajectoryRenderingSubSystem = resourceManager.Create<SHTrajectoryRenderingSubSystem>();
auto vfxPass = renderGraph->GetNode(SHGraphicsConstants::RenderGraphEntityNames::VFX_PASS.data());
// particle sub system initialization
particleSubSystem = resourceManager.Create<SHParticleSubSystem>();
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);

View File

@ -59,6 +59,7 @@ namespace SHADE
class SHMaterialInstance;
class SHMousePickSystem;
class SHTextRenderingSubSystem;
class SHParticleSubSystem;
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
@ -484,6 +485,10 @@ namespace SHADE
Handle<SHVkShaderModule> trajectoryVS;
Handle<SHVkShaderModule> trajectoryFS;
Handle<SHVkShaderModule> shadowMapBlurCS;
Handle<SHVkShaderModule> particleVS;
Handle<SHVkShaderModule> particleFS;
Handle<SHVkShaderModule> particleEmitCS;
Handle<SHVkShaderModule> particleUpdateCS;
// Fonts
Handle<SHFont> testFont;
@ -523,6 +528,7 @@ namespace SHADE
Handle<SHLightingSubSystem> lightingSubSystem;
Handle<SHTextRenderingSubSystem> textRenderingSubSystem;
Handle<SHTrajectoryRenderingSubSystem> trajectoryRenderingSubSystem;
Handle<SHParticleSubSystem> particleSubSystem;
Handle<SHSSAO> ssaoStorage;
uint32_t resizeWidth = 1;

View File

@ -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<SHVkShaderModule> 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<SHVkShaderModule> SHParticleEmitterComponent::GetCustomUpdateShader(void) const noexcept
{
return customUpdateShader;
}
}

View File

@ -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 <Assets/SHAssetMacros.h>
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<SHVkDescriptorSetGroup> particleDescriptorSet;
//! Custom update shader for the particles in this component
Handle<SHVkShaderModule> customUpdateShader;
//! Internally the system will bind this pipeline when it detects that this is not a null handle
Handle<SHVkPipeline> 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<std::array<uint32_t, 5>, 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<SHVkShaderModule> 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<SHVkShaderModule> 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;
};

View File

@ -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<uint32_t>(sizeof(vk::DrawIndirectCommand));
uint32_t sizeofUint = static_cast<uint32_t>(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<uint32_t>(sizeof(uint32_t));
uint32_t sizeofIndirectCmdAligned = logicalDevice->PadSSBOSize(sizeof(vk::DrawIndirectCommand));
uint32_t emitterStructAligned = logicalDevice->PadSSBOSize(sizeof (SHParticleEmitterComponent::GPUEmitterStruct));
uint32_t particleChunkStructAligned = logicalDevice->PadSSBOSize(sizeof (SHParticleEmitterComponent::GPUParticleStruct)) * comp.maxParticles;
uint32_t 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<uint32_t> freelistInit(comp.maxParticles + 1, 0);
freelistInit[0] = comp.maxParticles;
for (uint32_t i = 0; i < comp.maxParticles; ++i)
freelistInit[i + 1] = i;
// Particle emitter buffer. Multiple copies, Host-visible mapped. We want multiple copies because we'll be writing to it from the CPU. We don't want to do that while the GPU
// is using it during compute operations so we write to another portion.
@ -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<vk::DrawIndirectCommand, SHGraphicsConstants::NUM_FRAME_BUFFERS> 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<SHVkCommandBuffer> cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept
{
cmdBuffer->DrawMultiIndirect(comp.drawCallData, 1, static_cast<uint32_t>(sizeof(vk::DrawIndirectCommand)) * frameIndex);
//cmdBuffer->DrawArrays(4, 1, 0, 0);
}
void SHParticleSubSystem::PreparePrePostUpdateBarriers(std::vector<vk::BufferMemoryBarrier>& preUpdateBarriers, std::vector<vk::BufferMemoryBarrier>& 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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(sizeof(vk::DrawIndirectCommand))
};
// ...copy assign barriers on heap
postUpdateBarriers.push_back(outputParticleDataBarrierPostUpdate);
postUpdateBarriers.push_back(indicesDataBarrierPostUpdate);
postUpdateBarriers.push_back(drawDataBarrierPostUpdate);
}
Handle<SHVkPipeline> SHParticleSubSystem::GetCustomUpdatePipeline(Handle<SHVkShaderModule> 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<SHVkLogicalDevice> device, Handle<SHVkDescriptorPool> inDescPool, Handle<SHVkRenderpass> compatibleRenderpass, Handle<SHSubpass> subpass, Handle<SHVkShaderModule> VS, Handle<SHVkShaderModule> FS, Handle<SHVkShaderModule> emitCS, Handle<SHVkShaderModule> 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<SHParticleEmitterComponent>();
}
void SHParticleSubSystem::Run(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex, float dt) noexcept
void SHParticleSubSystem::Run(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex, Handle<SHVkFence> waitFence) noexcept
{
float dt = static_cast<float>(SHFrameRateController::GetRawDeltaTime());
auto& emitters = SHComponentManager::GetDense<SHParticleEmitterComponent>();
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<vk::BufferMemoryBarrier> 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<vk::BufferMemoryBarrier> postUpdateBarriers{};
postUpdateBarriers.reserve(emitters.size() * 3);
std::vector<vk::BufferMemoryBarrier> 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,7 +387,13 @@ 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.isActive)
{
if (!emitter.initialized)
InitializeComponent(emitter);
@ -255,51 +424,97 @@ namespace SHADE
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;
// prepare barriers
PreparePrePostUpdateBarriers(preUpdateBarriers, postUpdateBarriers, emitter, i, frameIndex);
// Emitter will emit once and stop emitting next frame (unless specified before reaching here to do so).
emitter.toEmit = false;
}
++i;
}
// issue the barrier to wait
cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::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.... */
/*-----------------------------------------------------------------------*/
// bind the pipeline for updating
cmdBuffer->BindPipeline(defaultUpdatePipelineData.pipeline);
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 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<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex) noexcept
{
auto& emitters = SHComponentManager::GetDense<SHParticleEmitterComponent>();
for (auto& emitter : emitters)
{
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<SHVkCommandBuffer> cmdBuffer, Handle<SHRenderer> renderer, uint32_t frameIndex) noexcept
{
auto& emitters = SHComponentManager::GetDense<SHParticleEmitterComponent>();
auto const& mappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::PARTICLE_RENEDERING);
// bind the pipeline for updating
cmdBuffer->BindPipeline(renderingPipelineData.pipeline);
// TODO: Issue barrier for output particle data. Semaphore should also be issued outside in SHGraphicsSystem
for (auto& emitter : emitters)
{
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
{

View File

@ -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<SHVkPipeline> customPipeline;
Handle<SHVkPipelineLayout> customPipelineLayout;
};
// To hold data for a pipeline and pipeline layout.
// We want this here because particles require 3 pipeline sets:
@ -75,16 +82,23 @@ namespace SHADE
//! Desc pool for particle component desc set allocation
Handle<SHVkDescriptorPool> descPool;
std::unordered_map<Handle<SHVkShaderModule>, CustomPipeline> customUpdatePipelineCache;
void InitializeComponent (SHParticleEmitterComponent& comp) noexcept;
void EmitComponent (Handle<SHVkCommandBuffer> cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept;
void UpdateCompoennt(Handle<SHVkCommandBuffer> cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept;
void RenderComponent(Handle<SHVkCommandBuffer> cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept;
void PreparePrePostUpdateBarriers (std::vector<vk::BufferMemoryBarrier>& preUpdateBarriers, std::vector<vk::BufferMemoryBarrier>& postUpdateBarriers, SHParticleEmitterComponent const& emitter, uint32_t const EMITTER_INDEX, uint32_t const FRAME_INDEX) noexcept;
Handle<SHVkPipeline> GetCustomUpdatePipeline (Handle<SHVkShaderModule> customUpdateShader) noexcept;
public:
void Init(Handle<SHVkLogicalDevice> device, Handle<SHVkDescriptorPool> inDescPool, Handle<SHVkRenderpass> compatibleRenderpass, Handle<SHSubpass> subpass, Handle<SHVkShaderModule> VS, Handle<SHVkShaderModule> FS, Handle<SHVkShaderModule> emitCS, Handle<SHVkShaderModule> defaultUpdateCS) noexcept;
void Run(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex, float dt) noexcept;
void Run(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex, Handle<SHVkFence> waitFence = {}) noexcept;
void ResetInstanceCounts (Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex) noexcept;
void Render(Handle<SHVkCommandBuffer> cmdBuffer, Handle<SHRenderer> renderer, uint32_t frameIndex) noexcept;
void Exit(void) noexcept;

View File

@ -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<float>(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<float>(NUM_SAMPLES);
float scale = static_cast<float>(i) / static_cast<float>(NUM_SAMPLES);
scale = std::lerp(0.1f, 1.0f, scale * scale);
samples[i] *= scale;
}

View File

@ -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;

View File

@ -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()

View File

@ -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 <Physics/System/SHGhostBody.h>
namespace SHADE
{
@ -85,20 +87,36 @@ namespace SHADE
{
std::vector<SHVec3> positions{};
std::vector<SHQuaternion> quats{};
physicsSystem->SimulateBody
(positions, quats,
SHPhysicsSystem::SimulateBodyInfo
auto* rigidBodyComp = SHComponentManager::GetComponent_s<SHRigidBodyComponent>(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<int>(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();

View File

@ -299,6 +299,8 @@ namespace SHADE
, created{ false }
{
if (pipelineLayout)
pipelineLayout->AddCallback([this]() {ConstructPipeline(); });
}

View File

@ -372,6 +372,7 @@ namespace SHADE
, ISelfHandle<SHRenderGraphNode>{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<SHVkCommandBuffer> commandBuffer, Handle<SHVkDescriptorPool> descPool, uint32_t frameIndex) noexcept
{
for (auto& func : preBeginFuncs)
{
func(commandBuffer, frameIndex);
}
if (renderpass)
{
uint32_t framebufferIndex = (framebuffers.size() > 1) ? frameIndex : 0;

View File

@ -27,6 +27,10 @@ namespace SHADE
class SH_API SHRenderGraphNode : public ISelfHandle<SHRenderGraphNode>
{
public:
using PreBeginFunction = std::function<void(Handle<SHVkCommandBuffer>, uint32_t)>;
private:
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
@ -74,6 +78,9 @@ namespace SHADE
//! of drawing objects on the image (i.e. compute).
std::vector<Handle<SHRenderGraphNodeCompute>> nodeComputes;
//! Calls before renderpass begins
std::vector<PreBeginFunction> preBeginFuncs;
//! Whether or not the node has finished execution
bool executed;
@ -118,6 +125,7 @@ namespace SHADE
/*-----------------------------------------------------------------------*/
Handle<SHSubpass> AddSubpass(std::string subpassName, Handle<SHViewport> viewport, Handle<SHRenderer> renderer) noexcept;
Handle<SHRenderGraphNodeCompute> AddNodeCompute(std::string nodeName, Handle<SHVkShaderModule> computeShaderModule, std::initializer_list<std::string> resources, std::unordered_set<BindingAndSetHash>&& dynamicBufferBindings = {}, uint32_t variableDescCount = 0, float numWorkGroupScale = 1.0f) noexcept;
void AddPreBeginFunction (PreBeginFunction const& func) noexcept;
void Execute(Handle<SHVkCommandBuffer> commandBuffer, Handle<SHVkDescriptorPool> descPool, uint32_t frameIndex) noexcept;
Handle<SHVkPipeline> GetOrCreatePipeline(std::pair<Handle<SHVkShaderModule>, Handle<SHVkShaderModule>> const& vsFsPair, Handle<SHSubpass> subpass) noexcept;

View File

@ -94,8 +94,8 @@ namespace SHADE
Recompile();
//for (auto& callback : onChangeCallbacks)
// callback();
for (auto& callback : onChangeCallbacks)
callback();
}
void SHVkShaderModule::AddCallback(ChangeCallback&& callback) noexcept

View File

@ -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<uint32_t>::max()) noexcept;
void Reset (void) noexcept;
/*-----------------------------------------------------------------------*/

View File

@ -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 */
/*-----------------------------------------------------------------------------------*/

View File

@ -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 */
/*---------------------------------------------------------------------------------*/

View File

@ -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 <SHpch.h>
// 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

View File

@ -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

View File

@ -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<SHVec3>& positions, std::vector<SHQuaternion>& orientations, const SimulateBodyInfo& simInfo)
void SHPhysicsSystem::SimulateBody(SHGhostBody& ghostBody, SimulateBodyInfo& simInfo, SimulateBodyOutput& output)
{
// Check for a valid rigidbody
const auto* rigidBody = SHComponentManager::GetComponent_s<SHRigidBodyComponent>(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();
float invMass = 1.0f / ghostBody.mass;
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;
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;

View File

@ -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. <br/>
* 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<float>(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<SHVec3>* positions = nullptr;
std::vector<SHQuaternion>* 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<SHVec3>& positions, std::vector<SHQuaternion>& orientations, const SimulateBodyInfo& simInfo);
void SimulateBody(SHGhostBody& ghostBody, SimulateBodyInfo& simInfo, SimulateBodyOutput& output);
/*---------------------------------------------------------------------------------*/
/* System Routines */

View File

@ -248,6 +248,7 @@ namespace SHADE
AddComponentToComponentNode<SHUIComponent>(components, eid);
AddComponentToComponentNode<SHAudioListenerComponent>(components, eid);
AddComponentToComponentNode<SHTrajectoryRenderableComponent>(components, eid);
AddComponentToComponentNode<SHParticleEmitterComponent>(components, eid);
node[ComponentsNode] = components;
@ -310,6 +311,7 @@ namespace SHADE
AddComponentID<SHUIComponent>(componentIDList, componentsNode);
AddComponentID<SHAudioListenerComponent>(componentIDList, componentsNode);
AddComponentID<SHTrajectoryRenderableComponent>(componentIDList, componentsNode);
AddComponentID<SHParticleEmitterComponent>(componentIDList, componentsNode);
return componentIDList;
}
@ -398,5 +400,6 @@ namespace SHADE
SHSerializationHelper::InitializeComponentFromNode<SHUIComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHAudioListenerComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHTrajectoryRenderableComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHParticleEmitterComponent>(componentsNode, eid);
}
}

View File

@ -17,6 +17,7 @@
#include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h"
#include "Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h"
#include "Physics/Collision/Shapes/SHCapsule.h"
#include <Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h>
namespace YAML
{
@ -35,6 +36,9 @@ namespace YAML
struct HasYAMLConv<SHAnimatorComponent> : std::true_type {};
template<>
struct HasYAMLConv<SHTrajectoryRenderableComponent> : std::true_type {};
template<>
struct HasYAMLConv<SHParticleEmitterComponent> : std::true_type {};
template<>
struct convert<SHVec4>
@ -497,4 +501,120 @@ namespace YAML
}
};
template<>
struct convert<SHParticleEmitterComponent>
{
static constexpr std::string_view EMISSION_COUNT_TAG = "Emission Count";
static constexpr std::string_view IS_PASSIVE_TAG = "Is Passive";
static constexpr std::string_view EMISSION_INTERVAL_TAG = "Emission Interval";
static constexpr std::string_view MIN_LIFE_TAG = "Min Life";
static constexpr std::string_view MAX_LIFE_TAG = "Max Life";
static constexpr std::string_view ANGULAR_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<SHGraphicsSystem>();
if (node[EMISSION_COUNT_TAG.data()].IsDefined())
rhs.SetEmissionCount(node[EMISSION_COUNT_TAG.data()].as<uint32_t>());
if (node[IS_PASSIVE_TAG.data()].IsDefined())
rhs.SetPassive(node[IS_PASSIVE_TAG.data()].as<bool>());
if (node[EMISSION_INTERVAL_TAG.data()].IsDefined())
rhs.SetEmissionInterval(node[EMISSION_INTERVAL_TAG.data()].as<float>());
if (node[MIN_LIFE_TAG.data()].IsDefined())
rhs.SetMinLife(node[MIN_LIFE_TAG.data()].as<float>());
if (node[MAX_LIFE_TAG.data()].IsDefined())
rhs.SetMaxLife(node[MAX_LIFE_TAG.data()].as<float>());
if (node[SIZE_DECAY_TAG.data()].IsDefined())
rhs.SetSizeDecayMult(node[SIZE_DECAY_TAG.data()].as<float>());
if (node[ANGULAR_RANGES_OFFSET_TAG.data()].IsDefined())
rhs.SetAngularRangesAndOffsets(node[ANGULAR_RANGES_OFFSET_TAG.data()].as<SHVec4>());
if (node[MIN_SPEED_TAG.data()].IsDefined())
rhs.SetMinSpeed(node[MIN_SPEED_TAG.data()].as<float>());
if (node[MAX_SPEED_TAG.data()].IsDefined())
rhs.SetMaxSpeed (node[MAX_SPEED_TAG.data()].as<float>());
if (node[MAX_SIZE_TAG.data()].IsDefined())
rhs.SetMaxSize(node[MAX_SIZE_TAG.data()].as<float>());
if (node[ROTATION_SPEED_TAG.data()].IsDefined())
rhs.SetRotationSpeed(node[ROTATION_SPEED_TAG.data()].as<float>());
if (node[ROTATION_DECAY_TAG.data()].IsDefined())
rhs.SetRotationDecay(node[ROTATION_DECAY_TAG.data()].as<float>());
if (node[TEXTURE_ASSET_ID_TAG.data()].IsDefined())
{
AssetID id = node[TEXTURE_ASSET_ID_TAG.data()].as<AssetID>();
Handle<SHTexture> texture = SHResourceManager::LoadOrGet<SHTexture>(id);
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<AssetID>();
Handle<SHVkShaderModule> shaderModule = SHResourceManager::LoadOrGet<SHVkShaderModule>(id);
SHResourceManager::FinaliseChanges();
//gfxSystem->BuildTextures();
rhs.SetCustomUpdateShader(shaderModule);
rhs.SetTextureAssetID(id);
}
return true;
}
};
}

View File

@ -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 */
/*---------------------------------------------------------------------------------*/

View File

@ -65,6 +65,12 @@ namespace SHADE
public:
property bool DestroyOnSceneExit
{
bool get();
void set(bool value);
}
//to comment ltr
void Play();
void Stop(bool fadeOut);

View File

@ -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()

View File

@ -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);
};
}