Implemented Animation Clip asset and animation controller #410

Merged
XiaoQiDigipen merged 66 commits from SP3-22-AnimationController into main 2023-03-09 16:19:40 +08:00
75 changed files with 4642 additions and 768 deletions
Showing only changes of commit 79fe9ec43d - Show all commits

View File

@ -1,4 +1,4 @@
Start in Fullscreen: false
Starting Scene ID: 86098106
Starting Scene ID: 91478134
Window Size: {x: 1920, y: 1080}
Window Title: SHADE Engine

View File

@ -0,0 +1,3 @@
Name: Master
ID: 187131295
Type: 11

View File

@ -0,0 +1,3 @@
Name: Master.strings
ID: 184993030
Type: 11

View File

@ -0,0 +1,3 @@
Name: Music
ID: 187337426
Type: 11

View File

@ -0,0 +1,3 @@
Name: SFX
ID: 200039123
Type: 11

View File

@ -0,0 +1,3 @@
Name: UI
ID: 185075145
Type: 11

View File

@ -0,0 +1,8 @@
- VertexShader: 47911992
FragmentShader: 46377769
SubPass: G-Buffer Write
Properties:
data.color: {x: 1, y: 1, z: 1, w: 1}
data.textureIndex: 57342922
data.alpha: 0
data.beta: {x: 1, y: 1, z: 1}

View File

@ -0,0 +1,3 @@
Name: AnimatedHomeowner
ID: 121518381
Type: 7

View File

@ -0,0 +1,8 @@
- VertexShader: 39210065
FragmentShader: 46377769
SubPass: G-Buffer Write
Properties:
data.color: {x: 1, y: 1, z: 1, w: 1}
data.textureIndex: 64651793
data.alpha: 0
data.beta: {x: 1, y: 1, z: 1}

View File

@ -0,0 +1,3 @@
Name: default_racoon
ID: 125722190
Type: 7

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -0,0 +1,7 @@
Name: MD_HomeownerV2
ID: 76586906
Type: 4
Sub Assets:
Name: Cube
ID: 148542784
Type: 8

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

33
Assets/Scenes/anim.shade Normal file
View File

@ -0,0 +1,33 @@
- EID: 0
Name: Default
IsActive: true
NumberOfChildren: 0
Components:
Light Component:
Position: {x: 0, y: 0, z: 0}
Type: Ambient
Direction: {x: 0, y: 0, z: 1}
Color: {x: 1, y: 1, z: 1, w: 1}
Layer: 4294967295
Strength: 1
IsActive: true
Scripts: ~
- EID: 2
Name: Default
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 0, y: 0, z: 0}
Rotate: {x: 0, y: 0, z: 0}
Scale: {x: 1, y: 1, z: 1}
IsActive: true
Renderable Component:
Mesh: 148542784
Material: 121518381
IsActive: true
Animator Component:
Rig: 76586906
Clip: 76586906
IsActive: true
Scripts: ~

View File

@ -0,0 +1,3 @@
Name: anim
ID: 91478134
Type: 5

View File

@ -20,10 +20,11 @@ layout(location = 0) out struct
vec2 uv; // location = 1
vec4 normal; // location = 2
vec4 worldPos; // location = 3
vec3 worldNormal; // location = 4
} Out;
// material stuff
layout(location = 4) out struct
layout(location = 5) out struct
{
int materialIndex;
uint eid;
@ -61,11 +62,13 @@ void main()
// uvs for texturing in fragment shader
Out.uv = aUV;
mat3 transposeInv = mat3 (transpose(inverse(modelViewMat)));
mat3 mvTransInv = mat3 (transpose(inverse(modelViewMat)));
mat3 modelTransInv = mat3 (transpose(inverse(worldTransform)));
// normals are also in view space
Out.normal.rgb = transposeInv * aNormal.rgb;
Out.normal.rgb = mvTransInv * aNormal.rgb;
Out.normal.rgb = normalize (Out.normal.rgb);
Out.worldNormal = normalize (modelTransInv * aNormal);
// Compute bone matrix
mat4 boneMatrix = BoneMatrices.data[firstBoneIndex + aBoneIndices[0]] * aBoneWeights[0];

Binary file not shown.

View File

@ -2,6 +2,7 @@
struct DirectionalLightStruct
{
vec4 directionWorld;
vec3 direction;
uint isActive;
uint cullingMask;
@ -22,7 +23,7 @@ layout(local_size_x = 16, local_size_y = 16) in;
layout(set = 3, binding = 0, rgba32f) uniform image2D positions;
layout(set = 3, binding = 1, rgba32f) uniform image2D normals;
layout(set = 3, binding = 2, rgba8) uniform image2D albedo;
layout(set = 3, binding = 3, r32ui) uniform uimage2D lightLayerData;
layout(set = 3, binding = 3, rgba32ui) uniform uimage2D lightLayerData;
layout(set = 3, binding = 4, r8) uniform image2D ssaoBlurredImage;
layout(set = 3, binding = 5, rgba8) uniform image2D positionWorldSpace;
layout(set = 3, binding = 6, rgba8) uniform image2D targetImage;
@ -54,7 +55,7 @@ float LinStep (float val, float low, float high)
return clamp ((val - low)/(high - low), 0.0f, 1.0f);
}
float CalcShadowValue (sampler2D shadowMap, vec4 worldSpaceFragPos, mat4 lightPV)
float CalcShadowValue (sampler2D shadowMap, vec4 worldSpaceFragPos, mat4 lightPV, vec3 worldNormal, vec3 lightDir)
{
// clip space for fragment from light view space
vec4 fragPosLightPOV = lightPV * worldSpaceFragPos;
@ -69,6 +70,13 @@ float CalcShadowValue (sampler2D shadowMap, vec4 worldSpaceFragPos, mat4 lightPV
if (converted.x < 0.0f || converted.x > 1.0f || converted.y < 0.0f || converted.y > 1.0f)
return 1.0f;
float returnVal = 0.0f;
float worldNormalDotLight = dot (normalize (worldNormal), normalize(lightDir));
if (worldNormalDotLight < 0.0f)
return 0.7f;
if (fragPosLightPOV.z > moments.x && fragPosLightPOV.w > 0.0f)
{
float p = step (fragPosLightPOV.z, moments.x);
@ -76,15 +84,19 @@ float CalcShadowValue (sampler2D shadowMap, vec4 worldSpaceFragPos, mat4 lightPV
float d = fragPosLightPOV.z - moments.x;
float pMax = LinStep (variance / (variance + (d * d)), 0.9f, 1.0f);
return min (max (p, pMax), 1.0f);
returnVal = min (max (p, pMax) + 0.7f, 1.0f);
return returnVal;
}
else if (fragPosLightPOV.z > 1.0f)
{
return 0.0f;
}
else
return 0.3f;
// return step (fragPosLightPOV.z, );
return 1.0f;
}
void main()
@ -104,8 +116,12 @@ void main()
// normal of fragment
vec3 normalView = imageLoad(normals, globalThread).rgb;
uvec4 lightLayerAndNormal = imageLoad (lightLayerData, globalThread);
// light layer index
uint lightLayer = imageLoad (lightLayerData, globalThread).r;
uint lightLayer = lightLayerAndNormal.x;
vec3 worldNormal = vec3 (unpackHalf2x16 (lightLayerAndNormal.y).xy, unpackHalf2x16 (lightLayerAndNormal.z).x);
vec3 fragColor = vec3 (0.0f);
@ -138,7 +154,7 @@ void main()
if ((DirLightData.dLightData[i].shadowData & uint(1)) == 1)
{
// calculate shadow map here
fragColor *= CalcShadowValue (shadowMaps[0], positionWorld, DirLightData.dLightData[i].pvMatrix).xxx;
fragColor.rgb *= CalcShadowValue (shadowMaps[0], positionWorld, DirLightData.dLightData[i].pvMatrix, worldNormal, DirLightData.dLightData[i].directionWorld.xyz).xxx;
}
}
}

View File

@ -0,0 +1,58 @@
#version 450
#define BLUR_WIDTH 7
#define BLUR_HALF_WIDTH BLUR_WIDTH / 2
#define SHM_WIDTH BLUR_WIDTH + 16 - 1
layout(local_size_x = 16, local_size_y = 16) in;
layout(set = 3, binding = 0, r8) uniform image2D shadowMap;
layout(set = 3, binding = 1, r8) uniform image2D shadowMapBlurred;
vec4 GetShadowMapValue(ivec2 uv, ivec2 imageSize)
{
if (uv.x >= 0 && uv.y >= 0 && uv.x < imageSize.x && uv.y < imageSize.y)
{
return imageLoad (shadowMap, uv);
}
return vec4 (0.0f);
}
shared vec4 sharedPixels[16 + BLUR_WIDTH - 1][16 + BLUR_WIDTH - 1];
void main()
{
ivec2 globalThread = ivec2 (gl_GlobalInvocationID.xy);
ivec2 localThread = ivec2 (gl_LocalInvocationID.xy);
ivec2 inputImageSize = imageSize(shadowMap);
// Load color into shared memory
ivec2 start = ivec2 (gl_WorkGroupID) * ivec2 (gl_WorkGroupSize) - (BLUR_HALF_WIDTH);
for (int i = localThread.x; i < SHM_WIDTH; i += int (gl_WorkGroupSize.x))
{
for (int j = localThread.y; j < SHM_WIDTH; j += int (gl_WorkGroupSize.y))
{
vec4 value = GetShadowMapValue (start + ivec2 (i, j), inputImageSize);
sharedPixels[i][j] = value;
}
}
// wait for all shared memory to load
barrier();
ivec2 shmStart = ivec2 (localThread + (BLUR_HALF_WIDTH));
vec4 sum = vec4 (0.0f);
for (int i = -BLUR_HALF_WIDTH; i <= BLUR_HALF_WIDTH; ++i)
{
for (int j = -BLUR_HALF_WIDTH; j <= BLUR_HALF_WIDTH; ++j)
{
vec4 sharedVal = sharedPixels[shmStart.x + i][shmStart.y + j];
sum += sharedVal;
}
}
sum /= (BLUR_WIDTH * BLUR_WIDTH);
imageStore(shadowMapBlurred, globalThread, sum);
}

Binary file not shown.

View File

@ -0,0 +1,3 @@
Name: ShadowMapBlur_CS
ID: 38004013
Type: 2

View File

@ -7,6 +7,12 @@ layout(location = 0) out vec4 shadowMap;
void main()
{
float depth = gl_FragCoord.z;
float dx = dFdx(depth);
float dy = dFdy(depth);
float moment2 = depth * depth + 0.25f * (dx * dx + dy * dy);
// shadowMap = vec4 (0.0f, 0.0f, gl_FragCoord.z, 1.0f);
shadowMap = vec4 (gl_FragCoord.z, gl_FragCoord.z * gl_FragCoord.z, 0.0f, 1.0f);
shadowMap = vec4 (gl_FragCoord.z, moment2, 0.0f, 1.0f);
}

View File

@ -32,10 +32,11 @@ layout(location = 0) in struct
vec2 uv; // location = 1
vec4 normal; // location = 2
vec4 worldPos; // location = 3
vec3 worldNormal; // location = 4
} In;
// material stuff
layout(location = 4) flat in struct
layout(location = 5) flat in struct
{
int materialIndex;
uint eid;
@ -57,7 +58,7 @@ layout (std430, set = 2, binding = 0) buffer MaterialProperties // For mater
layout(location = 0) out vec4 position;
layout(location = 1) out uint outEntityID;
layout(location = 2) out uint lightLayerIndices;
layout(location = 2) out uvec4 lightLayerIndices;
layout(location = 3) out vec4 normals;
layout(location = 4) out vec4 albedo;
layout(location = 5) out vec4 worldSpacePosition;
@ -72,11 +73,11 @@ void main()
{
position = In.vertPos;
normals = In.normal;
albedo = texture(textures[nonuniformEXT(MatProp.data[In2.materialIndex].textureIndex)], In.uv);
albedo = vec4 (texture(textures[nonuniformEXT(MatProp.data[In2.materialIndex].textureIndex)], In.uv).xyz, 1.0f);
worldSpacePosition = In.worldPos;
outEntityID = In2.eid;
lightLayerIndices = In2.lightLayerIndex;
lightLayerIndices = uvec4 (In2.lightLayerIndex, 0, 0, 1);
// float vpHeight = float (In2.screenSpacePos.y) - MatProp.data[In2.materialIndex].highlightPosition;
// bring the frame of reference to the object's screen space pos
@ -101,4 +102,6 @@ void main()
}
else
objectVFX = vec4(0.0f, 0.0f, 0.0f, 1.0f);
objectVFX.a = 1.0f;
}

View File

@ -17,6 +17,7 @@ layout(location = 0) out struct
vec2 uv; // location = 1
vec4 normal; // location = 2
vec4 worldPos; // location = 3
vec3 worldNormal; // location = 4
} Out;
@ -36,7 +37,7 @@ struct GenericData
};
// material stuff
layout(location = 4) out struct
layout(location = 5) out struct
{
int materialIndex;
uint eid;
@ -76,11 +77,13 @@ void main()
// uvs for texturing in fragment shader
Out.uv = aUV;
mat3 transposeInv = mat3 (transpose(inverse(modelViewMat)));
mat3 mvTransInv = mat3 (transpose(inverse(modelViewMat)));
mat3 modelTransInv = mat3 (transpose(inverse(worldTransform)));
// normals are also in view space
Out.normal.rgb = transposeInv * aNormal.rgb;
Out.normal.rgb = mvTransInv * aNormal.rgb;
Out.normal.rgb = normalize (Out.normal.rgb);
Out.worldNormal = normalize (modelTransInv * aNormal);
// Get center of object in world position
vec4 worldPos = vec4(worldTransform[3][0], worldTransform[3][1], worldTransform[3][2], 1.0f);

View File

@ -17,10 +17,11 @@ layout(location = 0) in struct
vec2 uv; // location = 1
vec4 normal; // location = 2
vec4 worldPos; // location = 3
vec3 worldNormal; // location = 4
} In;
// material stuff
layout(location = 4) flat in struct
layout(location = 5) flat in struct
{
int materialIndex;
uint eid;
@ -35,7 +36,7 @@ layout (std430, set = 2, binding = 0) buffer MaterialProperties // For mater
layout(location = 0) out vec4 position;
layout(location = 1) out uint outEntityID;
layout(location = 2) out uint lightLayerIndices;
layout(location = 2) out uvec4 lightLayerIndices;
layout(location = 3) out vec4 normals;
layout(location = 4) out vec4 albedo;
layout(location = 5) out vec4 worldSpacePosition;
@ -48,5 +49,5 @@ void main()
worldSpacePosition = In.worldPos;
outEntityID = In2.eid;
lightLayerIndices = In2.lightLayerIndex;
lightLayerIndices = uvec4 (In2.lightLayerIndex, packHalf2x16 (In.worldNormal.xy), packHalf2x16 (vec2 (In.worldNormal.z, 1.0f)), 1);
}

Binary file not shown.

View File

@ -20,11 +20,12 @@ layout(location = 0) out struct
vec2 uv; // location = 1
vec4 normal; // location = 2
vec4 worldPos; // location = 3
vec3 worldNormal; // location = 4
} Out;
// material stuff
layout(location = 4) out struct
layout(location = 5) out struct
{
int materialIndex;
uint eid;
@ -57,11 +58,13 @@ void main()
// uvs for texturing in fragment shader
Out.uv = aUV;
mat3 transposeInv = mat3 (transpose(inverse(modelViewMat)));
mat3 mvTransInv = mat3 (transpose(inverse(modelViewMat)));
mat3 modelTransInv = mat3 (transpose(inverse(worldTransform)));
// normals are also in view space
Out.normal.rgb = transposeInv * aNormal.rgb;
Out.normal.rgb = mvTransInv * aNormal.rgb;
Out.normal.rgb = normalize (Out.normal.rgb);
Out.worldNormal = normalize (modelTransInv * aNormal);
// clip space for rendering
gl_Position = cameraData.vpMat * worldTransform * vec4 (aVertexPos, 1.0f);

Binary file not shown.

View File

@ -62,7 +62,6 @@ if %_e%==3 (goto :done) else (goto :ModelCompiler)
echo -----------------------ModelCompiler----------------------------
rmdir "Dependencies/ModelCompiler" /S /Q
git clone https://github.com/SHADE-DP/ModelCompiler.git "Dependencies/ModelCompiler"
git clone https://github.com/SHADE-DP/assimp.git "Dependencies/ModelCompiler/Dependencies/assimp"
if %_e%==4 (goto :done) else (goto :spdlog)
@REM :ktx

View File

@ -55,14 +55,11 @@ namespace SHADE
// Set accordingly
currClip = clip;
currPlaybackTime = 0.0f;
channelMaps.clear();
auto RAW_ANIM = clip->GetRawAnimation();
// Set up if valid
if (currClip && RAW_ANIM)
{
// Create channel maps
channelMaps.emplace(RAW_ANIM, createChannelMap(RAW_ANIM));
// Calculate secs for the clip
secsPerTick = 1.0f / RAW_ANIM->GetTicksPerSecond();
@ -202,12 +199,6 @@ namespace SHADE
// Store
rawAnims.emplace(RAW_ANIM);
}
// Load channel maps
for (auto anim : rawAnims)
{
channelMaps.emplace(anim, createChannelMap(anim));
}
}
}
@ -219,19 +210,6 @@ namespace SHADE
return animController->SetTrigger(animInstanceData, paramName);
}
/*-----------------------------------------------------------------------------------*/
/* Helper Functions - Loading */
/*-----------------------------------------------------------------------------------*/
SHAnimatorComponent::ChannelMap SHAnimatorComponent::createChannelMap(Handle<SHRawAnimation> rawAnimData)
{
ChannelMap channelMap;
for (const auto& channel : rawAnimData->GetChannels())
{
channelMap.emplace(channel.Name, &channel);
}
return channelMap;
}
/*-----------------------------------------------------------------------------------*/
/* Helper Functions - Update */
/*-----------------------------------------------------------------------------------*/
@ -283,31 +261,25 @@ namespace SHADE
}
void SHAnimatorComponent::updatePoseWithClip(Handle<SHAnimationClip> clip, float poseTime)
{
// Get closest frame index
const int CLOSEST_FRAME_IDX = clip->GetStartFrameIndex() + static_cast<int>(std::floorf(poseTime / secsPerTick));
updatePoseWithClip(CLOSEST_FRAME_IDX, poseTime, clip->GetRawAnimation(), rig->GetRootNode(), SHMatrix::Identity);
updatePoseWithClip(poseTime, clip->GetRawAnimation(), rig->GetRootNode(), SHMatrix::Identity);
}
void SHAnimatorComponent::updatePoseWithClip(int closestFrameIndex, float poseTime, Handle<SHRawAnimation> rawAnimData, Handle<SHRigNode> node, const SHMatrix& parentMatrix)
void SHAnimatorComponent::updatePoseWithClip(float poseTime, Handle<SHRawAnimation> rawAnimData, Handle<SHRigNode> node, const SHMatrix& parentMatrix)
{
// Check if there is a channel for this node
const std::string& BONE_NAME = rig->GetName(node);
SHMatrix transformMatrix = node->TransformMatrix;
if (channelMaps.contains(rawAnimData))
const int BONE_IDX = rig->GetNodeIndex(node);
const auto& CHANNELS = rawAnimData->GetChannels();
if (BONE_IDX < CHANNELS.size())
{
auto channelMap = channelMaps[rawAnimData];
if (channelMap.contains(BONE_NAME))
{
const auto CHANNEL = channelMap[BONE_NAME];
const auto& CHANNEL = CHANNELS[BONE_IDX];
transformMatrix = SHMatrix::Transform
(
getInterpolatedValue(CHANNEL->PositionKeyFrames, closestFrameIndex, poseTime),
getInterpolatedValue(CHANNEL->RotationKeyFrames, closestFrameIndex, poseTime),
getInterpolatedValue(CHANNEL->ScaleKeyFrames, closestFrameIndex, poseTime)
getInterpolatedValue(CHANNEL.PositionKeyFrames, poseTime),
getInterpolatedValue(CHANNEL.RotationKeyFrames, poseTime),
getInterpolatedValue(CHANNEL.ScaleKeyFrames, poseTime)
);
}
}
// Apply parent's transformation
transformMatrix = transformMatrix * parentMatrix;
@ -323,7 +295,7 @@ namespace SHADE
// Apply pose to children
for (auto& child : node->Children)
{
updatePoseWithClip(closestFrameIndex, poseTime, rawAnimData, child, transformMatrix);
updatePoseWithClip(poseTime, rawAnimData, node, transformMatrix);
}
}
}

View File

@ -190,22 +190,17 @@ namespace SHADE
float secsPerTick = 0.0f;
// Buffer
std::vector<SHMatrix> boneMatrices;
// Caches
std::unordered_map<Handle<SHRawAnimation>, ChannelMap> channelMaps;
/*---------------------------------------------------------------------------------*/
/* Helper Functions */
/*---------------------------------------------------------------------------------*/
// Loading
ChannelMap createChannelMap(Handle<SHRawAnimation> rawAnimData);
// Update
void updateAnimController(float dt);
void updateManualClipState(float dt);
void updateCurrentAnimatorState(Handle<SHAnimationClip> clip, float playbackTime);
void updatePoseWithClip(Handle<SHAnimationClip> clip, float poseTime);
void updatePoseWithClip(int closestFrameIndex, float poseTime, Handle<SHRawAnimation> rawAnimData, Handle<SHRigNode> node, const SHMatrix& parentMatrix);
void updatePoseWithClip(float poseTime, Handle<SHRawAnimation> rawAnimData, Handle<SHRigNode> node, const SHMatrix& parentMatrix);
template<typename T>
T getInterpolatedValue(const std::vector<SHAnimationKeyFrame<T>>& keyframes, int closestFrameIndex, float poseTime);
T getInterpolatedValue(const std::vector<SHAnimationKeyFrame<T>>& keyframes, float poseTime);
/*---------------------------------------------------------------------------------*/
/* RTTR */

View File

@ -44,7 +44,7 @@ namespace SHADE
/* Helper Functions */
/*-----------------------------------------------------------------------------------*/
template<typename T>
T SHAnimatorComponent::getInterpolatedValue(const std::vector<SHAnimationKeyFrame<T>>& keyframes, int closestFrameIndex, float poseTime)
T SHAnimatorComponent::getInterpolatedValue(const std::vector<SHAnimationKeyFrame<T>>& keyframes, float poseTime)
{
// Only allow SHVec3 and SHQuaternion
static_assert(std::is_same_v<T, SHVec3> || std::is_same_v<T, SHQuaternion>, "Only interpolation for SHVec3 and SHQuaternion is allowed.");
@ -56,11 +56,11 @@ namespace SHADE
{
const auto& KEYFRAME = *iter;
if (KEYFRAME.FrameIndex <= closestFrameIndex)
if (KEYFRAME.TimeStamp <= poseTime)
{
firstKeyFrame = iter;
}
else // KEYFRAME.FrameIndex > closestFrameIndex
else // KEYFRAME.FrameIndex > poseTime
{
nextKeyFrame = iter;
break;
@ -84,8 +84,8 @@ namespace SHADE
}
// Get interpolated vector
const float PREV_FRAME_TIME = firstKeyFrame->FrameIndex * secsPerTick;
const float NEXT_FRAME_TIME = nextKeyFrame->FrameIndex * secsPerTick;
const float PREV_FRAME_TIME = firstKeyFrame->TimeStamp;
const float NEXT_FRAME_TIME = nextKeyFrame->TimeStamp;
const float NORMALISED_TIME = (poseTime - PREV_FRAME_TIME) / (NEXT_FRAME_TIME - PREV_FRAME_TIME);
if constexpr (std::is_same_v<T, SHQuaternion>)

View File

@ -20,16 +20,14 @@ namespace SHADE
/* Constructors */
/*-----------------------------------------------------------------------------------*/
SHRawAnimation::SHRawAnimation(const SHAnimAsset& asset)
: ticksPerSecond { static_cast<int>(asset.ticksPerSecond) }
, totalTime { static_cast<float>(asset.duration) / static_cast<int>(asset.ticksPerSecond) }
, totalFrames { static_cast<int>(asset.duration) }
{
// Populate keyframes
int maxFrames = 0;
totalTime = 0.0f;
for (const auto& channel : asset.nodeChannels)
{
// Create a channel
Channel newChannel;
newChannel.Name = std::string(channel.name);
newChannel.PositionKeyFrames.reserve(channel.positionKeys.size());
newChannel.RotationKeyFrames.reserve(channel.rotationKeys.size());
newChannel.ScaleKeyFrames.reserve(channel.scaleKeys.size());
@ -37,21 +35,30 @@ namespace SHADE
// Populate Keyframes
for (const auto& posKey : channel.positionKeys)
{
newChannel.PositionKeyFrames.emplace_back(SHAnimationKeyFrame<SHVec3>{ static_cast<int>(posKey.time), posKey.value});
newChannel.PositionKeyFrames.emplace_back(SHAnimationKeyFrame<SHVec3>{ posKey.time, posKey.value});
}
for (const auto& rotKey : channel.rotationKeys)
{
newChannel.RotationKeyFrames.emplace_back(SHAnimationKeyFrame<SHQuaternion>{ static_cast<int>(rotKey.time), rotKey.value});
newChannel.RotationKeyFrames.emplace_back(SHAnimationKeyFrame<SHQuaternion>{ rotKey.time, rotKey.value});
}
for (const auto& scaleKey : channel.scaleKeys)
{
newChannel.ScaleKeyFrames.emplace_back(SHAnimationKeyFrame<SHVec3>{ static_cast<int>(scaleKey.time), scaleKey.value});
newChannel.ScaleKeyFrames.emplace_back(SHAnimationKeyFrame<SHVec3>{ scaleKey.time, scaleKey.value});
}
newChannel.MaxFrames = std::max({ newChannel.PositionKeyFrames.size(), newChannel.RotationKeyFrames.size(), newChannel.ScaleKeyFrames.size() });
// Compute max frames
maxFrames = std::max(maxFrames, newChannel.MaxFrames);
// Compute total time
totalTime = std::max({ totalTime, newChannel.PositionKeyFrames.back().TimeStamp, newChannel.RotationKeyFrames.back().TimeStamp, newChannel.ScaleKeyFrames.back().TimeStamp });
// Insert the channel
channels.emplace_back(std::move(newChannel));
// Compute fps
ticksPerSecond = static_cast<int>(maxFrames / totalTime);
}
}

View File

@ -27,7 +27,7 @@ namespace SHADE
template<typename T>
struct SHAnimationKeyFrame
{
int FrameIndex;
float TimeStamp;
T Data;
};
@ -45,7 +45,6 @@ namespace SHADE
/// </summary>
struct Channel
{
std::string Name;
std::vector<SHAnimationKeyFrame<SHVec3>> PositionKeyFrames;
std::vector<SHAnimationKeyFrame<SHQuaternion>> RotationKeyFrames;
std::vector<SHAnimationKeyFrame<SHVec3>> ScaleKeyFrames;

View File

@ -120,8 +120,10 @@ namespace SHADE
// Fill the node with data
const auto& NODE_DATA = asset.nodeDataCollection.at(sourceNode->idRef);
newNode->OffsetMatrix = SHMatrix::Transpose(NODE_DATA.offset);
newNode->TransformMatrix = SHMatrix::Transpose(NODE_DATA.transform);
//newNode->OffsetMatrix = SHMatrix::Transpose(NODE_DATA.inverseBindMatrix);
//newNode->TransformMatrix = SHMatrix::Transpose(NODE_DATA.transform);
newNode->OffsetMatrix = NODE_DATA.inverseBindMatrix;
newNode->TransformMatrix = NODE_DATA.transform;
// Populate maps
if (!NODE_DATA.name.empty())

View File

@ -20,69 +20,61 @@
namespace SHADE
{
enum class SHAnimationBehaviour : uint8_t
enum class AnimationInterpolation : uint8_t
{
DEFAULT = 0x0,
CONSTANT = 0x1,
LINEAR = 0x2,
REPEAT = 0x3
DEFAULT = 0x1,
LINEAR = 0x1,
STEP = 0x2,
CUBICSPLINE = 0x3
};
// Base
struct KeyBase
{
float time;
};
// Smallest data containers
struct PositionKey
struct PositionKey :KeyBase
{
float time;
SHVec3 value;
};
struct RotationKey
struct RotationKey : KeyBase
{
float time;
SHVec4 value;
};
struct ScaleKey
struct ScaleKey :KeyBase
{
float time;
SHVec3 value;
};
// Headers for read/write
struct SHAnimNodeInfo
{
uint32_t charCount;
uint32_t posKeyCount;
uint32_t rotKeyCount;
uint32_t scaKeyCount;
};
struct SHAnimDataHeader
{
uint32_t charCount;
uint32_t animNodeCount;
std::vector<SHAnimNodeInfo> nodeHeaders;
uint32_t frameCount;
};
// Main data containers
struct SHAnimData
struct SHAnimNode
{
std::string name;
SHAnimationBehaviour pre;
SHAnimationBehaviour post;
std::vector<PositionKey> positionKeys;
std::vector<RotationKey> rotationKeys;
std::vector<ScaleKey> scaleKeys;
AnimationInterpolation interpolation;
std::vector<PositionKey> positionKeys{};
std::vector<RotationKey> rotationKeys{};
std::vector<ScaleKey> scaleKeys{};
};
struct SH_API SHAnimAsset : SHAssetData
struct SH_API SHAnimAsset final : SHAssetData
{
std::string name;
double duration;
double ticksPerSecond;
double duration{};
double ticksPerSecond{};
std::vector<SHAnimData> nodeChannels;
std::vector<SHAnimNode> nodeChannels{};
};
}

View File

@ -19,45 +19,28 @@
namespace SHADE
{
constexpr int BONE_INDEX_ALIGHTMENT = 4;
struct SHMeshDataHeader
{
uint32_t vertexCount;
uint32_t indexCount;
uint32_t charCount;
uint32_t boneCount;
};
struct MeshBoneInfo
{
uint32_t charCount;
uint32_t weightCount; // Size should be same as boneCount
};
struct BoneWeight
{
uint32_t index;
float weight;
};
struct MeshBone
{
std::string name;
SHMatrix offset;
std::vector<BoneWeight> weights;
bool hasWeights;
};
struct SH_API SHMeshAsset : SHAssetData
{
std::string name;
std::vector<SHVec3> VertexPositions;
std::vector<SHVec3> VertexTangents;
std::vector<SHVec3> VertexNormals;
std::vector<SHVec2> VertexTexCoords;
std::vector<uint32_t> Indices;
std::vector<SHVec4U> VertexBoneIndices;
std::vector<SHVec4> VertexBoneWeights;
std::vector<SHVec3> VertexPositions{};
std::vector<SHVec3> VertexTangents{};
std::vector<SHVec3> VertexNormals{};
std::vector<SHVec2> VertexTexCoords{};
std::vector<uint32_t> Indices{};
//Variables
std::vector<SHVec4> VertexBoneWeights{};
std::vector<SHVec4U> VertexBoneIndices{};
uint32_t BoneCount;
};
}

View File

@ -1,8 +1,6 @@
#include "SHpch.h"
#include "SHRigAsset.h"
#include <queue>
namespace SHADE
{
SHRigAsset::~SHRigAsset()

View File

@ -17,17 +17,30 @@
namespace SHADE
{
using NodeDataFlag = unsigned char;
constexpr NodeDataFlag NODE_DATA_ROTATION = 0b0001;
constexpr NodeDataFlag NODE_DATA_SCALE = 0b0010;
constexpr NodeDataFlag NODE_DATA_TRANSLATION = 0b0100;
constexpr NodeDataFlag NODE_DATA_MATRIX = 0b1000;
constexpr size_t NODE_COMPONENT_COUNT_ROTATION{ 4 };
constexpr size_t NODE_COMPONENT_COUNT_SCALE{ 3 };
constexpr size_t NODE_COMPONENT_COUNT_TRANSLATION{ 3 };
constexpr size_t NODE_COMPONENT_COUNT_MATRIX{ 16 };
struct SHRigDataHeader
{
uint32_t nodeCount;
std::vector<uint32_t> charCounts;
uint32_t nodeCount{};
uint32_t startNode{};
std::vector<uint32_t> charCounts{};
};
struct SHRigNodeData
{
std::string name;
SHMatrix transform;
SHMatrix offset;
SHMatrix inverseBindMatrix;
};
struct SHRigNodeAsset
@ -39,9 +52,8 @@ namespace SHADE
struct SH_API SHRigAsset : SHAssetData
{
~SHRigAsset();
SHRigDataHeader header;
std::vector<SHRigNodeData> nodeDataCollection;
std::vector<SHRigNodeData> nodeDataCollection{};
SHRigNodeAsset* root;
};
}

View File

@ -0,0 +1,23 @@
/******************************************************************************
* \file SHAudioBankAsset.h
* \author Loh Xiao Qi
* \date 31 January 2023
* \brief
*
* \copyright Copyright (c) 2023 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
#include "SHAssetData.h"
#include <string>
namespace SHADE
{
struct SHAudioBankAsset : SHAssetData
{
std::string name;
std::string path;
};
}

View File

@ -17,6 +17,52 @@
namespace SHADE
{
inline SHVec3 GetVec3FromVector(std::vector<double> const& vec)
{
assert(vec.size() == NODE_COMPONENT_COUNT_TRANSLATION);
return SHVec3{
static_cast<float>(vec[0]),
static_cast<float>(vec[1]),
static_cast<float>(vec[2])
};
}
inline SHVec4 GetVec4FromVector(std::vector<double> const& vec)
{
assert(vec.size() == NODE_COMPONENT_COUNT_ROTATION);
return SHVec4{
static_cast<float>(vec[0]),
static_cast<float>(vec[1]),
static_cast<float>(vec[2]),
static_cast<float>(vec[3])
};
}
inline SHMatrix GetMatrixFromVector(std::vector<double> const& vec)
{
assert(vec.size() == NODE_COMPONENT_COUNT_MATRIX);
return SHMatrix{
static_cast<float>(vec[0]),
static_cast<float>(vec[1]),
static_cast<float>(vec[2]),
static_cast<float>(vec[3]),
static_cast<float>(vec[4]),
static_cast<float>(vec[5]),
static_cast<float>(vec[6]),
static_cast<float>(vec[7]),
static_cast<float>(vec[8]),
static_cast<float>(vec[9]),
static_cast<float>(vec[10]),
static_cast<float>(vec[11]),
static_cast<float>(vec[12]),
static_cast<float>(vec[13]),
static_cast<float>(vec[14]),
static_cast<float>(vec[15])
};
}
void SHModelLoader::ReadHeaders(FileReference file, SHModelAsset& asset)
{
file.read(
@ -36,29 +82,12 @@ namespace SHADE
if (asset.header.animCount > 0)
{
asset.animHeaders.resize(asset.header.animCount);
for (auto i {0}; i < asset.header.animCount; ++i)
for (auto& animHeader : asset.animHeaders)
{
auto& animHeader = asset.animHeaders[i];
file.read(
reinterpret_cast<char*>(&animHeader.charCount),
sizeof(uint32_t)
reinterpret_cast<char*>(&animHeader),
sizeof(animHeader)
);
file.read(
reinterpret_cast<char*>(&animHeader.animNodeCount),
sizeof(uint32_t)
);
animHeader.nodeHeaders.resize(animHeader.animNodeCount);
for (auto j {0}; j < animHeader.animNodeCount; ++j)
{
auto& nodeHeader = animHeader.nodeHeaders[j];
file.read(
reinterpret_cast<char*>(&nodeHeader),
sizeof(SHAnimNodeInfo)
);
}
}
}
}
@ -74,53 +103,63 @@ namespace SHADE
ReadRigHeader(file, asset.rig.header);
ReadRigData(file, asset.rig.header, asset.rig.nodeDataCollection);
ReadRigTree(file, asset.rig.header, asset.rig.root);
}
}
void SHModelLoader::ReadAnimNode(FileReference file, SHAnimNodeInfo const& info, SHAnimData& data)
for (auto& mesh : asset.meshes)
{
mesh->BoneCount = asset.rig.nodeDataCollection.size();
}
//BuildTransformMatrices(asset.rig);
}
}
void SHModelLoader::ReadAnimNode(FileReference file, uint32_t frameCount, SHAnimNode& data)
{
data.name.resize(info.charCount);
file.read(
data.name.data(),
info.charCount
reinterpret_cast<char*>(&data.interpolation),
1
);
file.read(
reinterpret_cast<char*>(&data.pre),
sizeof(SHAnimationBehaviour)
);
file.read(
reinterpret_cast<char*>(&data.post),
sizeof(SHAnimationBehaviour)
);
uint32_t keySize {0};
file.read(
reinterpret_cast<char*>(&keySize),
sizeof(uint32_t)
);
data.positionKeys.resize(keySize);
data.rotationKeys.resize(keySize);
data.scaleKeys.resize(keySize);
data.positionKeys.resize(frameCount);
data.rotationKeys.resize(frameCount);
data.scaleKeys.resize(frameCount);
file.read(
reinterpret_cast<char*>(data.positionKeys.data()),
sizeof(PositionKey) * keySize
sizeof(PositionKey) * frameCount
);
file.read(
reinterpret_cast<char*>(data.rotationKeys.data()),
sizeof(RotationKey) * keySize
sizeof(RotationKey) * frameCount
);
file.read(
reinterpret_cast<char*>(data.scaleKeys.data()),
sizeof(ScaleKey) * keySize
sizeof(ScaleKey) * frameCount
);
}
void SHModelLoader::BuildTransformMatrices(SHRigAsset& rig)
{
std::queue<SHRigNodeAsset const*> nodeQueue;
nodeQueue.push(rig.root);
while(!nodeQueue.empty())
{
auto& current = nodeQueue.front();
nodeQueue.pop();
auto& parentData {rig.nodeDataCollection[current->idRef]};
for (auto const& child: current->children)
{
nodeQueue.push(child);
auto& childData {rig.nodeDataCollection[child->idRef]};
childData.transform = childData.transform * parentData.transform;
}
}
}
void SHModelLoader::ReadRigHeader(FileReference file, SHRigDataHeader& header)
{
file.read(
@ -128,6 +167,11 @@ namespace SHADE
sizeof(uint32_t)
);
file.read(
reinterpret_cast<char*>(&header.startNode),
sizeof(uint32_t)
);
header.charCounts.resize(header.nodeCount);
file.read(
reinterpret_cast<char*>(header.charCounts.data()),
@ -141,21 +185,72 @@ namespace SHADE
for (auto i {0}; i < header.nodeCount; ++i)
{
data[i].name.resize(header.charCounts[i]);
auto& node = data[i];
node.name.resize(header.charCounts[i]);
file.read(
data[i].name.data(),
node.name.data(),
header.charCounts[i]
);
file.read(
reinterpret_cast<char*>(&data[i].transform),
reinterpret_cast<char*>(&node.inverseBindMatrix),
sizeof(SHMatrix)
);
node.inverseBindMatrix = node.inverseBindMatrix;
NodeDataFlag flag;
file.get(reinterpret_cast<char&>(flag));
SHVec3 scale{ SHVec3::One }, translation{ SHVec3::Zero };
SHVec4 rotation{ SHVec4::UnitW };
SHMatrix matrix{ SHMatrix::Identity };
std::vector<double> carrier;
if (flag & NODE_DATA_ROTATION)
{
carrier.resize(NODE_COMPONENT_COUNT_ROTATION);
file.read(
reinterpret_cast<char*>(&data[i].offset),
sizeof(SHMatrix)
reinterpret_cast<char*>(carrier.data()),
sizeof(double) * NODE_COMPONENT_COUNT_ROTATION
);
rotation = GetVec4FromVector(carrier);
}
if (flag & NODE_DATA_SCALE)
{
carrier.resize(NODE_COMPONENT_COUNT_SCALE);
file.read(
reinterpret_cast<char*>(carrier.data()),
sizeof(double) * NODE_COMPONENT_COUNT_SCALE
);
scale = GetVec3FromVector(carrier);
}
if (flag & NODE_DATA_TRANSLATION)
{
carrier.resize(NODE_COMPONENT_COUNT_TRANSLATION);
file.read(
reinterpret_cast<char*>(carrier.data()),
sizeof(double) * NODE_COMPONENT_COUNT_TRANSLATION
);
translation = GetVec3FromVector(carrier);
}
if (flag & NODE_DATA_MATRIX)
{
carrier.resize(NODE_COMPONENT_COUNT_MATRIX);
file.read(
reinterpret_cast<char*>(carrier.data()),
sizeof(double) * NODE_COMPONENT_COUNT_MATRIX
);
matrix = GetMatrixFromVector(carrier);
}
//matrix *= SHMatrix::Transform(translation, rotation, scale);
matrix *= SHMatrix::Translate(translation) * SHMatrix::Rotate(rotation) * SHMatrix::Scale(scale);
node.transform = matrix;
//node.transform = SHMatrix::Inverse(node.inverseBindMatrix);
}
}
@ -199,8 +294,6 @@ namespace SHADE
nodeQueue.emplace(depthPtr++, depthTempPtr++);
}
}
delete[] dst;
}
void SHModelLoader::ReadMeshData(FileReference file, std::vector<SHMeshDataHeader> const& headers,
@ -221,7 +314,6 @@ namespace SHADE
data.VertexNormals.resize(header.vertexCount);
data.VertexTexCoords.resize(header.vertexCount);
data.Indices.resize(header.indexCount);
data.BoneCount = header.boneCount;
file.read(data.name.data(), header.charCount);
file.read(reinterpret_cast<char*>(data.VertexPositions.data()), vertexVec3Byte);
@ -230,48 +322,20 @@ namespace SHADE
file.read(reinterpret_cast<char*>(data.VertexTexCoords.data()), vertexVec2Byte);
file.read(reinterpret_cast<char*>(data.Indices.data()), sizeof(uint32_t) * header.indexCount);
if (header.boneCount)
if (header.hasWeights)
{
std::vector<MeshBoneInfo> boneInfos(header.boneCount);
std::vector<MeshBone> bones(header.boneCount);
file.read(reinterpret_cast<char*>(boneInfos.data()), sizeof(MeshBoneInfo) * header.boneCount);
for (auto i{ 0 }; i < header.boneCount; ++i)
{
auto& bone = bones[i];
auto const& info = boneInfos[i];
bone.name.resize(info.charCount);
file.read(bone.name.data(), info.charCount);
file.read(reinterpret_cast<char*>(&bone.offset), sizeof(SHMatrix));
bone.weights.resize(info.weightCount);
file.read(reinterpret_cast<char*>(bone.weights.data()), sizeof(BoneWeight) * info.weightCount);
}
data.VertexBoneIndices.resize(header.vertexCount);
data.VertexBoneWeights.resize(header.vertexCount);
for (uint32_t boneIndex{0}; boneIndex < bones.size(); ++boneIndex)
{
auto const& bone = bones[boneIndex];
for (auto const& weight : bone.weights)
{
auto& boneIndices = data.VertexBoneIndices[weight.index];
auto& boneWeight = data.VertexBoneWeights[weight.index];
file.read(
reinterpret_cast<char*>(data.VertexBoneWeights.data()),
sizeof(SHVec4) * header.vertexCount
);
for (auto j{0}; j < BONE_INDEX_ALIGHTMENT; ++j)
{
if (boneWeight[j] == 0.f)
{
boneIndices[j] = boneIndex;
boneWeight[j] = weight.weight;
break;
}
}
}
}
file.read(
reinterpret_cast<char*>(data.VertexBoneIndices.data()),
sizeof(SHVec4U) * header.vertexCount
);
}
meshes[i] = &data;
@ -306,7 +370,7 @@ namespace SHADE
animAsset.nodeChannels.resize(header.animNodeCount);
for (auto i {0}; i < header.animNodeCount; ++i)
{
ReadAnimNode(file, header.nodeHeaders[i], animAsset.nodeChannels[i]);
ReadAnimNode(file, header.frameCount, animAsset.nodeChannels[i]);
}
anims[i] = &animAsset;

View File

@ -20,7 +20,6 @@ namespace SHADE
{
using FileReference = std::ifstream&;
void ReadAnimNode(FileReference file, SHAnimNodeInfo const& info, SHAnimData& data);
void ReadRigHeader(FileReference file, SHRigDataHeader& header);
void ReadRigData(FileReference file, SHRigDataHeader const& header, std::vector<SHRigNodeData>& data);
@ -28,6 +27,9 @@ namespace SHADE
void ReadMeshData(FileReference file, std::vector<SHMeshDataHeader> const& headers, std::vector<SHMeshAsset*>& meshes);
void ReadAnimData(FileReference file, std::vector<SHAnimDataHeader> const& headers, std::vector<SHAnimAsset*>& anims);
void ReadAnimNode(FileReference file, uint32_t frameCount, SHAnimNode& data);
void BuildTransformMatrices(SHRigAsset& rig);
void ReadHeaders(FileReference file, SHModelAsset& asset);
void ReadData(FileReference file, SHModelAsset& asset);

View File

@ -14,6 +14,7 @@
#include "Assets/Asset Types/SHSceneAsset.h"
#include "Assets/Asset Types/SHPrefabAsset.h"
#include "Assets/Asset Types/SHMaterialAsset.h"
#include "Assets/Asset Types/SHAudioBankAsset.h"
#include <fstream>
#include <sstream>
@ -22,6 +23,14 @@ namespace SHADE
{
SHAssetData* SHTextBasedLoader::Load(AssetPath path)
{
if (path.extension().string() == AUDIO_BANK_EXTENSION)
{
auto data = new SHAudioBankAsset();
data->name = path.stem().string();
data->path = path.string();
return data;
}
std::ifstream file{ path, std::ios::in };
if (!file.is_open())

View File

@ -57,6 +57,7 @@ enum class AssetType : AssetTypeMeta
MESH,
SCRIPT,
FONT,
AUDIO_BANK,
ANIM_CONTAINER,
ANIM_CLIP,
MAX_COUNT
@ -87,6 +88,7 @@ constexpr std::string_view ANIM_CLIP_FOLDER{ "/Animations/" };
constexpr std::string_view META_EXTENSION {".shmeta"};
constexpr std::string_view AUDIO_EXTENSION {".ogg"};
constexpr std::string_view AUDIO_WAV_EXTENSION {".wav"};
constexpr std::string_view AUDIO_BANK_EXTENSION {".bank"};
constexpr std::string_view SHADER_EXTENSION{ ".shshader" };
constexpr std::string_view SHADER_BUILT_IN_EXTENSION{ ".shshaderb" };
constexpr std::string_view FONT_EXTENSION{ ".shfont" };
@ -111,7 +113,7 @@ constexpr std::string_view EXTENSIONS[] = {
FILLER_EXTENSION,
SCRIPT_EXTENSION,
FONT_EXTENSION,
AUDIO_WAV_EXTENSION,
AUDIO_BANK_EXTENSION,
ANIM_CONTAINER_EXTENSION,
FILLER_EXTENSION
};

View File

@ -499,6 +499,10 @@ namespace SHADE
fontPath += FONT_EXTENSION;
newPath = fontPath;
}
else if (ext == AUDIO_BANK_EXTENSION.data())
{
newPath = path.string();
}
else
{
SHLOG_WARNING("[Asset Manager] File Type compilation not yet Implemented: {}", path.string());
@ -582,6 +586,7 @@ namespace SHADE
loaders[static_cast<size_t>(AssetType::MESH)] = nullptr;
loaders[static_cast<size_t>(AssetType::SCRIPT)] = nullptr;
loaders[static_cast<size_t>(AssetType::FONT)] = dynamic_cast<SHAssetLoader*>(new SHFontLoader());
loaders[static_cast<size_t>(AssetType::AUDIO_BANK)] = loaders[static_cast<size_t>(AssetType::SCENE)];
loaders[static_cast<size_t>(AssetType::ANIM_CONTAINER)] = dynamic_cast<SHAssetLoader*>(new SHBinaryLoader());
loaders[static_cast<size_t>(AssetType::ANIM_CLIP)] = nullptr;
}
@ -752,6 +757,20 @@ namespace SHADE
return newAsset.id;
}
else if (ext == AUDIO_BANK_EXTENSION)
{
SHAsset newAsset{
path.stem().string(),
GenerateAssetID(AssetType::AUDIO_BANK),
AssetType::AUDIO_BANK,
path,
false
};
assetCollection.emplace(newAsset.id, newAsset);
SHAssetMetaHandler::WriteMetaData(newAsset);
return newAsset.id;
}
else if(ext == MATERIAL_EXTENSION)
{
SHAsset newAsset{

View File

@ -486,6 +486,7 @@ namespace SHADE
SHEditorWidgets::DragVec3("Direction", { "X", "Y", "Z" }, [component]() {return component->GetDirection(); }, [component](SHVec3 const& vec) {component->SetDirection(vec); });
SHEditorWidgets::ColorPicker("Color", [component]() {return component->GetColor(); }, [component](SHVec4 const& rgba) {component->SetColor(rgba); });
SHEditorWidgets::DragFloat("Strength", [component]() {return component->GetStrength(); }, [component](float const& value) {component->SetStrength(value); });
SHEditorWidgets::CheckBox("Casting Shadows", [comp = component]() {return comp->GetEnableShadow(); }, [comp = component](bool b) {comp->SetEnableShadow(b);});
}
else
{

View File

@ -30,4 +30,5 @@ constexpr SHEventIdentifier SH_BUTTON_RELEASE_EVENT { 21 };
constexpr SHEventIdentifier SH_BUTTON_HOVER_ENTER_EVENT { 22 };
constexpr SHEventIdentifier SH_BUTTON_HOVER_EXIT_EVENT { 23 };
constexpr SHEventIdentifier SH_ASSET_COMPILE_EVENT { 24 };
constexpr SHEventIdentifier SH_GRAPHICS_LIGHT_DELETE_EVENT { 25 };

View File

@ -162,6 +162,7 @@ namespace SHADE
{
assetCollection.emplace(asset.id, asset);
}
if (file.name == asset.name)
{
AssetPath path{ file.path };

View File

@ -6,13 +6,16 @@
namespace SHADE
{
struct SHLightEnableShadowEvent
struct SHLightShadowEvent
{
//! We need to get the light component and initialize the relevant variables.
EntityID lightEntity;
//! Generate a renderer for the light component
bool generateRenderer;
bool firstEnable;
//! True when light is set enable shadow
bool enableShadow;
};
struct SHWindowResizeEvent
@ -23,4 +26,10 @@ namespace SHADE
// New height when window resizes
uint32_t resizeHeight;
};
struct SHDeleteLightEvent
{
//! Entity of the light we are deleting
EntityID lightEntity;
};
}

View File

@ -128,7 +128,7 @@ namespace SHADE
SHFreetypeInstance::Init();
//SHAssetManager::CompileAsset("../../Assets/Shaders/DeferredComposite_CS.glsl", false);
SHAssetManager::CompileAsset("../../Assets/Shaders/DeferredComposite_CS.glsl", false);
//SHAssetManager::CompileAsset("../../Assets/Shaders/ShadowMap_FS.glsl", false);
//SHAssetManager::CompileAsset("../../Assets/Shaders/ShadowMap_FS.glsl", false);
//SHAssetManager::CompileAsset("../../Assets/Shaders/SSAO_CS.glsl", false);
@ -141,6 +141,8 @@ namespace SHADE
//SHAssetManager::CompileAsset("../../Assets/Shaders/Text_VS.glsl", false);
//SHAssetManager::CompileAsset("../../Assets/Shaders/Trajectory_VS.glsl", false);
//SHAssetManager::CompileAsset("../../Assets/Shaders/Trajectory_FS.glsl", false);
//SHAssetManager::CompileAsset("../../Assets/Shaders/ShadowMapBlur_CS.glsl", false);
//SHAssetManager::CompileAsset("../../Assets/Shaders/Anim_VS.glsl", false);
// Load Built In Shaders
static constexpr AssetID VS_DEFAULT = 39210065; defaultVertShader = SHResourceManager::LoadOrGet<SHVkShaderModule>(VS_DEFAULT);
@ -160,6 +162,7 @@ namespace SHADE
static constexpr AssetID SHADOW_MAP_FS = 45925790; shadowMapFS = SHResourceManager::LoadOrGet<SHVkShaderModule>(SHADOW_MAP_FS);
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);
}
@ -217,7 +220,7 @@ namespace SHADE
renderGraph->AddResource("Albedo", { SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR, SH_RENDER_GRAPH_RESOURCE_FLAGS::INPUT, SH_RENDER_GRAPH_RESOURCE_FLAGS::STORAGE }, true, windowDims.first, windowDims.second);
renderGraph->AddResource("Depth Buffer", { SH_RENDER_GRAPH_RESOURCE_FLAGS::DEPTH, SH_RENDER_GRAPH_RESOURCE_FLAGS::INPUT }, true, windowDims.first, windowDims.second, vk::Format::eD32SfloatS8Uint);
renderGraph->AddResource("Entity ID", { SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR, SH_RENDER_GRAPH_RESOURCE_FLAGS::SHARED }, true, windowDims.first, windowDims.second, vk::Format::eR32Uint, 1, vk::ImageUsageFlagBits::eTransferSrc);
renderGraph->AddResource("Light Layer Indices", { SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR, SH_RENDER_GRAPH_RESOURCE_FLAGS::INPUT, SH_RENDER_GRAPH_RESOURCE_FLAGS::STORAGE }, true, windowDims.first, windowDims.second, vk::Format::eR32Uint, 1, vk::ImageUsageFlagBits::eTransferSrc);
renderGraph->AddResource("Light Layer Indices And World Normals", { SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR, SH_RENDER_GRAPH_RESOURCE_FLAGS::INPUT, SH_RENDER_GRAPH_RESOURCE_FLAGS::STORAGE }, true, windowDims.first, windowDims.second, vk::Format::eR32G32B32A32Uint, 1, vk::ImageUsageFlagBits::eTransferSrc);
renderGraph->AddResource("Scene", { SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR, SH_RENDER_GRAPH_RESOURCE_FLAGS::INPUT, SH_RENDER_GRAPH_RESOURCE_FLAGS::STORAGE, SH_RENDER_GRAPH_RESOURCE_FLAGS::SHARED }, true, windowDims.first, windowDims.second);
renderGraph->AddResource("SSAO", { SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR, SH_RENDER_GRAPH_RESOURCE_FLAGS::INPUT, SH_RENDER_GRAPH_RESOURCE_FLAGS::STORAGE }, true, windowDims.first, windowDims.second, vk::Format::eR8Unorm);
renderGraph->AddResource("SSAO Blur", { SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR, SH_RENDER_GRAPH_RESOURCE_FLAGS::INPUT, SH_RENDER_GRAPH_RESOURCE_FLAGS::STORAGE }, true, windowDims.first, windowDims.second, vk::Format::eR8Unorm);
@ -231,7 +234,7 @@ namespace SHADE
{
"Position",
"Entity ID",
"Light Layer Indices",
"Light Layer Indices And World Normals",
"Normals",
"Albedo",
"Depth Buffer",
@ -248,7 +251,7 @@ namespace SHADE
auto gBufferSubpass = gBufferNode->AddSubpass(SHGraphicsConstants::RenderGraphEntityNames::GBUFFER_WRITE_SUBPASS.data(), worldViewport, worldRenderer);
gBufferSubpass->AddColorOutput("Position");
gBufferSubpass->AddColorOutput("Entity ID");
gBufferSubpass->AddColorOutput("Light Layer Indices");
gBufferSubpass->AddColorOutput("Light Layer Indices And World Normals");
gBufferSubpass->AddColorOutput("Normals");
gBufferSubpass->AddColorOutput("Albedo");
gBufferSubpass->AddColorOutput("Position World Space");
@ -258,7 +261,7 @@ namespace SHADE
auto gBufferVfxSubpass = gBufferNode->AddSubpass(SHGraphicsConstants::RenderGraphEntityNames::GBUFFER_WRITE_VFX_SUBPASS.data(), worldViewport, worldRenderer);
gBufferVfxSubpass->AddColorOutput("Position");
gBufferVfxSubpass->AddColorOutput("Entity ID");
gBufferVfxSubpass->AddColorOutput("Light Layer Indices");
gBufferVfxSubpass->AddColorOutput("Light Layer Indices And World Normals");
gBufferVfxSubpass->AddColorOutput("Normals");
gBufferVfxSubpass->AddColorOutput("Albedo");
gBufferVfxSubpass->AddColorOutput("Position World Space");
@ -314,7 +317,7 @@ namespace SHADE
auto deferredCompositeNode = renderGraph->AddNode(SHGraphicsConstants::RenderGraphEntityNames::DEFERRED_COMPOSITE_PASS.data(),
{
"Position",
"Light Layer Indices",
"Light Layer Indices And World Normals",
"Normals",
"Albedo",
"Scene",
@ -327,7 +330,7 @@ namespace SHADE
/*-----------------------------------------------------------------------*/
/* DEFERRED COMPOSITE SUBPASS INIT */
/*-----------------------------------------------------------------------*/
auto deferredCompositeCompute = deferredCompositeNode->AddNodeCompute(SHGraphicsConstants::RenderGraphEntityNames::DEFERRED_COMPOSITE_COMPUTE.data(), deferredCompositeShader, {"Position", "Normals", "Albedo", "Light Layer Indices", "SSAO Blur", "Position World Space", "Scene", "Object VFX"}, {}, SHLightingSubSystem::MAX_SHADOWS);
auto deferredCompositeCompute = deferredCompositeNode->AddNodeCompute(SHGraphicsConstants::RenderGraphEntityNames::DEFERRED_COMPOSITE_COMPUTE.data(), deferredCompositeShader, {"Position", "Normals", "Albedo", "Light Layer Indices And World Normals", "SSAO Blur", "Position World Space", "Scene", "Object VFX"}, {}, SHLightingSubSystem::MAX_SHADOWS);
deferredCompositeCompute->AddPreComputeFunction([=](Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex)
{
lightingSubSystem->PrepareShadowMapsForRead(cmdBuffer);
@ -534,12 +537,21 @@ namespace SHADE
ReceiverPtr lightEnableShadowReceivePtr = std::dynamic_pointer_cast<SHEventReceiver>(lightEnableShadowReceiver);
SHEventManager::SubscribeTo(SH_GRAPHICS_LIGHT_ENABLE_SHADOW_EVENT, lightEnableShadowReceivePtr);
std::shared_ptr<SHEventReceiverSpec<SHGraphicsSystem>> lightDeleteEvent
{
std::make_shared<SHEventReceiverSpec<SHGraphicsSystem>>(this, &SHGraphicsSystem::ReceiveLightDeleteEvent)
};
ReceiverPtr lightDeleteReceivePtr = std::dynamic_pointer_cast<SHEventReceiver>(lightDeleteEvent);
SHEventManager::SubscribeTo(SH_GRAPHICS_LIGHT_DELETE_EVENT, lightDeleteReceivePtr);
std::shared_ptr<SHEventReceiverSpec<SHGraphicsSystem>> compileAssetReceiever
{
std::make_shared<SHEventReceiverSpec<SHGraphicsSystem>>(this, &SHGraphicsSystem::ReceiveCompileAssetEvent)
};
ReceiverPtr compileAssetReceivePtr = std::dynamic_pointer_cast<SHEventReceiver>(compileAssetReceiever);
SHEventManager::SubscribeTo(SH_ASSET_COMPILE_EVENT, compileAssetReceivePtr);
}
void SHGraphicsSystem::InitGenericDataAndTexturesDescSet(void) noexcept
@ -674,22 +686,6 @@ namespace SHADE
#endif
}
//static bool shadowAdded = false;
//if (shadowAdded == false && SHInputManager::GetKey(SHInputManager::SH_KEYCODE::B))
//{
// shadowAdded = true;
// auto& lightComps = SHComponentManager::GetDense<SHLightComponent>();
// //if (lightComps.size() > 2)
// //{
// // lightComps[2].SetEnableShadow(true);
// //}
// for (auto& comp : lightComps)
// {
// comp.SetEnableShadow(true);
// }
//}
renderGraph->Begin(frameIndex);
auto cmdBuffer = renderGraph->GetCommandBuffer(frameIndex);
@ -864,36 +860,47 @@ namespace SHADE
SHEventHandle SHGraphicsSystem::ReceiveLightEnableShadowEvent(SHEventPtr eventPtr) noexcept
{
// we need to wait for the device to finish using the graph first
device->WaitIdle();
auto const& EVENT_DATA = reinterpret_cast<const SHEventSpec<SHLightEnableShadowEvent>*>(eventPtr.get())->data;
auto const& EVENT_DATA = reinterpret_cast<const SHEventSpec<SHLightShadowEvent>*>(eventPtr.get())->data;
auto* lightComp = SHComponentManager::GetComponent<SHLightComponent>(EVENT_DATA->lightEntity);
std::string depthResourceName = "ShadowMap_Depth " + std::to_string(EVENT_DATA->lightEntity);
std::string shadowMapResourceName = "ShadowMap " + std::to_string(EVENT_DATA->lightEntity);
Handle<SHSubpass> companionSubpass = renderGraph->GetNode(SHGraphicsConstants::RenderGraphEntityNames::GBUFFER_PASS.data())->GetSubpass(SHGraphicsConstants::RenderGraphEntityNames::GBUFFER_WRITE_SUBPASS);
std::string shadowMapBlurredResourceName = "ShadowMap Blurred" + std::to_string(EVENT_DATA->lightEntity);
Handle<SHSubpass> gBufferWriteSubpass = renderGraph->GetNode(SHGraphicsConstants::RenderGraphEntityNames::GBUFFER_PASS.data())->GetSubpass(SHGraphicsConstants::RenderGraphEntityNames::GBUFFER_WRITE_SUBPASS);
Handle<SHSubpass> gBufferWriteVfxSubpass = renderGraph->GetNode(SHGraphicsConstants::RenderGraphEntityNames::GBUFFER_PASS.data())->GetSubpass(SHGraphicsConstants::RenderGraphEntityNames::GBUFFER_WRITE_VFX_SUBPASS);
if (EVENT_DATA->generateRenderer)
if (EVENT_DATA->enableShadow)
{
// When the light first enables shadow rendering, we need to prepare the relevant objects to render shadows; namely renderpasses and subpasses, pipelines and descriptor sets
if (EVENT_DATA->firstEnable)
{
// we need to wait for the device to finish using the graph first
device->WaitIdle();
// Create new renderer for the light component and give it to the light component
Handle<SHRenderer> newRenderer = resourceManager.Create<SHRenderer>(device, swapchain->GetNumImages(), descPool, SHRenderer::PROJECTION_TYPE::ORTHOGRAPHIC);
lightComp->SetRenderer(newRenderer);
// assign shadow map index to light component
lightComp->SetShadowMapIndex(lightingSubSystem->GetNumShadowMaps());
}
// Add the shadow map resource to the graph
renderGraph->AddResource(depthResourceName, { SH_RENDER_GRAPH_RESOURCE_FLAGS::DEPTH }, false, SHLightingSubSystem::SHADOW_MAP_WIDTH, SHLightingSubSystem::SHADOW_MAP_HEIGHT, vk::Format::eD32Sfloat);
renderGraph->AddResource(shadowMapResourceName, { SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR, SH_RENDER_GRAPH_RESOURCE_FLAGS::INPUT }, false, SHLightingSubSystem::SHADOW_MAP_WIDTH, SHLightingSubSystem::SHADOW_MAP_HEIGHT, vk::Format::eR32G32B32A32Sfloat);
renderGraph->AddResource(shadowMapResourceName, { SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR, SH_RENDER_GRAPH_RESOURCE_FLAGS::INPUT, SH_RENDER_GRAPH_RESOURCE_FLAGS::STORAGE }, false, SHLightingSubSystem::SHADOW_MAP_WIDTH, SHLightingSubSystem::SHADOW_MAP_HEIGHT, vk::Format::eR32G32B32A32Sfloat);
renderGraph->AddResource(shadowMapBlurredResourceName, { SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR, SH_RENDER_GRAPH_RESOURCE_FLAGS::INPUT, SH_RENDER_GRAPH_RESOURCE_FLAGS::STORAGE }, false, SHLightingSubSystem::SHADOW_MAP_WIDTH, SHLightingSubSystem::SHADOW_MAP_HEIGHT, vk::Format::eR32G32B32A32Sfloat);
// link resource to node. This means linking the resource and regenerating the node's renderpass and framebuffer.
auto shadowMapNode = renderGraph->AddNodeAfter(SHGraphicsConstants::RenderGraphEntityNames::SHADOW_MAP_PASS.data() + shadowMapResourceName, {depthResourceName.c_str(), shadowMapResourceName.c_str()}, SHGraphicsConstants::RenderGraphEntityNames::GBUFFER_PASS.data());
auto shadowMapNode = renderGraph->AddNodeAfter(SHGraphicsConstants::RenderGraphEntityNames::SHADOW_MAP_PASS.data() + shadowMapResourceName, { depthResourceName.c_str(), shadowMapResourceName.c_str(), shadowMapBlurredResourceName.c_str() }, SHGraphicsConstants::RenderGraphEntityNames::GBUFFER_PASS.data());
// Add a subpass to render to that shadow map
auto newSubpass = shadowMapNode->RuntimeAddSubpass(shadowMapResourceName + " Subpass", shadowMapViewport, lightComp->GetRenderer());
newSubpass->AddColorOutput(shadowMapResourceName);
newSubpass->AddDepthOutput(depthResourceName, SH_RENDER_GRAPH_RESOURCE_FLAGS::DEPTH);
auto shadowMapDrawSubpass = shadowMapNode->RuntimeAddSubpass(shadowMapResourceName + " Subpass", shadowMapViewport, lightComp->GetRenderer());
shadowMapDrawSubpass->AddColorOutput(shadowMapResourceName);
shadowMapDrawSubpass->AddDepthOutput(depthResourceName, SH_RENDER_GRAPH_RESOURCE_FLAGS::DEPTH);
// add compute pass to blur shadow ma
shadowMapNode->AddNodeCompute(SHGraphicsConstants::RenderGraphEntityNames::SHADOW_MAP_PASS.data() + shadowMapResourceName, shadowMapBlurCS, { shadowMapResourceName.c_str(), shadowMapBlurredResourceName.c_str() });
// regenerate the node
shadowMapNode->RuntimeStandaloneRegenerate();
@ -910,19 +917,63 @@ namespace SHADE
tempLibrary.Init(device);
tempLibrary.CreateGraphicsPipelines
(
{ shadowMapVS, shadowMapFS }, shadowMapNode->GetRenderpass(), newSubpass,
{ shadowMapVS, shadowMapFS }, shadowMapNode->GetRenderpass(), shadowMapDrawSubpass,
SHGraphicsPredefinedData::SystemType::BATCHING,
SHGraphicsPredefinedData::GetShadowMapViState(), rasterState
);
shadowMapPipeline = tempLibrary.GetGraphicsPipeline({ shadowMapVS, shadowMapFS, newSubpass });
shadowMapPipeline = tempLibrary.GetGraphicsPipeline({ shadowMapVS, shadowMapFS, shadowMapDrawSubpass });
}
newSubpass->SetCompanionSubpass(companionSubpass, shadowMapPipeline); // set companion subpass and pipeline
shadowMapDrawSubpass->AddCompanionSubpass(gBufferWriteSubpass, shadowMapPipeline); // set companion subpass and pipeline
shadowMapDrawSubpass->AddCompanionSubpass(gBufferWriteVfxSubpass, shadowMapPipeline); // set companion subpass and pipeline
// add the shadow map to the lighting system
uint32_t const NEW_SHADOW_MAP_INDEX = lightingSubSystem->AddShadowMap(renderGraph->GetRenderGraphResource(shadowMapResourceName), EVENT_DATA->lightEntity);
// add the shadow map and the blurred version to the lighting system
uint32_t const NEW_SHADOW_MAP_INDEX = lightingSubSystem->AddShadowMap(renderGraph->GetRenderGraphResource(shadowMapBlurredResourceName), EVENT_DATA->lightEntity);
// Get deferred composite node compute and modify descriptor set
auto nodeCompute = renderGraph->GetNode(SHGraphicsConstants::RenderGraphEntityNames::DEFERRED_COMPOSITE_PASS.data())->GetNodeCompute(SHGraphicsConstants::RenderGraphEntityNames::DEFERRED_COMPOSITE_COMPUTE.data());
nodeCompute->ModifyWriteDescImageComputeResource(SHGraphicsConstants::DescriptorSetBindings::SHADOW_MAP_IMAGE_SAMPLER_DATA, lightingSubSystem->GetViewSamplerLayout(NEW_SHADOW_MAP_INDEX), NEW_SHADOW_MAP_INDEX);
}
else
{
// get the shadow map node
renderGraph->GetNode (SHGraphicsConstants::RenderGraphEntityNames::SHADOW_MAP_PASS.data() + shadowMapResourceName)->SetDynamicActive(true);
}
}
else
{
// get the shadow map node
renderGraph->GetNode(SHGraphicsConstants::RenderGraphEntityNames::SHADOW_MAP_PASS.data() + shadowMapResourceName)->SetDynamicActive(false);
}
return eventPtr->handle;
}
SHEventHandle SHGraphicsSystem::ReceiveLightDeleteEvent(SHEventPtr eventPtr) noexcept
{
// we need to wait for the device to finish using the graph first
device->WaitIdle();
auto const& EVENT_DATA = reinterpret_cast<const SHEventSpec<SHLightShadowEvent>*>(eventPtr.get())->data;
auto* lightComp = SHComponentManager::GetComponent<SHLightComponent>(EVENT_DATA->lightEntity);
if (lightComp && lightComp->GetShadowMapIndex() != SHLightData::INVALID_SHADOW_MAP_INDEX)
{
std::string depthResourceName = "ShadowMap_Depth " + std::to_string(EVENT_DATA->lightEntity);
std::string shadowMapResourceName = "ShadowMap " + std::to_string(EVENT_DATA->lightEntity);
std::string shadowMapBlurredResourceName = "ShadowMap Blurred" + std::to_string(EVENT_DATA->lightEntity);
// Remove render graph node
//renderGraph->RemoveNode(SHGraphicsConstants::RenderGraphEntityNames::SHADOW_MAP_PASS.data() + shadowMapResourceName);
// Remove render graph resource
//renderGraph->RemoveResource(depthResourceName);
//renderGraph->RemoveResource(shadowMapResourceName);
//renderGraph->RemoveResource(shadowMapBlurredResourceName);
// Register light component shadow map index into light system as recyclable
lightingSubSystem->RemoveShadowMap (EVENT_DATA->lightEntity);
}
return eventPtr->handle;
}

View File

@ -182,6 +182,7 @@ namespace SHADE
/* Light functions */
/*-----------------------------------------------------------------------*/
SHEventHandle ReceiveLightEnableShadowEvent (SHEventPtr eventPtr) noexcept;
SHEventHandle ReceiveLightDeleteEvent (SHEventPtr eventPtr) noexcept;
/*-----------------------------------------------------------------------*/
/* Asset Events */
@ -481,6 +482,7 @@ namespace SHADE
Handle<SHVkShaderModule> shadowMapFS;
Handle<SHVkShaderModule> trajectoryVS;
Handle<SHVkShaderModule> trajectoryFS;
Handle<SHVkShaderModule> shadowMapBlurCS;
// Fonts
Handle<SHFont> testFont;

View File

@ -21,6 +21,14 @@ namespace SHADE
void SHLightComponent::OnDestroy(void)
{
if (lightData.shadowMapIndex != SHLightData::INVALID_SHADOW_MAP_INDEX)
{
// Create new event and broadcast it
SHDeleteLightEvent newEvent;
newEvent.lightEntity = GetEID();
SHEventManager::BroadcastEvent<SHDeleteLightEvent>(newEvent, SH_GRAPHICS_LIGHT_DELETE_EVENT);
}
}
@ -113,15 +121,21 @@ namespace SHADE
lightData.castShadows = flag;
// If the flag is true
if (flag && lightData.shadowMapIndex == SHLightData::INVALID_SHADOW_MAP_INDEX)
if (flag)
{
// Create new event and broadcast it
SHLightEnableShadowEvent newEvent;
SHLightShadowEvent newEvent;
newEvent.lightEntity = GetEID();
newEvent.generateRenderer = static_cast<bool>(!renderer);
SHEventManager::BroadcastEvent<SHLightEnableShadowEvent>(newEvent, SH_GRAPHICS_LIGHT_ENABLE_SHADOW_EVENT);
// If shadow map index is invalid, that means light is enabling shadow for the first time.
newEvent.firstEnable = (lightData.shadowMapIndex == SHLightData::INVALID_SHADOW_MAP_INDEX);
// pass the flag to the event
newEvent.enableShadow = flag;
SHEventManager::BroadcastEvent<SHLightShadowEvent>(newEvent, SH_GRAPHICS_LIGHT_ENABLE_SHADOW_EVENT);
}
}
void SHLightComponent::SetRenderer(Handle<SHRenderer> newRenderer) noexcept
@ -190,6 +204,16 @@ namespace SHADE
return renderer;
}
bool SHLightComponent::GetEnableShadow(void) const noexcept
{
return lightData.castShadows;
}
uint32_t SHLightComponent::GetShadowMapIndex(void) const noexcept
{
return lightData.shadowMapIndex;
}
}
RTTR_REGISTRATION
@ -212,5 +236,6 @@ RTTR_REGISTRATION
.property("Color", &SHLightComponent::GetColor, &SHLightComponent::SetColor)
.property("Layer", &SHLightComponent::GetCullingMask, &SHLightComponent::SetCullingMask)
.property("Strength", &SHLightComponent::GetStrength, &SHLightComponent::SetStrength)
.property("Casting Shadows", &SHLightComponent::GetEnableShadow, &SHLightComponent::SetEnableShadow)
;
}

View File

@ -69,6 +69,9 @@ namespace SHADE
//uint32_t GetIndexInBuffer (void) const noexcept;
float GetStrength (void) const noexcept;
Handle<SHRenderer> GetRenderer (void) const noexcept;
bool GetEnableShadow (void) const noexcept;
uint32_t GetShadowMapIndex (void) const noexcept;
RTTR_ENABLE()
};
}

View File

@ -53,6 +53,7 @@ namespace SHADE
SHVec4 transformedDir = SHMatrix::Transpose(viewMat) * SHVec4(lightData.direction[0], lightData.direction[1], lightData.direction[2], 0.0f);
lightPtr->cullingMask = lightData.cullingMask;
lightPtr->directionWorld = SHVec4 (lightData.direction.x, lightData.direction.y, lightData.direction.z, 1.0f);
lightPtr->direction = SHVec3(transformedDir.x, transformedDir.y, transformedDir.z);
//lightPtr->direction = lightData.direction;
lightPtr->diffuseColor = lightData.color;
@ -525,7 +526,7 @@ namespace SHADE
if (auto renderer = light.GetRenderer())
{
//SHMatrix orthoMatrix = SHMatrix::OrthographicRH()
renderer->UpdateDataManual(frameIndex, GetViewMatrix(&light), SHMatrix::OrthographicLH(12.0f, 12.0f, 1.0f, 80.0f));
renderer->UpdateDataManual(frameIndex, GetViewMatrix(&light), SHMatrix::OrthographicLH(15.0f, 15.0f, 1.0f, 80.0f));
}
auto enumValue = SHUtilities::ConvertEnum(light.GetLightData().type);
@ -598,49 +599,25 @@ namespace SHADE
{
//Bind descriptor set(We bind at an offset because the buffer holds NUM_FRAME_BUFFERS sets of data).
cmdBuffer->BindDescriptorSet(lightingDataDescSet, SH_PIPELINE_TYPE::COMPUTE, setIndex, { dynamicOffsets[frameIndex] });
}
uint32_t SHLightingSubSystem::AddShadowMap(Handle<SHRenderGraphResource> newShadowMap, EntityID lightEntity) noexcept
uint32_t SHLightingSubSystem::AddShadowMap(Handle<SHRenderGraphResource> newShadowMapBlurred, EntityID lightEntity) noexcept
{
// Add to container of shadow maps
shadowMapIndexing.emplace(lightEntity, static_cast<uint32_t> (shadowMaps.size()));
shadowMaps.emplace_back(newShadowMap);
uint32_t usedIndex = 0;
// Just use the image view stored in the resource
Handle<SHVkImageView> const NEW_IMAGE_VIEW = newShadowMap->GetImageView();
// Prepare to write to descriptor
shadowMapImageSamplers.emplace_back(NEW_IMAGE_VIEW, shadowMapSampler, vk::ImageLayout::eShaderReadOnlyOptimal);
// Update descriptor set
//static constexpr uint32_t SHADOW_MAP_DESC_SET_INDEX = 0;
//uint32_t const SHADOW_MAP_DESC_ARRAY_INDEX = static_cast<uint32_t>(shadowMapImageSamplers.size()) - 1u;
//shadowMapDescriptorSet->ModifyWriteDescImage
//(
// SHADOW_MAP_DESC_SET_INDEX,
// SHGraphicsConstants::DescriptorSetBindings::SHADOW_MAP_IMAGE_SAMPLER_DATA,
// shadowMapImageSamplers[SHADOW_MAP_DESC_ARRAY_INDEX],
// SHADOW_MAP_DESC_ARRAY_INDEX
//);
//// TODO: Definitely can be optimized by writing a function that modifies a specific descriptor in the array
//shadowMapDescriptorSet->UpdateDescriptorSetImages
//(
// SHADOW_MAP_DESC_SET_INDEX,
// SHGraphicsConstants::DescriptorSetBindings::SHADOW_MAP_IMAGE_SAMPLER_DATA
//);
Handle<SHVkImageView> const NEW_IMAGE_VIEW = newShadowMapBlurred->GetImageView();
// add to barriers
shadowMapMemoryBarriers.push_back (vk::ImageMemoryBarrier
{
.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eColorAttachmentRead,
.srcAccessMask = vk::AccessFlagBits::eShaderWrite,
.dstAccessMask = vk::AccessFlagBits::eShaderRead,
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
.oldLayout = vk::ImageLayout::eGeneral,
.newLayout = vk::ImageLayout::eGeneral,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = newShadowMap->GetImage()->GetVkImage(),
.image = newShadowMapBlurred->GetImage()->GetVkImage(),
.subresourceRange = vk::ImageSubresourceRange
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
@ -651,38 +628,76 @@ namespace SHADE
}
});
if (recyclableIndices.empty())
{
shadowMaps.emplace_back(newShadowMapBlurred);
// Prepare to write to descriptor
shadowMapImageSamplers.emplace_back(NEW_IMAGE_VIEW, shadowMapSampler, vk::ImageLayout::eShaderReadOnlyOptimal);
// Add to container of shadow maps
shadowMapIndexing.emplace(lightEntity, static_cast<uint32_t> (shadowMaps.size()));
usedIndex = static_cast<uint32_t>(shadowMapImageSamplers.size()) - 1u;
}
else
{
uint32_t usedIndex = recyclableIndices.top();
recyclableIndices.pop();
// Recycle the slot in the vector of shadow maps
shadowMaps[usedIndex] = newShadowMapBlurred;
// register indexing for shadow maps
shadowMapIndexing.emplace(lightEntity, usedIndex);
// Recycle the slot in the vector of image view, samplers and layout
shadowMapImageSamplers[usedIndex] = std::make_tuple(NEW_IMAGE_VIEW, shadowMapSampler, vk::ImageLayout::eShaderReadOnlyOptimal);
}
// return new index of shadow map
return static_cast<uint32_t>(shadowMapImageSamplers.size()) - 1u;
return usedIndex;
}
void SHLightingSubSystem::RemoveShadowMap(EntityID lightEntity) noexcept
{
auto* lightComp = SHComponentManager::GetComponent<SHLightComponent>(lightEntity);
if (lightComp && shadowMapIndexing.contains(lightEntity))
{
uint32_t shadowMapIndex = shadowMapIndexing[lightEntity];
// Recycle the shadow map index used by the component
RecycleShadowMapIndex(lightComp);
// Not removed from container, can recycle
shadowMaps[shadowMapIndex] = {};
// Not removed from container, can recycle
shadowMapImageSamplers[shadowMapIndex] = {};
// Remove from barriers
shadowMapMemoryBarriers.erase(shadowMapMemoryBarriers.begin() + shadowMapIndex);
// remove from map for indexing
shadowMapIndexing.erase(lightEntity);
}
}
void SHLightingSubSystem::RecycleShadowMapIndex(SHLightComponent* lightComp) noexcept
{
// if index is recyclable, recycle it
if (lightComp && lightComp->GetShadowMapIndex() != SHLightData::INVALID_SHADOW_MAP_INDEX)
recyclableIndices.push (lightComp->GetShadowMapIndex());
}
void SHLightingSubSystem::PrepareShadowMapsForRead(Handle<SHVkCommandBuffer> cmdBuffer) noexcept
{
// Issue barrier to transition shadow maps for reading in compute shader
cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eComputeShader, {}, {}, {}, shadowMapMemoryBarriers);
cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eComputeShader, {}, {}, {}, shadowMapMemoryBarriers);
}
//void SHLightingSubSystem::HandleResize(Handle<SHRenderGraphNodeCompute> compute) noexcept
//{
// uint32_t const NUM_SHADOW_MAPS = static_cast<uint32_t>(shadowMaps.size());
// for (uint32_t i = 0; i < NUM_SHADOW_MAPS; ++i)
// {
// // Just use the image view stored in the resource
// Handle<SHVkImageView> const NEW_IMAGE_VIEW = shadowMaps[i]->GetImageView();
// // set new image view
// std::get<Handle<SHVkImageView>>(shadowMapImageSamplers[i]) = NEW_IMAGE_VIEW;
// // Set image for barrier
// shadowMapMemoryBarriers[i].image = shadowMaps[i]->GetImage()->GetVkImage();
// }
// if (NUM_SHADOW_MAPS > 0)
// {
// // modify descriptors in render graph node compute
// compute->ModifyWriteDescImageComputeResource (SHGraphicsConstants::DescriptorSetBindings::SHADOW_MAP_IMAGE_SAMPLER_DATA, shadowMapImageSamplers);
// }
//}
Handle<SHVkDescriptorSetGroup> SHLightingSubSystem::GetLightDataDescriptorSet(void) const noexcept
{
return lightingDataDescSet;

View File

@ -9,6 +9,7 @@
#include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h"
#include "Graphics/RenderGraph/SHRenderGraphResource.h"
#include "ECS_Base/SHECSMacros.h"
#include <stack>
namespace SHADE
@ -28,6 +29,9 @@ namespace SHADE
// Represents how the data will be interpreted in GPU. we want to copy to a container of these before passing to GPU.
struct SHDirectionalLightData
{
//! Direction of the light
SHVec4 directionWorld;
//! Direction of the light
SHVec3 direction;
@ -180,10 +184,6 @@ namespace SHADE
//! Shadow maps for every light that casts a shadow Order here doesn't matter. We just want to store it
std::vector<Handle<SHRenderGraphResource>> shadowMaps;
//! Descriptor sets required to be given to the compute shader for shadow calculation. This will be a descriptor array.
//! It will also be preallocated.
//Handle<SHVkDescriptorSetGroup> shadowMapDescriptorSet;
//! Combined image samplers for the texture descriptors
std::vector<std::tuple<Handle<SHVkImageView>, Handle<SHVkSampler>, vk::ImageLayout>> shadowMapImageSamplers;
@ -191,6 +191,9 @@ namespace SHADE
//! to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
std::vector<vk::ImageMemoryBarrier> shadowMapMemoryBarriers;
//! recycled indices after deleting lights with shadows
std::stack<uint32_t> recyclableIndices;
//! Resource hub from Graphics System
SHResourceHub* resourceHub;
@ -213,7 +216,9 @@ namespace SHADE
void Run (SHMatrix const& viewMat, uint32_t frameIndex) noexcept;
void Exit (void) noexcept;
void BindDescSet (Handle<SHVkCommandBuffer> cmdBuffer, uint32_t setIndex, uint32_t frameIndex) noexcept;
uint32_t AddShadowMap (Handle<SHRenderGraphResource> newShadowMap, EntityID lightEntity) noexcept;
uint32_t AddShadowMap (Handle<SHRenderGraphResource> newShadowMapBlurred, EntityID lightEntity) noexcept;
void RemoveShadowMap (EntityID lightEntity) noexcept;
void RecycleShadowMapIndex (SHLightComponent* lightComp) noexcept;
void PrepareShadowMapsForRead (Handle<SHVkCommandBuffer> cmdBuffer) noexcept;
//void HandleResize (Handle<SHRenderGraphNodeCompute> compute) noexcept;
//void RemoveShadowMap (uint32_t index) noexcept;

View File

@ -81,7 +81,8 @@ namespace SHADE
renderGraphStorage->logicalDevice->WaitIdle();
uint64_t handleID = renderGraphStorage->graphResources->at (resourceName).GetId().Raw;
auto resourceHdl = renderGraphStorage->graphResources->at(resourceName);
uint64_t handleID = resourceHdl.GetId().Raw;
// Record nodes that will be affected
std::vector<uint32_t> affectedNodes{};
@ -89,7 +90,7 @@ namespace SHADE
// Detach resource from all nodes if applicable
for (uint32_t i = 0; i < nodes.size(); ++i)
{
if (nodes[i]->DetachResource(resourceName, handleID))
if (nodes[i] && nodes[i]->DetachResource(resourceName, handleID))
affectedNodes.emplace_back(i);
}
@ -109,6 +110,8 @@ namespace SHADE
renderGraphStorage->graphResources->at(resourceName).Free();
renderGraphStorage->graphResources->erase (resourceName);
resourceHdl.Free ();
/*
* IMPORTANT NOTES
*
@ -121,6 +124,21 @@ namespace SHADE
*/
}
void SHRenderGraph::RemoveNode(std::string nodeName) noexcept
{
if (nodeIndexing.contains(nodeName))
{
// wait for device to stop
renderGraphStorage->logicalDevice->WaitIdle();
// Get handle to node since it exists
auto nodeHdl = nodes[nodeIndexing[nodeName]];
nodeHdl.Free();
nodeIndexing.erase(nodeName);
}
}
void SHRenderGraph::LinkNonOwningResource(Handle<SHRenderGraph> resourceOrigin, std::string resourceName) noexcept
{
// resource to link
@ -479,7 +497,7 @@ namespace SHADE
// get target node
auto targetNode = nodes.begin() + nodeIndexing.at(nodeToAddAfter);
auto node = nodes.insert(targetNode, renderGraphStorage->resourceHub->Create<SHRenderGraphNode>(nodeName, renderGraphStorage, std::move(descInitParams), std::vector<Handle<SHRenderGraphNode>>()));
auto node = nodes.insert(targetNode, renderGraphStorage->resourceHub->Create<SHRenderGraphNode>(nodeName, renderGraphStorage, std::move(descInitParams), std::vector<Handle<SHRenderGraphNode>>(), true));
ReindexNodes ();
return *node;
@ -583,6 +601,10 @@ namespace SHADE
for (auto& node : nodes)
{
// if node is dynamic check for active flag
if ((node->isDynamic) ? !node->dynamicIsActive : false)
continue;
if (node->renderpass)
{
// bind static global data

View File

@ -112,6 +112,7 @@ namespace SHADE
);
void RemoveResource (std::string resourceName) noexcept;
void RemoveNode (std::string nodeName) noexcept;
void LinkNonOwningResource
(

View File

@ -292,7 +292,7 @@ namespace SHADE
*/
/***************************************************************************/
SHRenderGraphNode::SHRenderGraphNode(std::string nodeName, Handle<SHRenderGraphStorage> renderGraphStorage, std::vector<SHAttachmentDescInitParams> attDescInitParams, std::vector<Handle<SHRenderGraphNode>> predecessors) noexcept
SHRenderGraphNode::SHRenderGraphNode(std::string nodeName, Handle<SHRenderGraphStorage> renderGraphStorage, std::vector<SHAttachmentDescInitParams> attDescInitParams, std::vector<Handle<SHRenderGraphNode>> predecessors, bool inIsDynamic) noexcept
: graphStorage{ renderGraphStorage}
, renderpass{}
, framebuffers{}
@ -305,6 +305,8 @@ namespace SHADE
, configured{ false }
, nodeComputes{}
, name { std::move(nodeName) }
, dynamicIsActive {true} // default set to true
, isDynamic{inIsDynamic}
{
// pipeline library initialization
pipelineLibrary.Init(graphStorage->logicalDevice);
@ -368,6 +370,8 @@ namespace SHADE
, nodeComputes{ std::move(rhs.nodeComputes) }
, name { std::move(rhs.name) }
, ISelfHandle<SHRenderGraphNode>{std::move(rhs)}
, dynamicIsActive {rhs.dynamicIsActive}
, isDynamic {rhs.isDynamic}
{
rhs.renderpass = {};
@ -393,12 +397,30 @@ namespace SHADE
spDeps = std::move(rhs.spDeps);
nodeComputes = std::move(rhs.nodeComputes);
name = std::move(rhs.name);
dynamicIsActive = rhs.dynamicIsActive;
isDynamic = rhs.isDynamic;
rhs.renderpass = {};
return *this;
}
SHRenderGraphNode::~SHRenderGraphNode(void) noexcept
{
if (renderpass)
renderpass.Free();
for (auto& framebuffer : framebuffers)
framebuffer.Free();
for (auto& subpass : subpasses)
subpass.Free();
// Too tired to test, its nearing end of milestone 3....
//for (auto& nc : nodeComputes)
// nc.Free();
}
/***************************************************************************/
/*!
@ -798,12 +820,18 @@ namespace SHADE
/***************************************************************************/
void SHRenderGraphNode::RuntimeStandaloneRegenerate(void) noexcept
{
AddDummySubpassIfNeeded();
StandaloneConfigureAttDesc(false);
ConfigureSubpasses();
CreateRenderpass();
CreateFramebuffer();
}
void SHRenderGraphNode::SetDynamicActive(bool active) noexcept
{
dynamicIsActive = active;
}
/***************************************************************************/
/*!
@ -849,4 +877,9 @@ namespace SHADE
return {};
}
bool SHRenderGraphNode::GetDynamicActive(void) const noexcept
{
return dynamicIsActive;
}
}

View File

@ -80,6 +80,13 @@ namespace SHADE
//! Whether or not the node has been configured already or not
bool configured;
//! Whether or not the rendergraph should be executed. Should ONLY be used for dynamic
//! nodes that are independent from other nodes.
bool dynamicIsActive;
//! True if the function is dynamic
bool isDynamic;
//! Manages batching for this RenderPass
SHBatcher batcher;
@ -101,9 +108,10 @@ namespace SHADE
/*-----------------------------------------------------------------------*/
/* CTORS AND DTORS */
/*-----------------------------------------------------------------------*/
SHRenderGraphNode(std::string nodeName, Handle<SHRenderGraphStorage> renderGraphStorage, std::vector<SHAttachmentDescInitParams> attDescInitParams, std::vector<Handle<SHRenderGraphNode>> predecessors) noexcept;
SHRenderGraphNode(std::string nodeName, Handle<SHRenderGraphStorage> renderGraphStorage, std::vector<SHAttachmentDescInitParams> attDescInitParams, std::vector<Handle<SHRenderGraphNode>> predecessors, bool inIsDynamic = false) noexcept;
SHRenderGraphNode(SHRenderGraphNode&& rhs) noexcept;
SHRenderGraphNode& operator= (SHRenderGraphNode&& rhs) noexcept;
~SHRenderGraphNode (void) noexcept;
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
@ -123,11 +131,15 @@ namespace SHADE
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
void SetDynamicActive(bool active) noexcept;
Handle<SHVkRenderpass> GetRenderpass(void) const noexcept;
Handle<SHSubpass> GetSubpass(std::string_view subpassName) const noexcept;
Handle<SHRenderGraphResource> GetResource (uint32_t resourceIndex) const noexcept;
std::vector<Handle<SHRenderGraphResource>> const& GetResources (void) const noexcept;
Handle<SHRenderGraphNodeCompute> GetNodeCompute (std::string nodeComputeName) const noexcept;
bool GetDynamicActive (void) const noexcept;
friend class SHRenderGraph;
};

View File

@ -77,9 +77,17 @@ namespace SHADE
, name { rhs.name }
, viewport {rhs.viewport}
, renderer {rhs.renderer}
, companionSubpass {rhs.companionSubpass}
, companionSubpasses {std::move (rhs.companionSubpasses)}
, dummyPipelineLayout{rhs.dummyPipelineLayout}
{
superBatch = std::move(rhs.superBatch);
rhs.superBatch = {};
inputDescriptorLayout = std::move(rhs.inputDescriptorLayout);
rhs.inputDescriptorLayout = {};
dummyPipelineLayout = std::move(rhs.dummyPipelineLayout);
rhs.dummyPipelineLayout = {};
}
@ -101,7 +109,6 @@ namespace SHADE
subpassIndex = std::move(rhs.subpassIndex);
parentNode = std::move(rhs.parentNode);
superBatch = std::move(rhs.superBatch);
colorReferences = std::move(rhs.colorReferences);
depthReferences = std::move(rhs.depthReferences);
inputReferences = std::move(rhs.inputReferences);
@ -115,13 +122,39 @@ namespace SHADE
name = std::move(rhs.name);
renderer = rhs.renderer;
viewport = rhs.viewport;
companionSubpass = rhs.companionSubpass;
companionSubpasses = rhs.companionSubpasses;
dummyPipelineLayout = rhs.dummyPipelineLayout;
superBatch = std::move(rhs.superBatch);
rhs.superBatch = {};
inputDescriptorLayout = std::move(rhs.inputDescriptorLayout);
rhs.inputDescriptorLayout = {};
dummyPipelineLayout = std::move(rhs.dummyPipelineLayout);
rhs.dummyPipelineLayout = {};
return *this;
}
SHSubpass::~SHSubpass(void) noexcept
{
graphStorage->logicalDevice->WaitIdle();
for (auto& set : inputImageDescriptorSets)
set.Free();
if (inputDescriptorLayout)
inputDescriptorLayout.Free();
if (dummyPipelineLayout)
dummyPipelineLayout.Free();
// not working
//if (superBatch)
// superBatch.Free();
}
/***************************************************************************/
/*!
@ -237,17 +270,19 @@ namespace SHADE
BindInputDescriptorSets (commandBuffer, descMappings.at(SHPredefinedDescriptorTypes::RENDER_GRAPH_RESOURCE), frameIndex);
// If companion subpass is not a valid handle, render super batch normally
if (!companionSubpass.companion)
if (companionSubpasses.empty())
{
// Draw all the batches
superBatch->Draw(commandBuffer, frameIndex);
}
else
{
for (auto& companion : companionSubpasses)
{
// if not bind pipeline for companion and and execute draw command
commandBuffer->BindPipeline(companionSubpass.pipeline);
companionSubpass.companion->superBatch->Draw(commandBuffer, frameIndex, false);
commandBuffer->BindPipeline(companion.pipeline);
companion.subpass->superBatch->Draw(commandBuffer, frameIndex, false);
}
}
}
@ -493,10 +528,11 @@ namespace SHADE
subpassIndex = index;
}
void SHSubpass::SetCompanionSubpass(Handle<SHSubpass> companion, Handle<SHVkPipeline> pipeline) noexcept
void SHSubpass::AddCompanionSubpass(Handle<SHSubpass> companion, Handle<SHVkPipeline> pipeline) noexcept
{
companionSubpass.companion = companion;
companionSubpass.pipeline = pipeline;
companionSubpasses.push_back(CompanionSubpass{companion, pipeline});
//companionSubpass.companion = companion;
//companionSubpass.pipeline = pipeline;
}
/***************************************************************************/

View File

@ -33,7 +33,7 @@ namespace SHADE
struct CompanionSubpass
{
// subpass whose data will be borrowed to draw
Handle<SHSubpass> companion;
Handle<SHSubpass> subpass;
// Pipeline that will be used for all the draw calls from all batches of the companion subpass
Handle<SHVkPipeline> pipeline;
@ -114,7 +114,8 @@ namespace SHADE
//! Optional component to a companion subpass. If the subpass handle of this object
//! is valid, the subpass will be drawn using this companion's data.
CompanionSubpass companionSubpass;
//CompanionSubpass companionSubpass;
std::vector<CompanionSubpass> companionSubpasses;
private:
/*-----------------------------------------------------------------------*/
@ -129,6 +130,7 @@ namespace SHADE
SHSubpass(const std::string& name, Handle<SHViewport> inViewport, Handle<SHRenderer> inRenderer, Handle<SHRenderGraphStorage> renderGraphStorage, Handle<SHRenderGraphNode> const& parent, uint32_t index, std::unordered_map<uint64_t, uint32_t> const* mapping) noexcept;
SHSubpass(SHSubpass&& rhs) noexcept;
SHSubpass& operator=(SHSubpass&& rhs) noexcept;
~SHSubpass(void) noexcept;
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
@ -165,7 +167,7 @@ namespace SHADE
/*-----------------------------------------------------------------------*/
/* PUBLIC SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
void SetCompanionSubpass (Handle<SHSubpass> companion, Handle<SHVkPipeline> pipeline) noexcept;
void AddCompanionSubpass (Handle<SHSubpass> companion, Handle<SHVkPipeline> pipeline) noexcept;
Handle<SHRenderGraphNode> GetParentNode(void) const noexcept;
SHSubPassIndex GetIndex() const noexcept;

View File

@ -34,6 +34,7 @@ namespace SHADE
, flags { 0 }
, drag { 0.01f }
, angularDrag { 0.1f }
, gravityScale{ 1.0f }
, rigidBody { nullptr }
{
// Default flags