Scene Changes and bug fixes #431
|
@ -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;
|
||||
|
|
|
@ -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.
|
@ -0,0 +1,3 @@
|
|||
Name: ParticleEmit_CS
|
||||
ID: 49959611
|
||||
Type: 2
|
|
@ -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.
|
@ -0,0 +1,3 @@
|
|||
Name: ParticleUpdateRandomAcc_CS
|
||||
ID: 35838633
|
||||
Type: 2
|
|
@ -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.
|
@ -0,0 +1,3 @@
|
|||
Name: ParticleUpdate_CS
|
||||
ID: 36260925
|
||||
Type: 2
|
|
@ -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.
|
@ -0,0 +1,3 @@
|
|||
Name: Particle_FS
|
||||
ID: 42509714
|
||||
Type: 2
|
|
@ -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.
|
@ -0,0 +1,3 @@
|
|||
Name: Particle_VS
|
||||
ID: 35035037
|
||||
Type: 2
|
|
@ -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.
Binary file not shown.
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -299,6 +299,8 @@ namespace SHADE
|
|||
, created{ false }
|
||||
|
||||
{
|
||||
if (pipelineLayout)
|
||||
pipelineLayout->AddCallback([this]() {ConstructPipeline(); });
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -94,8 +94,8 @@ namespace SHADE
|
|||
|
||||
Recompile();
|
||||
|
||||
//for (auto& callback : onChangeCallbacks)
|
||||
// callback();
|
||||
for (auto& callback : onChangeCallbacks)
|
||||
callback();
|
||||
}
|
||||
|
||||
void SHVkShaderModule::AddCallback(ChangeCallback&& callback) noexcept
|
||||
|
|
|
@ -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;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
|
|
@ -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 */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
|
|
@ -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 */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue