SP3-16 Slight rework to SHColour for compatibility #159

Merged
direnbharwani merged 7 commits from SP3-16-Math into main 2022-11-02 02:29:07 +08:00
181 changed files with 9336 additions and 2362 deletions
Showing only changes of commit 54b7fa7cd3 - Show all commits

5
.editorconfig Normal file
View File

@ -0,0 +1,5 @@
root = true
[*.{c,cpp,h,hpp}]
indent_style = space
indent_size = 2

View File

@ -1,3 +1,3 @@
Name: Cube.003 Name: Cube.003
ID: 110152941 ID: 71245919
Type: 6 Type: 4

View File

@ -1,3 +1,3 @@
Name: Cube.012 Name: Cube.012
ID: 107348815 ID: 80365422
Type: 6 Type: 4

View File

@ -4,13 +4,13 @@ Size=1920,20
Collapsed=0 Collapsed=0
[Window][SHEditorMenuBar] [Window][SHEditorMenuBar]
Pos=0,24 Pos=0,48
Size=1920,1036 Size=1920,1012
Collapsed=0 Collapsed=0
[Window][Hierarchy Panel] [Window][Hierarchy Panel]
Pos=0,120 Pos=0,197
Size=225,940 Size=308,863
Collapsed=0 Collapsed=0
DockId=0x00000004,0 DockId=0x00000004,0
@ -20,29 +20,96 @@ Size=400,400
Collapsed=0 Collapsed=0
[Window][Inspector] [Window][Inspector]
Pos=1686,24 Pos=1528,48
Size=234,1036 Size=392,1012
Collapsed=0 Collapsed=0
DockId=0x00000006,0 DockId=0x00000006,0
[Window][Profiler] [Window][Profiler]
Pos=0,24 Pos=0,48
Size=225,94 Size=308,147
Collapsed=0 Collapsed=0
DockId=0x00000003,0 DockId=0x00000003,0
[Window][Viewport] [Window][Viewport]
Pos=227,24 Pos=227,48
Size=1457,1036 Size=1457,1012
Collapsed=0 Collapsed=0
DockId=0x00000002,0 DockId=0x0000000B,0
[Window][ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌð‡Žoû]
Pos=60,60
Size=32,64
Collapsed=0
[Window][ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ]
Pos=60,60
Size=999,581
Collapsed=0
[Window][ð‡]
Pos=60,60
Size=32,64
Collapsed=0
[Window][ÌÌÌÌ]
Pos=60,60
Size=553,422
Collapsed=0
[Window][]
Pos=60,60
Size=770,394
Collapsed=0
[Window][ Viewport]
Pos=227,48
Size=1457,1012
Collapsed=0
DockId=0x0000000B,0
[Window][ Viewport]
Pos=227,48
Size=1457,1012
Collapsed=0
DockId=0x0000000B,0
[Window][î<> Viewport]
Pos=310,48
Size=1216,715
Collapsed=0
DockId=0x0000000B,0
[Window][V]
Pos=310,722
Size=1501,338
Collapsed=0
DockId=0x00000008,0
[Window][p£€Ê]
Pos=310,750
Size=1501,310
Collapsed=0
DockId=0x0000000A,0
[Window][ Asset Browser]
Pos=310,765
Size=1216,295
Collapsed=0
DockId=0x0000000C,0
[Docking][Data] [Docking][Data]
DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=8,55 Size=1920,1036 Split=X DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=8,79 Size=1920,1012 Split=X
DockNode ID=0x00000005 Parent=0xC5C9B8AB SizeRef=1684,1036 Split=X DockNode ID=0x00000005 Parent=0xC5C9B8AB SizeRef=1526,1036 Split=X
DockNode ID=0x00000001 Parent=0x00000005 SizeRef=225,1036 Split=Y Selected=0x1E6EB881 DockNode ID=0x00000001 Parent=0x00000005 SizeRef=308,1036 Split=Y Selected=0x1E6EB881
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=225,94 Selected=0x1E6EB881 DockNode ID=0x00000003 Parent=0x00000001 SizeRef=225,147 Selected=0x1E6EB881
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=225,940 Selected=0xE096E5AE DockNode ID=0x00000004 Parent=0x00000001 SizeRef=225,863 Selected=0xE096E5AE
DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1293,1036 CentralNode=1 Selected=0x13926F0B DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1216,1036 Split=Y Selected=0xB41284E7
DockNode ID=0x00000006 Parent=0xC5C9B8AB SizeRef=234,1036 Selected=0xE7039252 DockNode ID=0x00000007 Parent=0x00000002 SizeRef=1501,672 Split=Y Selected=0xB41284E7
DockNode ID=0x00000009 Parent=0x00000007 SizeRef=1501,700 Split=Y Selected=0xB41284E7
DockNode ID=0x0000000B Parent=0x00000009 SizeRef=1501,715 CentralNode=1 Selected=0xB41284E7
DockNode ID=0x0000000C Parent=0x00000009 SizeRef=1501,295 Selected=0xB128252A
DockNode ID=0x0000000A Parent=0x00000007 SizeRef=1501,310 Selected=0xD446F7B6
DockNode ID=0x00000008 Parent=0x00000002 SizeRef=1501,338 Selected=0xD9F31532
DockNode ID=0x00000006 Parent=0xC5C9B8AB SizeRef=392,1036 Selected=0xE7039252

View File

@ -1,54 +0,0 @@
[Window][MainStatusBar]
Pos=0,1060
Size=1920,20
Collapsed=0
[Window][SHEditorMenuBar]
Pos=0,48
Size=1920,1012
Collapsed=0
[Window][Hierarchy Panel]
Pos=0,142
Size=387,918
Collapsed=0
DockId=0x00000004,0
[Window][Debug##Default]
Pos=60,60
Size=400,400
Collapsed=0
[Window][Inspector]
Pos=1649,48
Size=271,1012
Collapsed=0
DockId=0x00000006,0
[Window][Profiler]
Pos=0,48
Size=387,92
Collapsed=0
DockId=0x00000003,0
[Window][Viewport]
Pos=648,48
Size=2519,1319
Collapsed=0
DockId=0x00000002,0
[Window][ Viewport]
Pos=389,48
Size=1258,1012
Collapsed=0
DockId=0x00000002,0
[Docking][Data]
DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=8,79 Size=1920,1012 Split=X
DockNode ID=0x00000005 Parent=0xC5C9B8AB SizeRef=1992,1036 Split=X
DockNode ID=0x00000001 Parent=0x00000005 SizeRef=387,1036 Split=Y Selected=0x1E6EB881
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=225,94 Selected=0x1E6EB881
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=225,940 Selected=0xE096E5AE
DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1258,1036 CentralNode=1 Selected=0xB41284E7
DockNode ID=0x00000006 Parent=0xC5C9B8AB SizeRef=271,1036 Selected=0xE7039252

Binary file not shown.

View File

@ -0,0 +1,3 @@
Name: RaccoonBag_Color_Ver4
ID: 58303057
Type: 3

View File

@ -1,3 +1,3 @@
Name: RaccoonPreTexturedVer1_Base9 Name: RaccoonPreTexturedVer1_Base9
ID: 91918845 ID: 64651793
Type: 4 Type: 3

View File

@ -0,0 +1,3 @@
Name: Kirsch_CS
ID: 19931255
Type: 2

View File

@ -0,0 +1,67 @@
//#version 450
//
//layout(local_size_x = 16, local_size_y = 16) in;
//layout(set = 4, binding = 0, rgba8) uniform image2D targetImage;
//
//
//void main()
//{
// ivec2 imageSize = imageSize (targetImage);
//
// if (gl_GlobalInvocationID.x >= imageSize.x && gl_GlobalInvocationID.y >= imageSize.y)
// return;
//
// // load the image
// vec4 color = imageLoad (targetImage, ivec2 (gl_GlobalInvocationID));
//
// // get the average
// float average = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
//
// // store result into result image
// imageStore(targetImage, ivec2(gl_GlobalInvocationID), vec4(average, average, average, 1.0f));
//
//}
//
//
//
//
/* Start Header *****************************************************************/
/*! \file (e.g. kirsch.comp)
\author William Zheng, william.zheng, 60001906. Brandon Mak, brandon.hao 390003920.
\par william.zheng\@digipen.edu. brandon.hao\@digipen.edu.
\date Sept 20, 2022
\brief Copyright (C) 20xx 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. */
/* End Header *******************************************************************/
#version 450
layout(local_size_x = 16, local_size_y = 16) in;
layout(set = 4, binding = 0, rgba8) uniform image2D inputImage;
layout(set = 4, binding = 1, rgba8) uniform image2D targetImage;
void main()
{
// convenient variables
ivec2 globalThread = ivec2(gl_GlobalInvocationID);
vec3 color = imageLoad (inputImage, globalThread).rgb;
// store result into result image
imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(color, 1.0f));
}

Binary file not shown.

View File

@ -0,0 +1,3 @@
Name: PureCopy_CS
ID: 29659779
Type: 2

View File

@ -23,6 +23,7 @@ layout(location = 2) flat in struct
{ {
int materialIndex; int materialIndex;
uint eid; uint eid;
uint lightLayerIndex;
} In2; } In2;
//layout (set = 0, binding = ) //layout (set = 0, binding = )
@ -35,6 +36,7 @@ layout (set = 3, binding = 0) buffer MaterialProperties // For materials
layout(location = 0) out vec4 outColor; layout(location = 0) out vec4 outColor;
layout(location = 1) out uint outEntityID; layout(location = 1) out uint outEntityID;
layout(location = 2) out uint lightLayerIndices;
void main() void main()
{ {
@ -42,5 +44,6 @@ void main()
MatProp.data[In2.materialIndex].color / MatProp.data[In2.materialIndex].alpha; MatProp.data[In2.materialIndex].color / MatProp.data[In2.materialIndex].alpha;
outEntityID = In2.eid; outEntityID = In2.eid;
lightLayerIndices = In2.lightLayerIndex;
//outColor = vec4 (1.0f); //outColor = vec4 (1.0f);
} }

Binary file not shown.

View File

@ -0,0 +1,3 @@
Name: TestCube_FS
ID: 18415057
Type: 2

View File

@ -3,12 +3,13 @@
//#include "ShaderDescriptorDefinitions.glsl" //#include "ShaderDescriptorDefinitions.glsl"
layout(location = 0) in vec3 aVertexPos; layout(location = 0) in vec3 aVertexPos;
layout(location = 1) in vec2 aUV; layout(location = 1) in vec2 aUV;
layout(location = 2) in vec3 aNormal; layout(location = 2) in vec3 aNormal;
layout(location = 3) in vec3 aTangent; layout(location = 3) in vec3 aTangent;
layout(location = 4) in mat4 worldTransform; layout(location = 4) in mat4 worldTransform;
layout(location = 8) in uint eid; layout(location = 8) in uvec2 integerData;
layout(location = 0) out struct layout(location = 0) out struct
@ -23,6 +24,7 @@ layout(location = 2) out struct
{ {
int materialIndex; int materialIndex;
uint eid; uint eid;
uint lightLayerIndex;
} Out2; } Out2;
@ -36,7 +38,8 @@ void main()
{ {
Out.uv = aUV; Out.uv = aUV;
Out2.materialIndex = gl_InstanceIndex; Out2.materialIndex = gl_InstanceIndex;
Out2.eid = eid; Out2.eid = integerData[0];
Out2.lightLayerIndex = integerData[1];
gl_Position = cameraData.vpMat * worldTransform * vec4 (aVertexPos, 1.0f); gl_Position = cameraData.vpMat * worldTransform * vec4 (aVertexPos, 1.0f);
Out.vertColor = vec4 (aVertexPos, 1.0f); Out.vertColor = vec4 (aVertexPos, 1.0f);
} }

Binary file not shown.

View File

@ -0,0 +1,3 @@
Name: TestCube_VS
ID: 29315909
Type: 2

Binary file not shown.

View File

@ -0,0 +1,3 @@
Name: TD_Checker_Base_Color
ID: 51995224
Type: 3

View File

@ -56,8 +56,8 @@ namespace Sandbox
_In_ INT nCmdShow _In_ INT nCmdShow
) )
{ {
// Set working directory // Set working directory
SHFileUtilities::SetWorkDirToExecDir(); SHFileUtilities::SetWorkDirToExecDir();
window.Create(hInstance, hPrevInstance, lpCmdLine, nCmdShow); window.Create(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
@ -110,14 +110,7 @@ namespace Sandbox
SHComponentManager::CreateComponentSparseSet<SHRenderable>(); SHComponentManager::CreateComponentSparseSet<SHRenderable>();
SHComponentManager::CreateComponentSparseSet<SHCameraComponent>(); SHComponentManager::CreateComponentSparseSet<SHCameraComponent>();
//TODO: REMOVE AFTER PRESENTATION SHAssetManager::Load();
//SHAssetManager::LoadDataTemp("../../Assets/racoon.gltf");
//SHAssetManager::LoadDataTemp("../../Assets/Cube.012.shmesh");
//SHAssetManager::LoadDataTemp("../../Assets/RaccoonBag_Color_Ver4.dds");
//SHAssetManager::LoadDataTemp("../../Assets/RaccoonPreTexturedVer1_Base9.dds");
//SHAssetManager::LoadDataTemp("../../Assets/RaccoonPreTexturedVer1_Base9.shtex");
//TODO: REMOVE AFTER PRESENTATION
auto id = SHFamilyID<SHSystem>::GetID<SHGraphicsSystem>(); auto id = SHFamilyID<SHSystem>::GetID<SHGraphicsSystem>();
auto id2 = SHFamilyID<SHSystem>::GetID<SHGraphicsSystem>(); auto id2 = SHFamilyID<SHSystem>::GetID<SHGraphicsSystem>();
@ -133,8 +126,6 @@ namespace Sandbox
SHSceneManager::InitSceneManager<SBTestScene>("TestScene"); SHSceneManager::InitSceneManager<SBTestScene>("TestScene");
SHFrameRateController::UpdateFRC(); SHFrameRateController::UpdateFRC();
SHAssetManager::Load();
} }
void SBApplication::Update(void) void SBApplication::Update(void)
@ -152,16 +143,16 @@ namespace Sandbox
SHSceneManager::SceneUpdate(0.016f); SHSceneManager::SceneUpdate(0.016f);
#endif #endif
SHSystemManager::RunRoutines(editor->editorState != SHEditor::State::PLAY, 0.016f); SHSystemManager::RunRoutines(editor->editorState != SHEditor::State::PLAY, 0.016f);
//editor->PollPicking(); editor->PollPicking();
} }
// Finish all graphics jobs first // Finish all graphics jobs first
graphicsSystem->AwaitGraphicsExecution(); graphicsSystem->AwaitGraphicsExecution();
} }
void SBApplication::Exit(void) void SBApplication::Exit(void)
{ {
#ifdef SHEDITOR #ifdef SHEDITOR
SDL_DestroyWindow(sdlWindow); SDL_DestroyWindow(sdlWindow);
SDL_Quit(); SDL_Quit();
#endif #endif

View File

@ -12,6 +12,7 @@
#include "Graphics/MiddleEnd/Interface/SHMaterialInstance.h" #include "Graphics/MiddleEnd/Interface/SHMaterialInstance.h"
#include "Physics/Components/SHRigidBodyComponent.h" #include "Physics/Components/SHRigidBodyComponent.h"
#include "Physics/Components/SHColliderComponent.h" #include "Physics/Components/SHColliderComponent.h"
#include "Graphics/MiddleEnd/Lights/SHLightComponent.h"
#include "Assets/SHAssetManager.h" #include "Assets/SHAssetManager.h"
#include "Camera/SHCameraComponent.h" #include "Camera/SHCameraComponent.h"
@ -52,7 +53,8 @@ namespace Sandbox
if (asset.name == "Cube.012") if (asset.name == "Cube.012")
handles.emplace_back(SHResourceManager::LoadOrGet<SHMesh>(asset.id)); handles.emplace_back(SHResourceManager::LoadOrGet<SHMesh>(asset.id));
break; break;
case AssetType::IMAGE: case AssetType::TEXTURE:
if (asset.name == "RaccoonPreTexturedVer1_Base9")
texHandles.emplace_back(SHResourceManager::LoadOrGet<SHTexture>(asset.id)); texHandles.emplace_back(SHResourceManager::LoadOrGet<SHTexture>(asset.id));
break; break;
} }
@ -82,7 +84,7 @@ namespace Sandbox
auto& collider = *SHComponentManager::GetComponent_s<SHColliderComponent>(entity); auto& collider = *SHComponentManager::GetComponent_s<SHColliderComponent>(entity);
//renderable.Mesh = handles.front(); //renderable.Mesh = handles.front();
renderable.Mesh = CUBE_MESH; renderable.SetMesh(CUBE_MESH);
renderable.SetMaterial(customMat); renderable.SetMaterial(customMat);
if (y == 50) if (y == 50)
@ -94,11 +96,7 @@ namespace Sandbox
transform.SetWorldRotation(SHMath::GenerateRandomNumber(0.0f, 360.0f), SHMath::GenerateRandomNumber(0.0f, 360.0f), SHMath::GenerateRandomNumber(0.0f, 360.0f)); transform.SetWorldRotation(SHMath::GenerateRandomNumber(0.0f, 360.0f), SHMath::GenerateRandomNumber(0.0f, 360.0f), SHMath::GenerateRandomNumber(0.0f, 360.0f));
transform.SetWorldScale(TEST_OBJ_SCALE); transform.SetWorldScale(TEST_OBJ_SCALE);
//if (const bool IS_EVEN = (y * NUM_ROWS + x) % 2; IS_EVEN) collider.AddBoundingBox(SHVec3::One, SHVec3::Zero);
collider.AddBoundingBox(SHVec3::One * 0.5f, SHVec3::Zero);
//else
// collider.AddBoundingSphere(0.5f, SHVec3::Zero);
stressTestObjects.emplace_back(entity); stressTestObjects.emplace_back(entity);
} }
@ -106,7 +104,7 @@ namespace Sandbox
auto& renderable = *SHComponentManager::GetComponent_s<SHRenderable>(raccoonSpin); auto& renderable = *SHComponentManager::GetComponent_s<SHRenderable>(raccoonSpin);
auto& transform = *SHComponentManager::GetComponent_s<SHTransformComponent>(raccoonSpin); auto& transform = *SHComponentManager::GetComponent_s<SHTransformComponent>(raccoonSpin);
renderable.Mesh = handles.front(); renderable.SetMesh(handles.front());
renderable.SetMaterial(customMat); renderable.SetMaterial(customMat);
renderable.GetModifiableMaterial()->SetProperty("data.color", SHVec4(0.0f, 0.0f, 0.0f, 0.0f)); renderable.GetModifiableMaterial()->SetProperty("data.color", SHVec4(0.0f, 0.0f, 0.0f, 0.0f));
renderable.GetModifiableMaterial()->SetProperty("data.alpha", 1.0f); renderable.GetModifiableMaterial()->SetProperty("data.alpha", 1.0f);
@ -121,9 +119,8 @@ namespace Sandbox
auto& floorRigidBody = *SHComponentManager::GetComponent_s<SHRigidBodyComponent>(floor); auto& floorRigidBody = *SHComponentManager::GetComponent_s<SHRigidBodyComponent>(floor);
auto& floorCollider = *SHComponentManager::GetComponent_s<SHColliderComponent>(floor); auto& floorCollider = *SHComponentManager::GetComponent_s<SHColliderComponent>(floor);
floorRenderable.Mesh = CUBE_MESH; floorRenderable.SetMesh(CUBE_MESH);
floorRenderable.SetMaterial(customMat); floorRenderable.SetMaterial(graphicsSystem->GetDefaultMaterialInstance());
floorRenderable.GetModifiableMaterial()->SetProperty("data.color", SHVec4(1.0f, 1.0f, 1.0f, 1.0f));
floorTransform.SetWorldScale({ 7.5f, 0.5f, 7.5 }); floorTransform.SetWorldScale({ 7.5f, 0.5f, 7.5 });
floorTransform.SetWorldPosition({ 0.0f, -3.0f, -5.0f }); floorTransform.SetWorldPosition({ 0.0f, -3.0f, -5.0f });
@ -147,7 +144,7 @@ namespace Sandbox
auto& renderableShowcase = *SHComponentManager::GetComponent_s<SHRenderable>(raccoonShowcase); auto& renderableShowcase = *SHComponentManager::GetComponent_s<SHRenderable>(raccoonShowcase);
auto& transformShowcase = *SHComponentManager::GetComponent_s<SHTransformComponent>(raccoonShowcase); auto& transformShowcase = *SHComponentManager::GetComponent_s<SHTransformComponent>(raccoonShowcase);
renderableShowcase.Mesh = handles.front(); renderableShowcase.SetMesh(handles.front());
renderableShowcase.SetMaterial(customMat); renderableShowcase.SetMaterial(customMat);
renderableShowcase.GetModifiableMaterial()->SetProperty("data.color", SHVec4(0.0f, 0.0f, 0.0f, 0.0f)); renderableShowcase.GetModifiableMaterial()->SetProperty("data.color", SHVec4(0.0f, 0.0f, 0.0f, 0.0f));
renderableShowcase.GetModifiableMaterial()->SetProperty("data.alpha", 1.0f); renderableShowcase.GetModifiableMaterial()->SetProperty("data.alpha", 1.0f);
@ -158,6 +155,7 @@ namespace Sandbox
scriptEngine->AddScript(raccoonShowcase, "RaccoonShowcase"); scriptEngine->AddScript(raccoonShowcase, "RaccoonShowcase");
SHComponentManager::AddComponent<SHCameraComponent>(0); SHComponentManager::AddComponent<SHCameraComponent>(0);
SHComponentManager::AddComponent<SHLightComponent>(0);
SHComponentManager::RemoveComponent <SHRigidBodyComponent>(0); SHComponentManager::RemoveComponent <SHRigidBodyComponent>(0);
SHComponentManager::RemoveComponent <SHColliderComponent>(0); SHComponentManager::RemoveComponent <SHColliderComponent>(0);
} }

52
SHADE_CSharp/premake5.lua Normal file
View File

@ -0,0 +1,52 @@
project "SHADE_CSharp"
architecture "x64"
kind "SharedLib"
language "C#"
clr "NetCore"
dotnetframework "net5.0"
namespace ("SHADE")
targetdir (outputdir)
objdir (interdir)
systemversion "latest"
files
{
"%{prj.location}/src/**.cs",
"%{prj.location}/src/**.tt"
}
flags
{
"MultiProcessorCompile"
}
dependson
{
"SHADE_Engine"
}
warnings 'Extra'
filter "configurations:Debug"
symbols "On"
defines {"_DEBUG"}
filter "configurations:Release"
optimize "On"
defines{"_RELEASE"}
filter "configurations:Publish"
optimize "On"
defines{"_RELEASE"}
require "vstudio"
function platformsElement(cfg)
_p(2,'<Platforms>x64</Platforms>')
end
premake.override(premake.vstudio.cs2005.elements, "projectProperties", function (oldfn, cfg)
return table.join(oldfn(cfg), {
platformsElement,
})
end)

View File

@ -0,0 +1,852 @@
/************************************************************************************//*!
\file CallbackAction.cs
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Oct 23, 2022
\brief Contains the definition of CallbackAction and related classes.
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.
*//*************************************************************************************/
using System;
using System.Reflection;
namespace SHADE
{
/// <summary>
/// Interface for a CallbackAction that all variants inherit from.
/// </summary>
public interface ICallbackAction
{
/// <summary>
/// Whether or not this CallbackAction is runtime assigned. If it is, then the
/// TargetMethodName and TargetObject properties are invalid.
/// </summary>
bool IsRuntimeAction { get; }
/// <summary>
/// Name of the method that this CallbackAction is using.
/// </summary>
string TargetMethodName { get; }
/// <summary>
/// Object which the specified target method is called on.
/// </summary>
Object TargetObject { get; }
}
/// <summary>
/// Represents a function call that can be serialised and put togetheer with scripts.
/// This variant accepts functions with 1 parameter.
/// </summary>
public class CallbackAction<T1> : ICallbackAction
{
#region Properties ------------------------------------------------------------
/// <inheritdoc/>
public Object TargetObject { get; private set; }
/// <inheritdoc/>
public string TargetMethodName => targetMethod == null ? "" : targetMethod.Name;
/// <inheritdoc/>
public bool IsRuntimeAction => targetAction != null;
#endregion
#region Fields ------------------------------------------------------------------
private MethodInfo targetMethod;
private Action<T1> targetAction;
private Object[] parameters;
#endregion
#region Constructors ------------------------------------------------------------
/// <summary>
/// Constructs an empty Callback action.
/// </summary>
public CallbackAction() {}
/// <summary>
/// Constructs a CallbackAction that represents a call to the specified method on the
/// specified target.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
/// <exception cref="ArgumentException">
/// Thrown if a method that is not compatible with the target is specified. The method's
/// source type must match the target's type.
/// </exception>
public CallbackAction(Object target, MethodInfo method)
{
// Error Checks
if (method.DeclaringType != target.GetType())
throw new ArgumentException("[CallbackAction] Attempted register an action using an incompatible target object and method.");
// No errors, assign
TargetObject = target;
targetMethod = method;
// Create storage for parameters for calling
parameters = new Object[1];
}
/// <summary>
/// Constructs a Callback action based on an action.
/// </summary>
/// <param name="action"></param>
public CallbackAction(Action<T1> action)
{
targetAction = action;
}
#endregion
#region Usage Functions ---------------------------------------------------------
/// <summary>
/// Invokes the CallbackAction's stored method/action with the specified parameters.
/// </summary>
public void Invoke(T1 t1)
{
if (targetAction != null)
{
targetAction.Invoke(t1);
}
else if (TargetObject != null && targetMethod != null)
{
parameters[0] = t1;
_ = targetMethod.Invoke(TargetObject, parameters);
}
}
#endregion
}
/// <summary>
/// Represents a function call that can be serialised and put togetheer with scripts.
/// This variant accepts functions with 2 parameters.
/// </summary>
public class CallbackAction<T1, T2> : ICallbackAction
{
#region Properties ------------------------------------------------------------
/// <inheritdoc/>
public Object TargetObject { get; private set; }
/// <inheritdoc/>
public string TargetMethodName => targetMethod == null ? "" : targetMethod.Name;
/// <inheritdoc/>
public bool IsRuntimeAction => targetAction != null;
#endregion
#region Fields ------------------------------------------------------------------
private MethodInfo targetMethod;
private Action<T1, T2> targetAction;
private Object[] parameters;
#endregion
#region Constructors ------------------------------------------------------------
/// <summary>
/// Constructs an empty Callback action.
/// </summary>
public CallbackAction() {}
/// <summary>
/// Constructs a CallbackAction that represents a call to the specified method on the
/// specified target.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
/// <exception cref="ArgumentException">
/// Thrown if a method that is not compatible with the target is specified. The method's
/// source type must match the target's type.
/// </exception>
public CallbackAction(Object target, MethodInfo method)
{
// Error Checks
if (method.DeclaringType != target.GetType())
throw new ArgumentException("[CallbackAction] Attempted register an action using an incompatible target object and method.");
// No errors, assign
TargetObject = target;
targetMethod = method;
// Create storage for parameters for calling
parameters = new Object[1];
}
/// <summary>
/// Constructs a Callback action based on an action.
/// </summary>
/// <param name="action"></param>
public CallbackAction(Action<T1, T2> action)
{
targetAction = action;
}
#endregion
#region Usage Functions ---------------------------------------------------------
/// <summary>
/// Invokes the CallbackAction's stored method/action with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2)
{
if (targetAction != null)
{
targetAction.Invoke(t1, t2);
}
else if (TargetObject != null && targetMethod != null)
{
parameters[0] = t1;
parameters[1] = t2;
_ = targetMethod.Invoke(TargetObject, parameters);
}
}
#endregion
}
/// <summary>
/// Represents a function call that can be serialised and put togetheer with scripts.
/// This variant accepts functions with 3 parameters.
/// </summary>
public class CallbackAction<T1, T2, T3> : ICallbackAction
{
#region Properties ------------------------------------------------------------
/// <inheritdoc/>
public Object TargetObject { get; private set; }
/// <inheritdoc/>
public string TargetMethodName => targetMethod == null ? "" : targetMethod.Name;
/// <inheritdoc/>
public bool IsRuntimeAction => targetAction != null;
#endregion
#region Fields ------------------------------------------------------------------
private MethodInfo targetMethod;
private Action<T1, T2, T3> targetAction;
private Object[] parameters;
#endregion
#region Constructors ------------------------------------------------------------
/// <summary>
/// Constructs an empty Callback action.
/// </summary>
public CallbackAction() {}
/// <summary>
/// Constructs a CallbackAction that represents a call to the specified method on the
/// specified target.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
/// <exception cref="ArgumentException">
/// Thrown if a method that is not compatible with the target is specified. The method's
/// source type must match the target's type.
/// </exception>
public CallbackAction(Object target, MethodInfo method)
{
// Error Checks
if (method.DeclaringType != target.GetType())
throw new ArgumentException("[CallbackAction] Attempted register an action using an incompatible target object and method.");
// No errors, assign
TargetObject = target;
targetMethod = method;
// Create storage for parameters for calling
parameters = new Object[1];
}
/// <summary>
/// Constructs a Callback action based on an action.
/// </summary>
/// <param name="action"></param>
public CallbackAction(Action<T1, T2, T3> action)
{
targetAction = action;
}
#endregion
#region Usage Functions ---------------------------------------------------------
/// <summary>
/// Invokes the CallbackAction's stored method/action with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3)
{
if (targetAction != null)
{
targetAction.Invoke(t1, t2, t3);
}
else if (TargetObject != null && targetMethod != null)
{
parameters[0] = t1;
parameters[1] = t2;
parameters[2] = t3;
_ = targetMethod.Invoke(TargetObject, parameters);
}
}
#endregion
}
/// <summary>
/// Represents a function call that can be serialised and put togetheer with scripts.
/// This variant accepts functions with 4 parameters.
/// </summary>
public class CallbackAction<T1, T2, T3, T4> : ICallbackAction
{
#region Properties ------------------------------------------------------------
/// <inheritdoc/>
public Object TargetObject { get; private set; }
/// <inheritdoc/>
public string TargetMethodName => targetMethod == null ? "" : targetMethod.Name;
/// <inheritdoc/>
public bool IsRuntimeAction => targetAction != null;
#endregion
#region Fields ------------------------------------------------------------------
private MethodInfo targetMethod;
private Action<T1, T2, T3, T4> targetAction;
private Object[] parameters;
#endregion
#region Constructors ------------------------------------------------------------
/// <summary>
/// Constructs an empty Callback action.
/// </summary>
public CallbackAction() {}
/// <summary>
/// Constructs a CallbackAction that represents a call to the specified method on the
/// specified target.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
/// <exception cref="ArgumentException">
/// Thrown if a method that is not compatible with the target is specified. The method's
/// source type must match the target's type.
/// </exception>
public CallbackAction(Object target, MethodInfo method)
{
// Error Checks
if (method.DeclaringType != target.GetType())
throw new ArgumentException("[CallbackAction] Attempted register an action using an incompatible target object and method.");
// No errors, assign
TargetObject = target;
targetMethod = method;
// Create storage for parameters for calling
parameters = new Object[1];
}
/// <summary>
/// Constructs a Callback action based on an action.
/// </summary>
/// <param name="action"></param>
public CallbackAction(Action<T1, T2, T3, T4> action)
{
targetAction = action;
}
#endregion
#region Usage Functions ---------------------------------------------------------
/// <summary>
/// Invokes the CallbackAction's stored method/action with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3, T4 t4)
{
if (targetAction != null)
{
targetAction.Invoke(t1, t2, t3, t4);
}
else if (TargetObject != null && targetMethod != null)
{
parameters[0] = t1;
parameters[1] = t2;
parameters[2] = t3;
parameters[3] = t4;
_ = targetMethod.Invoke(TargetObject, parameters);
}
}
#endregion
}
/// <summary>
/// Represents a function call that can be serialised and put togetheer with scripts.
/// This variant accepts functions with 5 parameters.
/// </summary>
public class CallbackAction<T1, T2, T3, T4, T5> : ICallbackAction
{
#region Properties ------------------------------------------------------------
/// <inheritdoc/>
public Object TargetObject { get; private set; }
/// <inheritdoc/>
public string TargetMethodName => targetMethod == null ? "" : targetMethod.Name;
/// <inheritdoc/>
public bool IsRuntimeAction => targetAction != null;
#endregion
#region Fields ------------------------------------------------------------------
private MethodInfo targetMethod;
private Action<T1, T2, T3, T4, T5> targetAction;
private Object[] parameters;
#endregion
#region Constructors ------------------------------------------------------------
/// <summary>
/// Constructs an empty Callback action.
/// </summary>
public CallbackAction() {}
/// <summary>
/// Constructs a CallbackAction that represents a call to the specified method on the
/// specified target.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
/// <exception cref="ArgumentException">
/// Thrown if a method that is not compatible with the target is specified. The method's
/// source type must match the target's type.
/// </exception>
public CallbackAction(Object target, MethodInfo method)
{
// Error Checks
if (method.DeclaringType != target.GetType())
throw new ArgumentException("[CallbackAction] Attempted register an action using an incompatible target object and method.");
// No errors, assign
TargetObject = target;
targetMethod = method;
// Create storage for parameters for calling
parameters = new Object[1];
}
/// <summary>
/// Constructs a Callback action based on an action.
/// </summary>
/// <param name="action"></param>
public CallbackAction(Action<T1, T2, T3, T4, T5> action)
{
targetAction = action;
}
#endregion
#region Usage Functions ---------------------------------------------------------
/// <summary>
/// Invokes the CallbackAction's stored method/action with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{
if (targetAction != null)
{
targetAction.Invoke(t1, t2, t3, t4, t5);
}
else if (TargetObject != null && targetMethod != null)
{
parameters[0] = t1;
parameters[1] = t2;
parameters[2] = t3;
parameters[3] = t4;
parameters[4] = t5;
_ = targetMethod.Invoke(TargetObject, parameters);
}
}
#endregion
}
/// <summary>
/// Represents a function call that can be serialised and put togetheer with scripts.
/// This variant accepts functions with 6 parameters.
/// </summary>
public class CallbackAction<T1, T2, T3, T4, T5, T6> : ICallbackAction
{
#region Properties ------------------------------------------------------------
/// <inheritdoc/>
public Object TargetObject { get; private set; }
/// <inheritdoc/>
public string TargetMethodName => targetMethod == null ? "" : targetMethod.Name;
/// <inheritdoc/>
public bool IsRuntimeAction => targetAction != null;
#endregion
#region Fields ------------------------------------------------------------------
private MethodInfo targetMethod;
private Action<T1, T2, T3, T4, T5, T6> targetAction;
private Object[] parameters;
#endregion
#region Constructors ------------------------------------------------------------
/// <summary>
/// Constructs an empty Callback action.
/// </summary>
public CallbackAction() {}
/// <summary>
/// Constructs a CallbackAction that represents a call to the specified method on the
/// specified target.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
/// <exception cref="ArgumentException">
/// Thrown if a method that is not compatible with the target is specified. The method's
/// source type must match the target's type.
/// </exception>
public CallbackAction(Object target, MethodInfo method)
{
// Error Checks
if (method.DeclaringType != target.GetType())
throw new ArgumentException("[CallbackAction] Attempted register an action using an incompatible target object and method.");
// No errors, assign
TargetObject = target;
targetMethod = method;
// Create storage for parameters for calling
parameters = new Object[1];
}
/// <summary>
/// Constructs a Callback action based on an action.
/// </summary>
/// <param name="action"></param>
public CallbackAction(Action<T1, T2, T3, T4, T5, T6> action)
{
targetAction = action;
}
#endregion
#region Usage Functions ---------------------------------------------------------
/// <summary>
/// Invokes the CallbackAction's stored method/action with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{
if (targetAction != null)
{
targetAction.Invoke(t1, t2, t3, t4, t5, t6);
}
else if (TargetObject != null && targetMethod != null)
{
parameters[0] = t1;
parameters[1] = t2;
parameters[2] = t3;
parameters[3] = t4;
parameters[4] = t5;
parameters[5] = t6;
_ = targetMethod.Invoke(TargetObject, parameters);
}
}
#endregion
}
/// <summary>
/// Represents a function call that can be serialised and put togetheer with scripts.
/// This variant accepts functions with 7 parameters.
/// </summary>
public class CallbackAction<T1, T2, T3, T4, T5, T6, T7> : ICallbackAction
{
#region Properties ------------------------------------------------------------
/// <inheritdoc/>
public Object TargetObject { get; private set; }
/// <inheritdoc/>
public string TargetMethodName => targetMethod == null ? "" : targetMethod.Name;
/// <inheritdoc/>
public bool IsRuntimeAction => targetAction != null;
#endregion
#region Fields ------------------------------------------------------------------
private MethodInfo targetMethod;
private Action<T1, T2, T3, T4, T5, T6, T7> targetAction;
private Object[] parameters;
#endregion
#region Constructors ------------------------------------------------------------
/// <summary>
/// Constructs an empty Callback action.
/// </summary>
public CallbackAction() {}
/// <summary>
/// Constructs a CallbackAction that represents a call to the specified method on the
/// specified target.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
/// <exception cref="ArgumentException">
/// Thrown if a method that is not compatible with the target is specified. The method's
/// source type must match the target's type.
/// </exception>
public CallbackAction(Object target, MethodInfo method)
{
// Error Checks
if (method.DeclaringType != target.GetType())
throw new ArgumentException("[CallbackAction] Attempted register an action using an incompatible target object and method.");
// No errors, assign
TargetObject = target;
targetMethod = method;
// Create storage for parameters for calling
parameters = new Object[1];
}
/// <summary>
/// Constructs a Callback action based on an action.
/// </summary>
/// <param name="action"></param>
public CallbackAction(Action<T1, T2, T3, T4, T5, T6, T7> action)
{
targetAction = action;
}
#endregion
#region Usage Functions ---------------------------------------------------------
/// <summary>
/// Invokes the CallbackAction's stored method/action with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
{
if (targetAction != null)
{
targetAction.Invoke(t1, t2, t3, t4, t5, t6, t7);
}
else if (TargetObject != null && targetMethod != null)
{
parameters[0] = t1;
parameters[1] = t2;
parameters[2] = t3;
parameters[3] = t4;
parameters[4] = t5;
parameters[5] = t6;
parameters[6] = t7;
_ = targetMethod.Invoke(TargetObject, parameters);
}
}
#endregion
}
/// <summary>
/// Represents a function call that can be serialised and put togetheer with scripts.
/// This variant accepts functions with 8 parameters.
/// </summary>
public class CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8> : ICallbackAction
{
#region Properties ------------------------------------------------------------
/// <inheritdoc/>
public Object TargetObject { get; private set; }
/// <inheritdoc/>
public string TargetMethodName => targetMethod == null ? "" : targetMethod.Name;
/// <inheritdoc/>
public bool IsRuntimeAction => targetAction != null;
#endregion
#region Fields ------------------------------------------------------------------
private MethodInfo targetMethod;
private Action<T1, T2, T3, T4, T5, T6, T7, T8> targetAction;
private Object[] parameters;
#endregion
#region Constructors ------------------------------------------------------------
/// <summary>
/// Constructs an empty Callback action.
/// </summary>
public CallbackAction() {}
/// <summary>
/// Constructs a CallbackAction that represents a call to the specified method on the
/// specified target.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
/// <exception cref="ArgumentException">
/// Thrown if a method that is not compatible with the target is specified. The method's
/// source type must match the target's type.
/// </exception>
public CallbackAction(Object target, MethodInfo method)
{
// Error Checks
if (method.DeclaringType != target.GetType())
throw new ArgumentException("[CallbackAction] Attempted register an action using an incompatible target object and method.");
// No errors, assign
TargetObject = target;
targetMethod = method;
// Create storage for parameters for calling
parameters = new Object[1];
}
/// <summary>
/// Constructs a Callback action based on an action.
/// </summary>
/// <param name="action"></param>
public CallbackAction(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
{
targetAction = action;
}
#endregion
#region Usage Functions ---------------------------------------------------------
/// <summary>
/// Invokes the CallbackAction's stored method/action with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
{
if (targetAction != null)
{
targetAction.Invoke(t1, t2, t3, t4, t5, t6, t7, t8);
}
else if (TargetObject != null && targetMethod != null)
{
parameters[0] = t1;
parameters[1] = t2;
parameters[2] = t3;
parameters[3] = t4;
parameters[4] = t5;
parameters[5] = t6;
parameters[6] = t7;
parameters[7] = t8;
_ = targetMethod.Invoke(TargetObject, parameters);
}
}
#endregion
}
/// <summary>
/// Represents a function call that can be serialised and put togetheer with scripts.
/// This variant accepts functions with 9 parameters.
/// </summary>
public class CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8, T9> : ICallbackAction
{
#region Properties ------------------------------------------------------------
/// <inheritdoc/>
public Object TargetObject { get; private set; }
/// <inheritdoc/>
public string TargetMethodName => targetMethod == null ? "" : targetMethod.Name;
/// <inheritdoc/>
public bool IsRuntimeAction => targetAction != null;
#endregion
#region Fields ------------------------------------------------------------------
private MethodInfo targetMethod;
private Action<T1, T2, T3, T4, T5, T6, T7, T8, T9> targetAction;
private Object[] parameters;
#endregion
#region Constructors ------------------------------------------------------------
/// <summary>
/// Constructs an empty Callback action.
/// </summary>
public CallbackAction() {}
/// <summary>
/// Constructs a CallbackAction that represents a call to the specified method on the
/// specified target.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
/// <exception cref="ArgumentException">
/// Thrown if a method that is not compatible with the target is specified. The method's
/// source type must match the target's type.
/// </exception>
public CallbackAction(Object target, MethodInfo method)
{
// Error Checks
if (method.DeclaringType != target.GetType())
throw new ArgumentException("[CallbackAction] Attempted register an action using an incompatible target object and method.");
// No errors, assign
TargetObject = target;
targetMethod = method;
// Create storage for parameters for calling
parameters = new Object[1];
}
/// <summary>
/// Constructs a Callback action based on an action.
/// </summary>
/// <param name="action"></param>
public CallbackAction(Action<T1, T2, T3, T4, T5, T6, T7, T8, T9> action)
{
targetAction = action;
}
#endregion
#region Usage Functions ---------------------------------------------------------
/// <summary>
/// Invokes the CallbackAction's stored method/action with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9)
{
if (targetAction != null)
{
targetAction.Invoke(t1, t2, t3, t4, t5, t6, t7, t8, t9);
}
else if (TargetObject != null && targetMethod != null)
{
parameters[0] = t1;
parameters[1] = t2;
parameters[2] = t3;
parameters[3] = t4;
parameters[4] = t5;
parameters[5] = t6;
parameters[6] = t7;
parameters[7] = t8;
parameters[8] = t9;
_ = targetMethod.Invoke(TargetObject, parameters);
}
}
#endregion
}
/// <summary>
/// Represents a function call that can be serialised and put togetheer with scripts.
/// This variant accepts functions with 10 parameters.
/// </summary>
public class CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : ICallbackAction
{
#region Properties ------------------------------------------------------------
/// <inheritdoc/>
public Object TargetObject { get; private set; }
/// <inheritdoc/>
public string TargetMethodName => targetMethod == null ? "" : targetMethod.Name;
/// <inheritdoc/>
public bool IsRuntimeAction => targetAction != null;
#endregion
#region Fields ------------------------------------------------------------------
private MethodInfo targetMethod;
private Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> targetAction;
private Object[] parameters;
#endregion
#region Constructors ------------------------------------------------------------
/// <summary>
/// Constructs an empty Callback action.
/// </summary>
public CallbackAction() {}
/// <summary>
/// Constructs a CallbackAction that represents a call to the specified method on the
/// specified target.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
/// <exception cref="ArgumentException">
/// Thrown if a method that is not compatible with the target is specified. The method's
/// source type must match the target's type.
/// </exception>
public CallbackAction(Object target, MethodInfo method)
{
// Error Checks
if (method.DeclaringType != target.GetType())
throw new ArgumentException("[CallbackAction] Attempted register an action using an incompatible target object and method.");
// No errors, assign
TargetObject = target;
targetMethod = method;
// Create storage for parameters for calling
parameters = new Object[1];
}
/// <summary>
/// Constructs a Callback action based on an action.
/// </summary>
/// <param name="action"></param>
public CallbackAction(Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> action)
{
targetAction = action;
}
#endregion
#region Usage Functions ---------------------------------------------------------
/// <summary>
/// Invokes the CallbackAction's stored method/action with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10)
{
if (targetAction != null)
{
targetAction.Invoke(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10);
}
else if (TargetObject != null && targetMethod != null)
{
parameters[0] = t1;
parameters[1] = t2;
parameters[2] = t3;
parameters[3] = t4;
parameters[4] = t5;
parameters[5] = t6;
parameters[6] = t7;
parameters[7] = t8;
parameters[8] = t9;
parameters[9] = t10;
_ = targetMethod.Invoke(TargetObject, parameters);
}
}
#endregion
}
}

View File

@ -0,0 +1,132 @@
<#
/************************************************************************************//*!
\file CallbackAction.tt
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Oct 23, 2022
\brief Contains the T4 template for the definition of CallbackAction and
related classes.
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.
*//*************************************************************************************/#>
<#@ template hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<# var max = 10; #>
/************************************************************************************//*!
\file CallbackAction.cs
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Oct 23, 2022
\brief Contains the definition of CallbackAction and related classes.
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.
*//*************************************************************************************/
using System;
using System.Reflection;
namespace SHADE
{
/// <summary>
/// Interface for a CallbackAction that all variants inherit from.
/// </summary>
public interface ICallbackAction
{
/// <summary>
/// Whether or not this CallbackAction is runtime assigned. If it is, then the
/// TargetMethodName and TargetObject properties are invalid.
/// </summary>
bool IsRuntimeAction { get; }
/// <summary>
/// Name of the method that this CallbackAction is using.
/// </summary>
string TargetMethodName { get; }
/// <summary>
/// Object which the specified target method is called on.
/// </summary>
Object TargetObject { get; }
}
<# for (int i = 1; i <= max; ++i) { #>
/// <summary>
/// Represents a function call that can be serialised and put togetheer with scripts.
/// This variant accepts functions with <#=i#> parameter<# if (i > 1) {#>s<#} #>.
/// </summary>
public class CallbackAction<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>> : ICallbackAction
{
#region Properties ------------------------------------------------------------
/// <inheritdoc/>
public Object TargetObject { get; private set; }
/// <inheritdoc/>
public string TargetMethodName => targetMethod == null ? "" : targetMethod.Name;
/// <inheritdoc/>
public bool IsRuntimeAction => targetAction != null;
#endregion
#region Fields ------------------------------------------------------------------
private MethodInfo targetMethod;
private Action<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>> targetAction;
private Object[] parameters;
#endregion
#region Constructors ------------------------------------------------------------
/// <summary>
/// Constructs an empty Callback action.
/// </summary>
public CallbackAction() {}
/// <summary>
/// Constructs a CallbackAction that represents a call to the specified method on the
/// specified target.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
/// <exception cref="ArgumentException">
/// Thrown if a method that is not compatible with the target is specified. The method's
/// source type must match the target's type.
/// </exception>
public CallbackAction(Object target, MethodInfo method)
{
// Error Checks
if (method.DeclaringType != target.GetType())
throw new ArgumentException("[CallbackAction] Attempted register an action using an incompatible target object and method.");
// No errors, assign
TargetObject = target;
targetMethod = method;
// Create storage for parameters for calling
parameters = new Object[1];
}
/// <summary>
/// Constructs a Callback action based on an action.
/// </summary>
/// <param name="action"></param>
public CallbackAction(Action<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>> action)
{
targetAction = action;
}
#endregion
#region Usage Functions ---------------------------------------------------------
/// <summary>
/// Invokes the CallbackAction's stored method/action with the specified parameters.
/// </summary>
public void Invoke(<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#> t<#=t#><# if (t != i) { #>, <# } #><# } #>)
{
if (targetAction != null)
{
targetAction.Invoke(<# for (int t = 1; t < i + 1; ++t) { #>t<#=t#><# if (t != i) { #>, <# } #><# } #>);
}
else if (TargetObject != null && targetMethod != null)
{
<# for (int t = 0; t < i; ++t) {#>parameters[<#=t#>] = t<#=t+1#>;
<# } #>_ = targetMethod.Invoke(TargetObject, parameters);
}
}
#endregion
}
<# } #>
}

View File

@ -0,0 +1,908 @@
/************************************************************************************//*!
\file CallbackEvent.cs
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Oct 23, 2022
\brief Contains the definition of CallbackEvent and related classes.
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.
*//*************************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Metadata.Ecma335;
namespace SHADE
{
/// <summary>
/// Interface for a CallbackEvent that all variants inherit from.
/// </summary>
public interface ICallbackEvent
{
/// <summary>
/// Registers an empty ICallbackAction.
/// </summary>
void RegisterAction();
/// <summary>
/// Registers an ICallbackAction with the event such that it will be called in
/// future
/// </summary>
/// <param name="action">ICallbackAction to register with.</param>
void RegisterAction(ICallbackAction action);
/// <summary>
/// Deregisters an ICallbackAction that was previously added. This should
/// only emit a warning if an action that was not previous added was
/// provided.
/// </summary>
/// <param name="action">ICallbackAction to remove.</param>
void DeregisterAction(ICallbackAction action);
/// <summary>
/// Iterable set of ICallbackActions that were registered to this event.
/// </summary>
IEnumerable<ICallbackAction> Actions { get; }
}
/// <summary>
/// A container of CallbackActions that is correlated to a specific scenario as
/// specified by the user of this class.
/// This variant accepts CallbackEvents with 1 generic parameter.
/// </summary>
public class CallbackEvent<T1> : ICallbackEvent
{
#region Properties --------------------------------------------------------------
/// <inheritdoc/>
public IEnumerable<ICallbackAction> Actions => actions;
#endregion
#region Fields ------------------------------------------------------------------
private List<ICallbackAction> actions = new List<ICallbackAction>();
#endregion
#region Usage Functions ---------------------------------------------------------
/// <inheritdoc/>
public void RegisterAction()
{
actions.Add(new CallbackAction<T1>());
}
/// <inheritdoc/>
public void RegisterAction(ICallbackAction action)
{
// Check if valid action
if (action.GetType() != typeof(CallbackAction<T1>))
{
Debug.LogWarning("Attempted to register an invalid CallbackAction type. Ignoring.", this);
return;
}
actions.Add(action);
}
/// <summary>
/// Adds a CallbackAction into the event.
/// </summary>
/// <param name="action">CallbackAction to add.</param>
public void RegisterAction(CallbackAction<T1> action)
{
actions.Add(action);
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="action">System.Action to add as a CallbackAction.</param>
public void RegisterAction(Action<T1> action)
{
actions.Add(new CallbackAction<T1>(action));
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
public void RegisterAction(Object target, MethodInfo method)
{
actions.Add(new CallbackAction<T1>(target, method));
}
/// <inheritdoc/>
public void DeregisterAction(ICallbackAction action)
{
if (!actions.Remove(action))
{
Debug.LogWarning("Attempted to deregister invalid action. Ignored.", this);
}
}
/// <summary>
/// Invokes all stored CallbackActions with the specified parameters.
/// </summary>
public void Invoke(T1 t1)
{
foreach (CallbackAction<T1> action in actions)
{
try
{
action.Invoke(t1);
}
catch (Exception e)
{
Debug.LogException(e, this);
}
}
}
#endregion
}
/// <summary>
/// A container of CallbackActions that is correlated to a specific scenario as
/// specified by the user of this class.
/// This variant accepts CallbackEvents with 1 generic parameter.
/// </summary>
public class CallbackEvent<T1, T2> : ICallbackEvent
{
#region Properties --------------------------------------------------------------
/// <inheritdoc/>
public IEnumerable<ICallbackAction> Actions => actions;
#endregion
#region Fields ------------------------------------------------------------------
private List<ICallbackAction> actions = new List<ICallbackAction>();
#endregion
#region Usage Functions ---------------------------------------------------------
/// <inheritdoc/>
public void RegisterAction()
{
actions.Add(new CallbackAction<T1, T2>());
}
/// <inheritdoc/>
public void RegisterAction(ICallbackAction action)
{
// Check if valid action
if (action.GetType() != typeof(CallbackAction<T1, T2>))
{
Debug.LogWarning("Attempted to register an invalid CallbackAction type. Ignoring.", this);
return;
}
actions.Add(action);
}
/// <summary>
/// Adds a CallbackAction into the event.
/// </summary>
/// <param name="action">CallbackAction to add.</param>
public void RegisterAction(CallbackAction<T1, T2> action)
{
actions.Add(action);
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="action">System.Action to add as a CallbackAction.</param>
public void RegisterAction(Action<T1, T2> action)
{
actions.Add(new CallbackAction<T1, T2>(action));
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
public void RegisterAction(Object target, MethodInfo method)
{
actions.Add(new CallbackAction<T1, T2>(target, method));
}
/// <inheritdoc/>
public void DeregisterAction(ICallbackAction action)
{
if (!actions.Remove(action))
{
Debug.LogWarning("Attempted to deregister invalid action. Ignored.", this);
}
}
/// <summary>
/// Invokes all stored CallbackActions with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2)
{
foreach (CallbackAction<T1, T2> action in actions)
{
try
{
action.Invoke(t1, t2);
}
catch (Exception e)
{
Debug.LogException(e, this);
}
}
}
#endregion
}
/// <summary>
/// A container of CallbackActions that is correlated to a specific scenario as
/// specified by the user of this class.
/// This variant accepts CallbackEvents with 1 generic parameter.
/// </summary>
public class CallbackEvent<T1, T2, T3> : ICallbackEvent
{
#region Properties --------------------------------------------------------------
/// <inheritdoc/>
public IEnumerable<ICallbackAction> Actions => actions;
#endregion
#region Fields ------------------------------------------------------------------
private List<ICallbackAction> actions = new List<ICallbackAction>();
#endregion
#region Usage Functions ---------------------------------------------------------
/// <inheritdoc/>
public void RegisterAction()
{
actions.Add(new CallbackAction<T1, T2, T3>());
}
/// <inheritdoc/>
public void RegisterAction(ICallbackAction action)
{
// Check if valid action
if (action.GetType() != typeof(CallbackAction<T1, T2, T3>))
{
Debug.LogWarning("Attempted to register an invalid CallbackAction type. Ignoring.", this);
return;
}
actions.Add(action);
}
/// <summary>
/// Adds a CallbackAction into the event.
/// </summary>
/// <param name="action">CallbackAction to add.</param>
public void RegisterAction(CallbackAction<T1, T2, T3> action)
{
actions.Add(action);
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="action">System.Action to add as a CallbackAction.</param>
public void RegisterAction(Action<T1, T2, T3> action)
{
actions.Add(new CallbackAction<T1, T2, T3>(action));
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
public void RegisterAction(Object target, MethodInfo method)
{
actions.Add(new CallbackAction<T1, T2, T3>(target, method));
}
/// <inheritdoc/>
public void DeregisterAction(ICallbackAction action)
{
if (!actions.Remove(action))
{
Debug.LogWarning("Attempted to deregister invalid action. Ignored.", this);
}
}
/// <summary>
/// Invokes all stored CallbackActions with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3)
{
foreach (CallbackAction<T1, T2, T3> action in actions)
{
try
{
action.Invoke(t1, t2, t3);
}
catch (Exception e)
{
Debug.LogException(e, this);
}
}
}
#endregion
}
/// <summary>
/// A container of CallbackActions that is correlated to a specific scenario as
/// specified by the user of this class.
/// This variant accepts CallbackEvents with 1 generic parameter.
/// </summary>
public class CallbackEvent<T1, T2, T3, T4> : ICallbackEvent
{
#region Properties --------------------------------------------------------------
/// <inheritdoc/>
public IEnumerable<ICallbackAction> Actions => actions;
#endregion
#region Fields ------------------------------------------------------------------
private List<ICallbackAction> actions = new List<ICallbackAction>();
#endregion
#region Usage Functions ---------------------------------------------------------
/// <inheritdoc/>
public void RegisterAction()
{
actions.Add(new CallbackAction<T1, T2, T3, T4>());
}
/// <inheritdoc/>
public void RegisterAction(ICallbackAction action)
{
// Check if valid action
if (action.GetType() != typeof(CallbackAction<T1, T2, T3, T4>))
{
Debug.LogWarning("Attempted to register an invalid CallbackAction type. Ignoring.", this);
return;
}
actions.Add(action);
}
/// <summary>
/// Adds a CallbackAction into the event.
/// </summary>
/// <param name="action">CallbackAction to add.</param>
public void RegisterAction(CallbackAction<T1, T2, T3, T4> action)
{
actions.Add(action);
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="action">System.Action to add as a CallbackAction.</param>
public void RegisterAction(Action<T1, T2, T3, T4> action)
{
actions.Add(new CallbackAction<T1, T2, T3, T4>(action));
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
public void RegisterAction(Object target, MethodInfo method)
{
actions.Add(new CallbackAction<T1, T2, T3, T4>(target, method));
}
/// <inheritdoc/>
public void DeregisterAction(ICallbackAction action)
{
if (!actions.Remove(action))
{
Debug.LogWarning("Attempted to deregister invalid action. Ignored.", this);
}
}
/// <summary>
/// Invokes all stored CallbackActions with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3, T4 t4)
{
foreach (CallbackAction<T1, T2, T3, T4> action in actions)
{
try
{
action.Invoke(t1, t2, t3, t4);
}
catch (Exception e)
{
Debug.LogException(e, this);
}
}
}
#endregion
}
/// <summary>
/// A container of CallbackActions that is correlated to a specific scenario as
/// specified by the user of this class.
/// This variant accepts CallbackEvents with 1 generic parameter.
/// </summary>
public class CallbackEvent<T1, T2, T3, T4, T5> : ICallbackEvent
{
#region Properties --------------------------------------------------------------
/// <inheritdoc/>
public IEnumerable<ICallbackAction> Actions => actions;
#endregion
#region Fields ------------------------------------------------------------------
private List<ICallbackAction> actions = new List<ICallbackAction>();
#endregion
#region Usage Functions ---------------------------------------------------------
/// <inheritdoc/>
public void RegisterAction()
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5>());
}
/// <inheritdoc/>
public void RegisterAction(ICallbackAction action)
{
// Check if valid action
if (action.GetType() != typeof(CallbackAction<T1, T2, T3, T4, T5>))
{
Debug.LogWarning("Attempted to register an invalid CallbackAction type. Ignoring.", this);
return;
}
actions.Add(action);
}
/// <summary>
/// Adds a CallbackAction into the event.
/// </summary>
/// <param name="action">CallbackAction to add.</param>
public void RegisterAction(CallbackAction<T1, T2, T3, T4, T5> action)
{
actions.Add(action);
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="action">System.Action to add as a CallbackAction.</param>
public void RegisterAction(Action<T1, T2, T3, T4, T5> action)
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5>(action));
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
public void RegisterAction(Object target, MethodInfo method)
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5>(target, method));
}
/// <inheritdoc/>
public void DeregisterAction(ICallbackAction action)
{
if (!actions.Remove(action))
{
Debug.LogWarning("Attempted to deregister invalid action. Ignored.", this);
}
}
/// <summary>
/// Invokes all stored CallbackActions with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{
foreach (CallbackAction<T1, T2, T3, T4, T5> action in actions)
{
try
{
action.Invoke(t1, t2, t3, t4, t5);
}
catch (Exception e)
{
Debug.LogException(e, this);
}
}
}
#endregion
}
/// <summary>
/// A container of CallbackActions that is correlated to a specific scenario as
/// specified by the user of this class.
/// This variant accepts CallbackEvents with 1 generic parameter.
/// </summary>
public class CallbackEvent<T1, T2, T3, T4, T5, T6> : ICallbackEvent
{
#region Properties --------------------------------------------------------------
/// <inheritdoc/>
public IEnumerable<ICallbackAction> Actions => actions;
#endregion
#region Fields ------------------------------------------------------------------
private List<ICallbackAction> actions = new List<ICallbackAction>();
#endregion
#region Usage Functions ---------------------------------------------------------
/// <inheritdoc/>
public void RegisterAction()
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6>());
}
/// <inheritdoc/>
public void RegisterAction(ICallbackAction action)
{
// Check if valid action
if (action.GetType() != typeof(CallbackAction<T1, T2, T3, T4, T5, T6>))
{
Debug.LogWarning("Attempted to register an invalid CallbackAction type. Ignoring.", this);
return;
}
actions.Add(action);
}
/// <summary>
/// Adds a CallbackAction into the event.
/// </summary>
/// <param name="action">CallbackAction to add.</param>
public void RegisterAction(CallbackAction<T1, T2, T3, T4, T5, T6> action)
{
actions.Add(action);
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="action">System.Action to add as a CallbackAction.</param>
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6> action)
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6>(action));
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
public void RegisterAction(Object target, MethodInfo method)
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6>(target, method));
}
/// <inheritdoc/>
public void DeregisterAction(ICallbackAction action)
{
if (!actions.Remove(action))
{
Debug.LogWarning("Attempted to deregister invalid action. Ignored.", this);
}
}
/// <summary>
/// Invokes all stored CallbackActions with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{
foreach (CallbackAction<T1, T2, T3, T4, T5, T6> action in actions)
{
try
{
action.Invoke(t1, t2, t3, t4, t5, t6);
}
catch (Exception e)
{
Debug.LogException(e, this);
}
}
}
#endregion
}
/// <summary>
/// A container of CallbackActions that is correlated to a specific scenario as
/// specified by the user of this class.
/// This variant accepts CallbackEvents with 1 generic parameter.
/// </summary>
public class CallbackEvent<T1, T2, T3, T4, T5, T6, T7> : ICallbackEvent
{
#region Properties --------------------------------------------------------------
/// <inheritdoc/>
public IEnumerable<ICallbackAction> Actions => actions;
#endregion
#region Fields ------------------------------------------------------------------
private List<ICallbackAction> actions = new List<ICallbackAction>();
#endregion
#region Usage Functions ---------------------------------------------------------
/// <inheritdoc/>
public void RegisterAction()
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6, T7>());
}
/// <inheritdoc/>
public void RegisterAction(ICallbackAction action)
{
// Check if valid action
if (action.GetType() != typeof(CallbackAction<T1, T2, T3, T4, T5, T6, T7>))
{
Debug.LogWarning("Attempted to register an invalid CallbackAction type. Ignoring.", this);
return;
}
actions.Add(action);
}
/// <summary>
/// Adds a CallbackAction into the event.
/// </summary>
/// <param name="action">CallbackAction to add.</param>
public void RegisterAction(CallbackAction<T1, T2, T3, T4, T5, T6, T7> action)
{
actions.Add(action);
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="action">System.Action to add as a CallbackAction.</param>
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7> action)
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6, T7>(action));
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
public void RegisterAction(Object target, MethodInfo method)
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6, T7>(target, method));
}
/// <inheritdoc/>
public void DeregisterAction(ICallbackAction action)
{
if (!actions.Remove(action))
{
Debug.LogWarning("Attempted to deregister invalid action. Ignored.", this);
}
}
/// <summary>
/// Invokes all stored CallbackActions with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
{
foreach (CallbackAction<T1, T2, T3, T4, T5, T6, T7> action in actions)
{
try
{
action.Invoke(t1, t2, t3, t4, t5, t6, t7);
}
catch (Exception e)
{
Debug.LogException(e, this);
}
}
}
#endregion
}
/// <summary>
/// A container of CallbackActions that is correlated to a specific scenario as
/// specified by the user of this class.
/// This variant accepts CallbackEvents with 1 generic parameter.
/// </summary>
public class CallbackEvent<T1, T2, T3, T4, T5, T6, T7, T8> : ICallbackEvent
{
#region Properties --------------------------------------------------------------
/// <inheritdoc/>
public IEnumerable<ICallbackAction> Actions => actions;
#endregion
#region Fields ------------------------------------------------------------------
private List<ICallbackAction> actions = new List<ICallbackAction>();
#endregion
#region Usage Functions ---------------------------------------------------------
/// <inheritdoc/>
public void RegisterAction()
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8>());
}
/// <inheritdoc/>
public void RegisterAction(ICallbackAction action)
{
// Check if valid action
if (action.GetType() != typeof(CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8>))
{
Debug.LogWarning("Attempted to register an invalid CallbackAction type. Ignoring.", this);
return;
}
actions.Add(action);
}
/// <summary>
/// Adds a CallbackAction into the event.
/// </summary>
/// <param name="action">CallbackAction to add.</param>
public void RegisterAction(CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8> action)
{
actions.Add(action);
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="action">System.Action to add as a CallbackAction.</param>
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8>(action));
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
public void RegisterAction(Object target, MethodInfo method)
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8>(target, method));
}
/// <inheritdoc/>
public void DeregisterAction(ICallbackAction action)
{
if (!actions.Remove(action))
{
Debug.LogWarning("Attempted to deregister invalid action. Ignored.", this);
}
}
/// <summary>
/// Invokes all stored CallbackActions with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
{
foreach (CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8> action in actions)
{
try
{
action.Invoke(t1, t2, t3, t4, t5, t6, t7, t8);
}
catch (Exception e)
{
Debug.LogException(e, this);
}
}
}
#endregion
}
/// <summary>
/// A container of CallbackActions that is correlated to a specific scenario as
/// specified by the user of this class.
/// This variant accepts CallbackEvents with 1 generic parameter.
/// </summary>
public class CallbackEvent<T1, T2, T3, T4, T5, T6, T7, T8, T9> : ICallbackEvent
{
#region Properties --------------------------------------------------------------
/// <inheritdoc/>
public IEnumerable<ICallbackAction> Actions => actions;
#endregion
#region Fields ------------------------------------------------------------------
private List<ICallbackAction> actions = new List<ICallbackAction>();
#endregion
#region Usage Functions ---------------------------------------------------------
/// <inheritdoc/>
public void RegisterAction()
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8, T9>());
}
/// <inheritdoc/>
public void RegisterAction(ICallbackAction action)
{
// Check if valid action
if (action.GetType() != typeof(CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8, T9>))
{
Debug.LogWarning("Attempted to register an invalid CallbackAction type. Ignoring.", this);
return;
}
actions.Add(action);
}
/// <summary>
/// Adds a CallbackAction into the event.
/// </summary>
/// <param name="action">CallbackAction to add.</param>
public void RegisterAction(CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8, T9> action)
{
actions.Add(action);
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="action">System.Action to add as a CallbackAction.</param>
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7, T8, T9> action)
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8, T9>(action));
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
public void RegisterAction(Object target, MethodInfo method)
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8, T9>(target, method));
}
/// <inheritdoc/>
public void DeregisterAction(ICallbackAction action)
{
if (!actions.Remove(action))
{
Debug.LogWarning("Attempted to deregister invalid action. Ignored.", this);
}
}
/// <summary>
/// Invokes all stored CallbackActions with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9)
{
foreach (CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8, T9> action in actions)
{
try
{
action.Invoke(t1, t2, t3, t4, t5, t6, t7, t8, t9);
}
catch (Exception e)
{
Debug.LogException(e, this);
}
}
}
#endregion
}
/// <summary>
/// A container of CallbackActions that is correlated to a specific scenario as
/// specified by the user of this class.
/// This variant accepts CallbackEvents with 1 generic parameter.
/// </summary>
public class CallbackEvent<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : ICallbackEvent
{
#region Properties --------------------------------------------------------------
/// <inheritdoc/>
public IEnumerable<ICallbackAction> Actions => actions;
#endregion
#region Fields ------------------------------------------------------------------
private List<ICallbackAction> actions = new List<ICallbackAction>();
#endregion
#region Usage Functions ---------------------------------------------------------
/// <inheritdoc/>
public void RegisterAction()
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>());
}
/// <inheritdoc/>
public void RegisterAction(ICallbackAction action)
{
// Check if valid action
if (action.GetType() != typeof(CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>))
{
Debug.LogWarning("Attempted to register an invalid CallbackAction type. Ignoring.", this);
return;
}
actions.Add(action);
}
/// <summary>
/// Adds a CallbackAction into the event.
/// </summary>
/// <param name="action">CallbackAction to add.</param>
public void RegisterAction(CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> action)
{
actions.Add(action);
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="action">System.Action to add as a CallbackAction.</param>
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> action)
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(action));
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
public void RegisterAction(Object target, MethodInfo method)
{
actions.Add(new CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(target, method));
}
/// <inheritdoc/>
public void DeregisterAction(ICallbackAction action)
{
if (!actions.Remove(action))
{
Debug.LogWarning("Attempted to deregister invalid action. Ignored.", this);
}
}
/// <summary>
/// Invokes all stored CallbackActions with the specified parameters.
/// </summary>
public void Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10)
{
foreach (CallbackAction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> action in actions)
{
try
{
action.Invoke(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10);
}
catch (Exception e)
{
Debug.LogException(e, this);
}
}
}
#endregion
}
}

View File

@ -0,0 +1,152 @@
<#
/************************************************************************************//*!
\file CallbackEvent.tt
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Oct 23, 2022
\brief Contains the T4 template for the definition of CallbackEvent and
related classes.
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.
*//*************************************************************************************/#>
<#@ template hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<# var max = 10; #>
/************************************************************************************//*!
\file CallbackEvent.cs
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Oct 23, 2022
\brief Contains the definition of CallbackEvent and related classes.
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.
*//*************************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Metadata.Ecma335;
namespace SHADE
{
/// <summary>
/// Interface for a CallbackEvent that all variants inherit from.
/// </summary>
public interface ICallbackEvent
{
/// <summary>
/// Registers an empty ICallbackAction.
/// </summary>
void RegisterAction();
/// <summary>
/// Registers an ICallbackAction with the event such that it will be called in
/// future
/// </summary>
/// <param name="action">ICallbackAction to register with.</param>
void RegisterAction(ICallbackAction action);
/// <summary>
/// Deregisters an ICallbackAction that was previously added. This should
/// only emit a warning if an action that was not previous added was
/// provided.
/// </summary>
/// <param name="action">ICallbackAction to remove.</param>
void DeregisterAction(ICallbackAction action);
/// <summary>
/// Iterable set of ICallbackActions that were registered to this event.
/// </summary>
IEnumerable<ICallbackAction> Actions { get; }
}
<# for (int i = 1; i <= max; ++i) { #>
/// <summary>
/// A container of CallbackActions that is correlated to a specific scenario as
/// specified by the user of this class.
/// This variant accepts CallbackEvents with 1 generic parameter.
/// </summary>
public class CallbackEvent<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>> : ICallbackEvent
{
#region Properties --------------------------------------------------------------
/// <inheritdoc/>
public IEnumerable<ICallbackAction> Actions => actions;
#endregion
#region Fields ------------------------------------------------------------------
private List<ICallbackAction> actions = new List<ICallbackAction>();
#endregion
#region Usage Functions ---------------------------------------------------------
/// <inheritdoc/>
public void RegisterAction()
{
actions.Add(new CallbackAction<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>>());
}
/// <inheritdoc/>
public void RegisterAction(ICallbackAction action)
{
// Check if valid action
if (action.GetType() != typeof(CallbackAction<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>>))
{
Debug.LogWarning("Attempted to register an invalid CallbackAction type. Ignoring.", this);
return;
}
actions.Add(action);
}
/// <summary>
/// Adds a CallbackAction into the event.
/// </summary>
/// <param name="action">CallbackAction to add.</param>
public void RegisterAction(CallbackAction<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>> action)
{
actions.Add(action);
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="action">System.Action to add as a CallbackAction.</param>
public void RegisterAction(Action<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>> action)
{
actions.Add(new CallbackAction<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>>(action));
}
/// <summary>
/// Constructs and adds a CallbackACtion into the event.
/// </summary>
/// <param name="target">Object to call the method on.</param>
/// <param name="method">Method to call.</param>
public void RegisterAction(Object target, MethodInfo method)
{
actions.Add(new CallbackAction<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>>(target, method));
}
/// <inheritdoc/>
public void DeregisterAction(ICallbackAction action)
{
if (!actions.Remove(action))
{
Debug.LogWarning("Attempted to deregister invalid action. Ignored.", this);
}
}
/// <summary>
/// Invokes all stored CallbackActions with the specified parameters.
/// </summary>
public void Invoke(<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#> t<#=t#><# if (t != i) { #>, <# } #><# } #>)
{
foreach (CallbackAction<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>> action in actions)
{
try
{
action.Invoke(<# for (int t = 1; t < i + 1; ++t) { #>t<#=t#><# if (t != i) { #>, <# } #><# } #>);
}
catch (Exception e)
{
Debug.LogException(e, this);
}
}
}
#endregion
}
<# } #>
}

View File

@ -0,0 +1,37 @@
using System;
using System.Runtime.InteropServices;
namespace SHADE
{
internal static class Debug
{
[DllImport("SHADE_Engine.dll", EntryPoint = "SHLog_Info")]
public static extern void LogInfo([MarshalAs(UnmanagedType.LPStr)] string str);
[DllImport("SHADE_Engine.dll", EntryPoint = "SHLog_Warning")]
public static extern void LogWarning([MarshalAs(UnmanagedType.LPStr)] string str);
[DllImport("SHADE_Engine.dll", EntryPoint = "SHLog_Error")]
public static extern void LogError([MarshalAs(UnmanagedType.LPStr)] string str);
[DllImport("SHADE_Engine.dll", EntryPoint = "SHLog_Critical")]
public static extern void LogCritical([MarshalAs(UnmanagedType.LPStr)] string str);
public static void LogInfo(string msg, Object thrower)
{
LogInfo($"[{thrower.GetType().Name}] {msg}");
}
public static void LogWarning(string msg, Object thrower)
{
LogWarning($"[{thrower.GetType().Name}] {msg}");
}
public static void LogError(string msg, Object thrower)
{
LogError($"[{thrower.GetType().Name}] {msg}");
}
public static void LogCritical(string msg, Object thrower)
{
LogCritical($"[{thrower.GetType().Name}] {msg}");
}
public static void LogException(Exception exception, Object thrower)
{
LogError($"[{ thrower.GetType().Name }] Unhandled exception: { exception.ToString() }");
}
}
}

View File

@ -0,0 +1,61 @@
/*************************************************************************//**
* \file SHShaderAsset.cpp
* \author Brandon Mak
* \date 24 October 2022
* \brief
*
* 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"
#include "SHShaderAsset.h"
namespace SHADE
{
SHShaderAsset::SHShaderAsset() noexcept
: spirvBinary{},
shaderType{SH_SHADER_TYPE::VERTEX},
name{}
{
}
SHShaderAsset::SHShaderAsset(SHShaderAsset const& rhs) noexcept
: spirvBinary{rhs.spirvBinary},
shaderType{ rhs.shaderType },
name{rhs.name}
{
}
SHShaderAsset::SHShaderAsset(SHShaderAsset&& rhs) noexcept
: spirvBinary{ std::move(rhs.spirvBinary) },
shaderType{ std::move(rhs.shaderType) },
name{ std::move(rhs.name) }
{
}
SHShaderAsset& SHShaderAsset::operator=(SHShaderAsset&& rhs) noexcept
{
if (this == &rhs)
{
return *this;
}
spirvBinary = std::move(rhs.spirvBinary);
shaderType = std::move(rhs.shaderType);
name = std::move(rhs.name);
}
SHShaderAsset& SHShaderAsset::operator=(SHShaderAsset const& rhs) noexcept
{
if (this == &rhs)
{
return *this;
}
spirvBinary = rhs.spirvBinary;
shaderType = rhs.shaderType;
name = rhs.name;
}
}

View File

@ -0,0 +1,48 @@
/*************************************************************************//**
* \file SHShaderAsset.h
* \author Brandon Mak
* \date 24 October 2022
* \brief
*
* 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
#include "SHAssetData.h"
#include "SH_API.h"
#include <vector>
#include <string>
namespace SHADE
{
enum class SH_SHADER_TYPE : uint8_t
{
VERTEX,
FRAGMENT,
COMPUTE,
INAVLID_TYPE
};
struct SH_API SHShaderAsset : SHAssetData
{
/*-----------------------------------------------------------------------*/
/* MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! container storing the spirv binary
std::vector<uint32_t> spirvBinary;
//! For the compilation of the shader. Vulkan backend will use it too
SH_SHADER_TYPE shaderType;
//! Name of the shader file (without parent path)
std::string name;
SHShaderAsset() noexcept;
SHShaderAsset(SHShaderAsset const& rhs) noexcept;
SHShaderAsset(SHShaderAsset&& rhs) noexcept;
SHShaderAsset& operator= (SHShaderAsset&& rhs) noexcept;
SHShaderAsset& operator= (SHShaderAsset const& rhs) noexcept;
};
}

View File

@ -1,24 +1,30 @@
/*************************************************************************//** /*************************************************************************//**
* \file SHAssimpLibrary.cpp * \file SHMeshCompiler.cpp
* \author Loh Xiao Qi * \author Loh Xiao Qi
* \date October 2022 * \date 30 September 2022
* \brief * \brief Library to write data in SHMeshAsset into binary file for faster
* loading in the future
*
* *
* Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
* disclosure of this file or its contents without the prior written consent * disclosure of this file or its contents without the prior written consent
* of DigiPen Institute of Technology is prohibited. * of DigiPen Institute of Technology is prohibited.
*****************************************************************************/ *****************************************************************************/
#include "SHpch.h" #include "SHpch.h"
#include "SHAssimpLibrary.h" #include "SHMeshCompiler.h"
#include "Graphics/MiddleEnd/Meshes/SHMeshData.h"
#include <assimp/postprocess.h> #include <assimp/postprocess.h>
#include <fstream>
namespace SHADE namespace SHADE
{ {
Assimp::Importer SHAssimpLibrary::aiImporter;
void SHAssimpLibrary::ProcessNode(aiNode const& node, aiScene const& scene, MeshVectorRef meshes) noexcept Assimp::Importer SHMeshCompiler::aiImporter;
{
for (size_t i {0}; i < node.mNumMeshes; ++i) void SHMeshCompiler::ProcessNode(aiNode const& node, aiScene const& scene, MeshVectorRef meshes) noexcept
{
for (size_t i{ 0 }; i < node.mNumMeshes; ++i)
{ {
aiMesh* mesh = scene.mMeshes[node.mMeshes[i]]; aiMesh* mesh = scene.mMeshes[node.mMeshes[i]];
meshes.push_back(ProcessMesh(*mesh)); meshes.push_back(ProcessMesh(*mesh));
@ -28,16 +34,16 @@ namespace SHADE
{ {
ProcessNode(*node.mChildren[i], scene, meshes); ProcessNode(*node.mChildren[i], scene, meshes);
} }
} }
void SHAssimpLibrary::ExtractAnimations(aiScene const& scene, AnimVectorRef anims) noexcept void SHMeshCompiler::ExtractAnimations(aiScene const& scene, AnimVectorRef anims) noexcept
{ {
if (scene.HasAnimations()) if (scene.HasAnimations())
{ {
std::vector<SHAnimationAsset> anims(scene.mNumAnimations); std::vector<SHAnimationAsset> anims(scene.mNumAnimations);
for (auto i{0}; i < scene.mNumAnimations; ++i) for (auto i{ 0 }; i < scene.mNumAnimations; ++i)
{ {
auto const& anim {*scene.mAnimations[i]}; auto const& anim{ *scene.mAnimations[i] };
anims[i].name = anim.mName.C_Str(); anims[i].name = anim.mName.C_Str();
@ -47,17 +53,17 @@ namespace SHADE
std::copy_n(anim.mChannels, anim.mNumChannels, anims[i].nodeChannels.data()); std::copy_n(anim.mChannels, anim.mNumChannels, anims[i].nodeChannels.data());
std::copy_n(anim.mMeshChannels, anim.mNumMeshChannels, anims[i].meshChannels.data()); std::copy_n(anim.mMeshChannels, anim.mNumMeshChannels, anims[i].meshChannels.data());
std::copy_n(anim.mMorphMeshChannels, anim.mNumMorphMeshChannels, anims[i].morphMeshChannels.data()); std::copy_n(anim.mMorphMeshChannels, anim.mNumMorphMeshChannels, anims[i].morphMeshChannels.data());
} }
} }
} }
SHMeshAsset* SHAssimpLibrary::ProcessMesh(aiMesh const& mesh) noexcept SHMeshAsset* SHMeshCompiler::ProcessMesh(aiMesh const& mesh) noexcept
{ {
SHMeshAsset* result = new SHMeshAsset(); SHMeshAsset* result = new SHMeshAsset();
result->compiled = false; result->compiled = false;
result->changed = false; result->changed = false;
for (size_t i{0}; i < mesh.mNumVertices; ++i) for (size_t i{ 0 }; i < mesh.mNumVertices; ++i)
{ {
// Vertex position // Vertex position
SHVec3 vertex; SHVec3 vertex;
@ -67,7 +73,7 @@ namespace SHADE
result->vertexPosition.push_back(vertex); result->vertexPosition.push_back(vertex);
// Tex coords // Tex coords
SHVec2 texCoord{0.f, 0.f}; SHVec2 texCoord{ 0.f, 0.f };
if (mesh.mTextureCoords[0]) if (mesh.mTextureCoords[0])
{ {
texCoord.x = mesh.mTextureCoords[0][i].x; texCoord.x = mesh.mTextureCoords[0][i].x;
@ -76,7 +82,7 @@ namespace SHADE
result->texCoords.push_back(texCoord); result->texCoords.push_back(texCoord);
// Normals // Normals
SHVec3 normal{0.f, 0.f, 0.f}; SHVec3 normal{ 0.f, 0.f, 0.f };
if (mesh.mNormals) if (mesh.mNormals)
{ {
normal.x = mesh.mNormals[i].x; normal.x = mesh.mNormals[i].x;
@ -86,7 +92,7 @@ namespace SHADE
result->vertexNormal.push_back(normal); result->vertexNormal.push_back(normal);
// Tangent // Tangent
SHVec3 tangent{0.f, 0.f, 0.f}; SHVec3 tangent{ 0.f, 0.f, 0.f };
if (mesh.mTangents) if (mesh.mTangents)
{ {
tangent.x = mesh.mTangents[i].x; tangent.x = mesh.mTangents[i].x;
@ -96,10 +102,10 @@ namespace SHADE
result->vertexTangent.push_back(tangent); result->vertexTangent.push_back(tangent);
} }
for (size_t i {0}; i < mesh.mNumFaces; ++i) for (size_t i{ 0 }; i < mesh.mNumFaces; ++i)
{ {
aiFace face = mesh.mFaces[i]; aiFace face = mesh.mFaces[i];
for (size_t j{0}; j < face.mNumIndices; ++j) for (size_t j{ 0 }; j < face.mNumIndices; ++j)
{ {
result->indices.push_back(face.mIndices[j]); result->indices.push_back(face.mIndices[j]);
} }
@ -110,10 +116,10 @@ namespace SHADE
result->header.name = mesh.mName.C_Str(); result->header.name = mesh.mName.C_Str();
return result; return result;
} }
void SHAssimpLibrary::LoadFromFile(AssetPath path, MeshVectorRef meshes, AnimVectorRef anims) noexcept void SHMeshCompiler::LoadFromFile(AssetPath path, MeshVectorRef meshes, AnimVectorRef anims) noexcept
{ {
const aiScene* scene = aiImporter.ReadFile(path.string().c_str(), const aiScene* scene = aiImporter.ReadFile(path.string().c_str(),
aiProcess_Triangulate // Make sure we get triangles rather than nvert polygons aiProcess_Triangulate // Make sure we get triangles rather than nvert polygons
| aiProcess_GenUVCoords // Convert any type of mapping to uv mapping | aiProcess_GenUVCoords // Convert any type of mapping to uv mapping
@ -132,10 +138,64 @@ namespace SHADE
return; return;
} }
ExtractAnimations(*scene, anims); //ExtractAnimations(*scene, anims);
ProcessNode(*scene->mRootNode, *scene, meshes); ProcessNode(*scene->mRootNode, *scene, meshes);
aiImporter.FreeScene(); aiImporter.FreeScene();
} }
std::optional<AssetPath> SHMeshCompiler::CompileMeshBinary(SHMeshAsset const& asset, AssetPath path) noexcept
{
std::string newPath{ path.parent_path().string() + '/' };
newPath += asset.header.name + MESH_EXTENSION.data();
std::ofstream file{ newPath, std::ios::out | std::ios::binary | std::ios::trunc };
if (!file.is_open())
{
SHLOG_ERROR("Unable to open file for writing mesh file: {}", path.string());
}
file.write(
reinterpret_cast<char const*>(&(asset.header.vertexCount)),
sizeof(uint32_t)
);
file.write(
reinterpret_cast<const char*>(&(asset.header.indexCount)),
sizeof(uint32_t)
);
auto const vertexVec3Byte{ sizeof(SHVec3) * asset.header.vertexCount };
auto const vertexVec2Byte{ sizeof(SHVec2) * asset.header.vertexCount };
file.write(
reinterpret_cast<char const*>(asset.vertexPosition.data()),
vertexVec3Byte
);
file.write(
reinterpret_cast<char const*>(asset.vertexTangent.data()),
vertexVec3Byte
);
file.write(
reinterpret_cast<char const*>(asset.vertexNormal.data()),
vertexVec3Byte
);
file.write(
reinterpret_cast<char const*>(asset.texCoords.data()),
vertexVec2Byte
);
file.write(
reinterpret_cast<char const*>(asset.indices.data()),
sizeof(uint32_t) * asset.header.indexCount
);
file.close();
return newPath;
}
} }

View File

@ -0,0 +1,40 @@
/*************************************************************************//**
* \file SHMeshCompiler.h
* \author Loh Xiao Qi
* \date 30 September 2022
* \brief Library to write data in SHMeshAsset into binary file for faster
* loading in the future
*
*
* 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
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <vector>
#include "Assets/Asset Types/SHAnimationAsset.h"
#include "Assets/Asset Types/SHMeshAsset.h"
#include "Assets/SHAssetMacros.h"
namespace SHADE
{
class SHMeshCompiler
{
using MeshVectorRef = std::vector<SHMeshAsset*>&;
using AnimVectorRef = std::vector<SHAnimationAsset*>&;
static Assimp::Importer aiImporter;
static void ProcessNode(aiNode const& node, aiScene const& scene, MeshVectorRef meshes) noexcept;
static void ExtractAnimations(aiScene const& scene, AnimVectorRef anims) noexcept;
static SHMeshAsset* ProcessMesh(aiMesh const& mesh) noexcept;
public:
static void LoadFromFile(AssetPath path, MeshVectorRef meshes, AnimVectorRef anims) noexcept;
static std::optional<AssetPath> CompileMeshBinary(SHMeshAsset const& asset, AssetPath path) noexcept;
};
}

View File

@ -0,0 +1,162 @@
/*************************************************************************//**
* \file SHShaderSourceCompiler.cpp
* \author Loh Xiao Qi
* \date 23 10 2022
* \brief
*
* 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"
#include "SHShaderSourceCompiler.h"
#include "shaderc/shaderc.hpp"
#include <fstream>
#include <sstream>
#include <string>
#include <algorithm>
namespace SHADE
{
std::string SHShaderSourceCompiler::CompileShaderSourceToBinary(AssetPath path, SHShaderAsset const& data) noexcept
{
std::string newPath{ path.string() };
newPath = newPath.substr(0, newPath.find_last_of('.'));
newPath += SHADER_BUILT_IN_EXTENSION.data();
std::ofstream file{ newPath, std::ios::binary | std::ios::out | std::ios::trunc };
file.write(
reinterpret_cast<char const*>(& data.shaderType), sizeof(uint8_t)
);
size_t const byteCount = sizeof(uint32_t) * data.spirvBinary.size();
file.write(
reinterpret_cast<char const*>(&byteCount), sizeof(size_t)
);
file.write(
reinterpret_cast<char const*>(data.spirvBinary.data()), byteCount
);
file.close();
return newPath;
}
SHShaderAsset const* SHShaderSourceCompiler::CompileShaderSourceToMemory(std::string const& data, std::string const& name, SH_SHADER_TYPE type) noexcept
{
// shaderc compiler
shaderc::Compiler compiler;
shaderc::CompileOptions options;
options.AddMacroDefinition("MY_DEFINE", "1");
//TODO: Check if we need optimisation levels when compiling into spirv
// Set optimization levels
//if (opLevel != shaderc_optimization_level_zero)
// options.SetOptimizationLevel(opLevel);
// Attempt to get the shaderc equivalent shader stage
shaderc_shader_kind shaderKind;
switch (type)
{
case SH_SHADER_TYPE::VERTEX:
shaderKind = shaderc_shader_kind::shaderc_glsl_vertex_shader;
break;
case SH_SHADER_TYPE::FRAGMENT:
shaderKind = shaderc_shader_kind::shaderc_glsl_fragment_shader;
break;
case SH_SHADER_TYPE::COMPUTE:
shaderKind = shaderc_shader_kind::shaderc_glsl_compute_shader;
break;
default:
shaderKind = shaderc_shader_kind::shaderc_glsl_vertex_shader;
break;
}
// Compile the shader and get the result
shaderc::SpvCompilationResult compileResult = compiler.CompileGlslToSpv(data, shaderKind, name.c_str(), options);
if (compileResult.GetCompilationStatus() != shaderc_compilation_status_success)
{
SHLOG_ERROR("Shaderc failed to compile GLSL shader to binary | " + compileResult.GetErrorMessage());
return nullptr;
}
auto result = new SHShaderAsset();
result->spirvBinary.resize(compileResult.end() - compileResult.begin());
std::ranges::copy(compileResult.begin(), compileResult.end(), result->spirvBinary.data());
result->name = name;
result->shaderType = type;
return result;
}
SH_SHADER_TYPE SHShaderSourceCompiler::GetShaderTypeFromFilename(std::string name) noexcept
{
for (auto i { 0}; i < SHADER_TYPE_MAX_COUNT; ++i)
{
if (name.find(SHADER_IDENTIFIERS[i].data()) != std::string::npos)
{
return static_cast<SH_SHADER_TYPE>(i);
}
}
return SH_SHADER_TYPE::INAVLID_TYPE;
}
std::optional<AssetPath> SHShaderSourceCompiler::LoadAndCompileShader(AssetPath path) noexcept
{
auto type = GetShaderTypeFromFilename(path.filename().string());
if (type == SH_SHADER_TYPE::INAVLID_TYPE)
{
SHLOG_ERROR("Invalid filename for shaders, follow suffix in SHAssetMacros.h: {}", path.string());
return {};
}
path.make_preferred();
std::ifstream file{ path.string(), std::ios::in };
if (file.is_open())
{
std::stringstream stream;
stream << file.rdbuf();
std::string const content = stream.str();
auto data = CompileShaderSourceToMemory(content, path.filename().string(), type);
if (data == nullptr)
{
return{};
}
return CompileShaderSourceToBinary(path, *data);
}
SHLOG_ERROR("Unable to open shader file: {}", path.string());
return {};
}
std::optional<AssetPath> SHShaderSourceCompiler::CompileShaderFromString
(std::string const& string, AssetPath path, SH_SHADER_TYPE type) noexcept
{
auto const data = CompileShaderSourceToMemory(string, path.filename().string(), type);
if (data == nullptr)
{
return{};
}
return CompileShaderSourceToBinary(path, *data);
}
}

View File

@ -0,0 +1,31 @@
/*************************************************************************//**
* \file SHShaderSourceCompiler.h
* \author Loh Xiao Qi
* \date 23 10 2022
* \brief
*
* 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
#include "Assets/SHAssetMacros.h"
#include "Assets/Asset Types/SHShaderAsset.h"
namespace SHADE
{
class SHShaderSourceCompiler
{
private:
static std::string CompileShaderSourceToBinary(AssetPath path, SHShaderAsset const& data) noexcept;
static SHShaderAsset const* CompileShaderSourceToMemory(std::string const& data, std::string const& name, SH_SHADER_TYPE type) noexcept;
static SH_SHADER_TYPE GetShaderTypeFromFilename(std::string name) noexcept;
public:
static std::optional<AssetPath> LoadAndCompileShader(AssetPath path) noexcept;
static std::optional<AssetPath> CompileShaderFromString
(std::string const& string, AssetPath path, SH_SHADER_TYPE type) noexcept;
};
}

View File

@ -0,0 +1,171 @@
/*************************************************************************//**
* \file SHTextureCompiler.cpp
* \author Loh Xiao Qi
* \date 30 September 2022
* \brief Library to write data in SHTextureAsset into binary file for
* faster loading in the future
*
*
* 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"
#include "SHTextureCompiler.h"
#include <fstream>
namespace SHADE
{
std::string SHTextureCompiler::TinyDDSResultToString(tinyddsloader::Result value)
{
switch (value)
{
case tinyddsloader::Result::ErrorFileOpen:
return "File open err";
case tinyddsloader::Result::ErrorRead:
return "File read err";
case tinyddsloader::Result::ErrorMagicWord:
return "File header magic word err";
case tinyddsloader::Result::ErrorSize:
return "File size err";
case tinyddsloader::Result::ErrorVerify:
return "Pixel format err";
case tinyddsloader::Result::ErrorNotSupported:
return "Unsupported format";
case tinyddsloader::Result::ErrorInvalidData:
return "Invalid data";
default:
return "Unknown";
}
}
vk::Format SHTextureCompiler::ddsLoaderToVkFormat(tinyddsloader::DDSFile::DXGIFormat format, bool isLinear)
{
switch (format)
{
case tinyddsloader::DDSFile::DXGIFormat::BC1_UNorm:
case tinyddsloader::DDSFile::DXGIFormat::BC1_UNorm_SRGB:
return isLinear ? vk::Format::eBc1RgbaUnormBlock : vk::Format::eBc1RgbaSrgbBlock;
case tinyddsloader::DDSFile::DXGIFormat::BC2_UNorm:
case tinyddsloader::DDSFile::DXGIFormat::BC2_UNorm_SRGB:
return isLinear ? vk::Format::eBc2UnormBlock : vk::Format::eBc2SrgbBlock;
case tinyddsloader::DDSFile::DXGIFormat::BC3_UNorm:
case tinyddsloader::DDSFile::DXGIFormat::BC3_UNorm_SRGB:
return isLinear ? vk::Format::eBc3UnormBlock : vk::Format::eBc3SrgbBlock;
case tinyddsloader::DDSFile::DXGIFormat::BC5_UNorm:
case tinyddsloader::DDSFile::DXGIFormat::BC5_SNorm:
return isLinear ? vk::Format::eBc5UnormBlock : vk::Format::eBc5SnormBlock;
case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_UNorm:
case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_UNorm_SRGB:
return isLinear ? vk::Format::eR8G8B8A8Unorm : vk::Format::eR8G8B8A8Srgb;
case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_SNorm:
return vk::Format::eR8G8B8A8Snorm;
case tinyddsloader::DDSFile::DXGIFormat::B8G8R8A8_UNorm:
case tinyddsloader::DDSFile::DXGIFormat::B8G8R8A8_UNorm_SRGB:
return isLinear ? vk::Format::eB8G8R8A8Unorm : vk::Format::eB8G8R8A8Srgb;
case tinyddsloader::DDSFile::DXGIFormat::B8G8R8X8_UNorm:
case tinyddsloader::DDSFile::DXGIFormat::B8G8R8X8_UNorm_SRGB:
return isLinear ? vk::Format::eB8G8R8A8Unorm : vk::Format::eB8G8R8Srgb;
default:
throw std::runtime_error("Unsupported DDS format.");
}
}
void SHTextureCompiler::LoadTinyDDS(AssetPath path, SHTextureAsset& asset) noexcept
{
tinyddsloader::Result loadResult = tinyddsloader::Result::Success;
tinyddsloader::DDSFile file;
loadResult = file.Load(path.string().c_str());
if (loadResult != tinyddsloader::Result::Success)
{
SHLOG_ERROR("Unable to load Texture file: {} at {}", TinyDDSResultToString(loadResult), path.string());
}
size_t totalBytes{ 0 };
std::vector<uint32_t> mipOff(file.GetMipCount());
for (size_t i{ 0 }; i < file.GetMipCount(); ++i)
{
mipOff[i] = static_cast<uint32_t>(totalBytes);
totalBytes += file.GetImageData(static_cast<uint32_t>(i), 0)->m_memSlicePitch;
}
SHTexture::PixelChannel* pixel = new SHTexture::PixelChannel[totalBytes];
std::memcpy(pixel, file.GetImageData()->m_mem, totalBytes);
//pixel = std::move(reinterpret_cast<SHTexture::PixelChannel const*>(file.GetDDSData()));
asset.name = path.stem().string();
asset.compiled = false;
asset.numBytes = static_cast<uint32_t>(totalBytes);
asset.width = file.GetWidth();
asset.height = file.GetHeight();
asset.format = ddsLoaderToVkFormat(file.GetFormat(), true);
asset.mipOffsets = std::move(mipOff);
asset.pixelData = std::move(pixel);
}
std::string SHTextureCompiler::WriteToFile(SHTextureAsset const& asset, AssetPath path) noexcept
{
std::string newPath{ path.string() };
newPath = newPath.substr(0, newPath.find_last_of('.'));
newPath += TEXTURE_EXTENSION;
std::ofstream file{ newPath, std::ios::out | std::ios::binary };
if (!file.is_open())
{
SHLOG_ERROR("Unable to open file for writing texture file: {}", path.string());
}
constexpr auto intBytes{ sizeof(uint32_t) };
uint32_t const mipOffsetCount{ static_cast<uint32_t>(asset.mipOffsets.size()) };
file.write(
reinterpret_cast<char const*>(&asset.numBytes),
intBytes
);
file.write(
reinterpret_cast<char const*>(&asset.width),
intBytes
);
file.write(
reinterpret_cast<char const*>(&asset.height),
intBytes
);
file.write(
reinterpret_cast<char const*>(&asset.format),
sizeof(SHTexture::TextureFormat)
);
file.write(
reinterpret_cast<char const*>(&mipOffsetCount),
intBytes
);
file.write(
reinterpret_cast<char const*>(asset.mipOffsets.data()),
intBytes * asset.mipOffsets.size()
);
file.write(
reinterpret_cast<char const*>(asset.pixelData),
asset.numBytes
);
file.close();
return newPath;
}
std::optional<AssetPath> SHTextureCompiler::CompileTextureAsset(AssetPath path)
{
auto data = new SHTextureAsset();
LoadTinyDDS(path, *data);
return WriteToFile(*data, path);
}
}

View File

@ -0,0 +1,33 @@
/*************************************************************************//**
* \file SHTextureCompiler.h
* \author Loh Xiao Qi
* \date 30 September 2022
* \brief Library to write data in SHTextureAsset into binary file for
* faster loading in the future
*
*
* 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
#define TINYDDSLOADER_IMPLEMENTATION
#include "Assets/Asset Types/SHTextureAsset.h"
#include "Assets/SHAssetMacros.h"
#include "tinyddsloader.h"
namespace SHADE
{
class SHTextureCompiler
{
private:
static std::string TinyDDSResultToString(tinyddsloader::Result value);
static vk::Format ddsLoaderToVkFormat(tinyddsloader::DDSFile::DXGIFormat format, bool isLinear);
static void LoadTinyDDS(AssetPath path, SHTextureAsset& asset) noexcept;
static std::string WriteToFile(SHTextureAsset const& asset, AssetPath path) noexcept;
public:
static std::optional<AssetPath> CompileTextureAsset(AssetPath path);
};
}

View File

@ -10,6 +10,7 @@
*****************************************************************************/ *****************************************************************************/
#pragma once #pragma once
#include "Assets/SHAssetMacros.h"
#include "Assets/Asset Types/SHAssetData.h" #include "Assets/Asset Types/SHAssetData.h"
namespace SHADE namespace SHADE

View File

@ -10,13 +10,13 @@
* of DigiPen Institute of Technology is prohibited. * of DigiPen Institute of Technology is prohibited.
*****************************************************************************/ *****************************************************************************/
#pragma once #pragma once
#include "../SHAssetMacros.h" #include "Assets/SHAssetMacros.h"
#include "../Asset Types/SHMeshAsset.h" #include "Assets/Asset Types/SHMeshAsset.h"
#include "SHAssetLoader.h" #include "SHAssetLoader.h"
namespace SHADE namespace SHADE
{ {
struct SHMeshLoader : public SHAssetLoader struct SHMeshLoader : SHAssetLoader
{ {
void LoadSHMesh(AssetPath path, SHMeshAsset& meshes) noexcept; void LoadSHMesh(AssetPath path, SHMeshAsset& meshes) noexcept;
SHAssetData* Load(AssetPath path) override; SHAssetData* Load(AssetPath path) override;

View File

@ -0,0 +1,46 @@
/*************************************************************************//**
* \file SHShaderSourceLoader.cpp
* \author Loh Xiao Qi
* \date 23 10 2022
* \brief
*
* 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"
#include "SHShaderSourceLoader.h"
#include "Assets/Asset Types/SHShaderAsset.h"
#include <fstream>
namespace SHADE
{
SHAssetData* SHShaderSourceLoader::Load(AssetPath path)
{
auto result = new SHShaderAsset();
result->name = path.stem().stem().string();
std::ifstream file{ path.string(), std::ios::in | std::ios::binary };
if (!file.is_open())
{
SHLOG_ERROR("Unable to open compiled shader file: {}", path.string());
return nullptr;
}
size_t byteCount = 0;
file.read(reinterpret_cast<char*>(&result->shaderType), sizeof(uint8_t));
file.read(reinterpret_cast<char*>(&byteCount), sizeof(size_t));
result->spirvBinary.resize(byteCount / sizeof(uint32_t));
file.read(reinterpret_cast<char*>(result->spirvBinary.data()), byteCount);
file.close();
return result;
}
}

View File

@ -1,7 +1,7 @@
/*************************************************************************//** /*************************************************************************//**
* \file SHAnimationAsset.h * \file SHShaderSourceLoader.h
* \author Loh Xiao Qi * \author Loh Xiao Qi
* \date October 2022 * \date 23 10 2022
* \brief * \brief
* *
* Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
@ -10,21 +10,13 @@
*****************************************************************************/ *****************************************************************************/
#pragma once #pragma once
#include <vector> #include "Assets/Libraries/Loaders/SHAssetLoader.h"
#include <assimp/anim.h> #include "Assets/SHAssetMacros.h"
#include "SH_API.power h"
namespace SHADE namespace SHADE
{ {
struct SH_API SHAnimationAsset struct SHShaderSourceLoader : SHAssetLoader
{ {
std::string name; SHAssetData* Load(AssetPath path) override;
std::vector<aiNodeAnim*> nodeChannels;
std::vector<aiMeshAnim*> meshChannels;
std::vector<aiMeshMorphAnim*> morphMeshChannels;
double duration;
double ticksPerSecond;
}; };
} }

View File

@ -0,0 +1,58 @@
/*************************************************************************//**
* \file SHTextureLoader.cpp
* \author Loh Xiao Qi
* \date 30 September 2022
* \brief Library to load dds textures and custom binary format
*
*
* 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"
#include "SHTextureLoader.h"
#include <fstream>
namespace SHADE
{
void SHTextureLoader::LoadSHTexture(AssetPath path, SHTextureAsset& asset) noexcept
{
std::ifstream file{ path.string(), std::ios::in | std::ios::binary };
if (!file.is_open())
{
SHLOG_ERROR("Error opening SHTexture file: {}", path.string());
}
auto const intBytes{ sizeof(uint32_t) };
uint32_t mipCount;
file.read(reinterpret_cast<char*>(&asset.numBytes), intBytes);
file.read(reinterpret_cast<char*>(&asset.width), intBytes);
file.read(reinterpret_cast<char*>(&asset.height), intBytes);
file.read(reinterpret_cast<char*>(&asset.format), sizeof(SHTexture::TextureFormat));
file.read(reinterpret_cast<char*>(&mipCount), intBytes);
std::vector<uint32_t> mips(mipCount);
file.read(reinterpret_cast<char*>(mips.data()), intBytes * mipCount);
auto pixel = new SHTexture::PixelChannel[asset.numBytes];
file.read(reinterpret_cast<char*>(pixel), asset.numBytes);
asset.mipOffsets = std::move(mips);
asset.pixelData = std::move(pixel);
asset.compiled = true;
file.close();
}
SHAssetData* SHTextureLoader::Load(AssetPath path)
{
auto result = new SHTextureAsset();
LoadSHTexture(path, *result);
return result;
}
}

View File

@ -1,9 +1,8 @@
/*************************************************************************//** /*************************************************************************//**
* \file SHTextureCompiler.h * \file SHTextureLoader.h
* \author Loh Xiao Qi * \author Loh Xiao Qi
* \date 30 September 2022 * \date 30 September 2022
* \brief Library to write data in SHTextureAsset into binary file for * \brief Library to load dds textures and custom binary format
* faster loading in the future
* *
* *
* Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
@ -12,13 +11,15 @@
*****************************************************************************/ *****************************************************************************/
#pragma once #pragma once
#include "Assets/Asset Types/SHTextureAsset.h"
#include "Assets/SHAssetMacros.h" #include "Assets/SHAssetMacros.h"
#include "Assets/Asset Types/SHTextureAsset.h"
#include "SHAssetLoader.h"
namespace SHADE namespace SHADE
{ {
struct SHTextureCompiler class SHTextureLoader : public SHAssetLoader
{ {
static std::string CompileTextureBinary(SHTextureAsset const& asset, AssetPath path); void LoadSHTexture(AssetPath path, SHTextureAsset& asset) noexcept;
SHAssetData* Load(AssetPath path) override;
}; };
} }

View File

@ -1,36 +0,0 @@
/*************************************************************************//**
* \file SHAssimpLibrary.h
* \author Loh Xiao Qi
* \date October 2022
* \brief
*
* 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
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <vector>
#include "../SHAssetMacros.h"
#include "../Asset Types/SHMeshAsset.h"
#include "../Asset Types/SHAnimationAsset.h"
namespace SHADE
{
class SHAssimpLibrary
{
private:
using MeshVectorRef = std::vector<SHMeshAsset*>&;
using AnimVectorRef = std::vector<SHAnimationAsset*>&;
static Assimp::Importer aiImporter;
static void ProcessNode(aiNode const& node, aiScene const& scene,MeshVectorRef meshes) noexcept;
static void ExtractAnimations(aiScene const& scene, AnimVectorRef anims) noexcept;
static SHMeshAsset* ProcessMesh(aiMesh const& mesh) noexcept;
public:
static void LoadFromFile(AssetPath path, MeshVectorRef meshes, AnimVectorRef anims) noexcept;
};
}

View File

@ -1,72 +0,0 @@
/*************************************************************************//**
* \file SHMeshCompiler.cpp
* \author Loh Xiao Qi
* \date 30 September 2022
* \brief Library to write data in SHMeshAsset into binary file for faster
* loading in the future
*
*
* 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"
#include "SHMeshCompiler.h"
#include "Graphics/MiddleEnd/Meshes/SHMeshData.h"
#include <fstream>
std::string SHADE::SHMeshCompiler::CompileMeshBinary(SHMeshAsset const& asset, AssetPath path) noexcept
{
std::string newPath{ path.string() };
newPath = newPath.substr(0, newPath.find_last_of('/') + 1);
newPath += asset.header.name + MESH_EXTENSION.data();
std::ofstream file{ newPath, std::ios::out | std::ios::binary | std::ios::trunc };
if (!file.is_open())
{
SHLOG_ERROR("Unable to open file for writing mesh file: {}", path.string());
}
file.write(
reinterpret_cast<char const*>(&(asset.header.vertexCount)),
sizeof(uint32_t)
);
file.write(
reinterpret_cast<const char*>(&(asset.header.indexCount)),
sizeof(uint32_t)
);
auto const vertexVec3Byte {sizeof(SHVec3) * asset.header.vertexCount};
auto const vertexVec2Byte {sizeof(SHVec2) * asset.header.vertexCount};
file.write(
reinterpret_cast<char const*>(asset.vertexPosition.data()),
vertexVec3Byte
);
file.write(
reinterpret_cast<char const*>(asset.vertexTangent.data()),
vertexVec3Byte
);
file.write(
reinterpret_cast<char const*>(asset.vertexNormal.data()),
vertexVec3Byte
);
file.write(
reinterpret_cast<char const*>(asset.texCoords.data()),
vertexVec2Byte
);
file.write(
reinterpret_cast<char const*>(asset.indices.data()),
sizeof(uint32_t) * asset.header.indexCount
);
file.close();
return newPath;
}

View File

@ -1,26 +0,0 @@
/*************************************************************************//**
* \file SHMeshCompiler.h
* \author Loh Xiao Qi
* \date 30 September 2022
* \brief Library to write data in SHMeshAsset into binary file for faster
* loading in the future
*
*
* 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
#include "../Asset Types/SHMeshAsset.h"
#include "../SHAssetMacros.h"
namespace SHADE
{
class SHMeshCompiler
{
private:
public:
static std::string CompileMeshBinary(SHMeshAsset const& asset, AssetPath path) noexcept;
};
}

View File

@ -1,75 +0,0 @@
/*************************************************************************//**
* \file SHTextureCompiler.cpp
* \author Loh Xiao Qi
* \date 30 September 2022
* \brief Library to write data in SHTextureAsset into binary file for
* faster loading in the future
*
*
* 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"
#include "SHTextureCompiler.h"
#include <fstream>
namespace SHADE
{
std::string SHTextureCompiler::CompileTextureBinary(SHTextureAsset const& asset, AssetPath path)
{
std::string newPath{ path.string() };
newPath = newPath.substr(0, newPath.find_last_of('.'));
newPath += TEXTURE_EXTENSION;
std::ofstream file{ newPath, std::ios::out | std::ios::binary };
if (!file.is_open())
{
SHLOG_ERROR("Unable to open file for writing texture file: {}", path.string());
}
auto const intBytes{sizeof(uint32_t)};
uint32_t mipOffsetCount{ static_cast<uint32_t>(asset.mipOffsets.size()) };
file.write(
reinterpret_cast<char const*>(&asset.numBytes),
intBytes
);
file.write(
reinterpret_cast<char const*>(&asset.width),
intBytes
);
file.write(
reinterpret_cast<char const*>(&asset.height),
intBytes
);
file.write(
reinterpret_cast<char const*>(&asset.format),
sizeof(SHTexture::TextureFormat)
);
file.write(
reinterpret_cast<char const*>(&mipOffsetCount),
intBytes
);
file.write(
reinterpret_cast<char const*>(asset.mipOffsets.data()),
intBytes * asset.mipOffsets.size()
);
file.write(
reinterpret_cast<char const*>(asset.pixelData),
asset.numBytes
);
file.close();
return newPath;
}
}

View File

@ -1,156 +0,0 @@
/*************************************************************************//**
* \file SHTextureLoader.cpp
* \author Loh Xiao Qi
* \date 30 September 2022
* \brief Library to load dds textures and custom binary format
*
*
* 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"
#include "SHTextureLoader.h"
namespace SHADE
{
std::string SHTextureLoader::TinyDDSResultToString(tinyddsloader::Result value)
{
switch (value)
{
case tinyddsloader::Result::ErrorFileOpen:
return "File open err";
case tinyddsloader::Result::ErrorRead:
return "File read err";
case tinyddsloader::Result::ErrorMagicWord:
return "File header magic word err";
case tinyddsloader::Result::ErrorSize:
return "File size err";
case tinyddsloader::Result::ErrorVerify:
return "Pixel format err";
case tinyddsloader::Result::ErrorNotSupported:
return "Unsupported format";
case tinyddsloader::Result::ErrorInvalidData:
return "Invalid data";
default:
return "Unknown";
}
}
vk::Format SHTextureLoader::ddsLoaderToVkFormat(tinyddsloader::DDSFile::DXGIFormat format, bool isLinear)
{
switch (format)
{
case tinyddsloader::DDSFile::DXGIFormat::BC1_UNorm:
case tinyddsloader::DDSFile::DXGIFormat::BC1_UNorm_SRGB:
return isLinear ? vk::Format::eBc1RgbaUnormBlock : vk::Format::eBc1RgbaSrgbBlock;
case tinyddsloader::DDSFile::DXGIFormat::BC2_UNorm:
case tinyddsloader::DDSFile::DXGIFormat::BC2_UNorm_SRGB:
return isLinear ? vk::Format::eBc2UnormBlock : vk::Format::eBc2SrgbBlock;
case tinyddsloader::DDSFile::DXGIFormat::BC3_UNorm:
case tinyddsloader::DDSFile::DXGIFormat::BC3_UNorm_SRGB:
return isLinear ? vk::Format::eBc3UnormBlock : vk::Format::eBc3SrgbBlock;
case tinyddsloader::DDSFile::DXGIFormat::BC5_UNorm:
case tinyddsloader::DDSFile::DXGIFormat::BC5_SNorm:
return isLinear ? vk::Format::eBc5UnormBlock : vk::Format::eBc5SnormBlock;
case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_UNorm:
case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_UNorm_SRGB:
return isLinear ? vk::Format::eR8G8B8A8Unorm : vk::Format::eR8G8B8A8Srgb;
case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_SNorm:
return vk::Format::eR8G8B8A8Snorm;
case tinyddsloader::DDSFile::DXGIFormat::B8G8R8A8_UNorm:
case tinyddsloader::DDSFile::DXGIFormat::B8G8R8A8_UNorm_SRGB:
return isLinear ? vk::Format::eB8G8R8A8Unorm : vk::Format::eB8G8R8A8Srgb;
case tinyddsloader::DDSFile::DXGIFormat::B8G8R8X8_UNorm:
case tinyddsloader::DDSFile::DXGIFormat::B8G8R8X8_UNorm_SRGB:
return isLinear ? vk::Format::eB8G8R8A8Unorm : vk::Format::eB8G8R8Srgb;
default:
throw std::runtime_error("Unsupported DDS format.");
}
}
void SHTextureLoader::LoadTinyDDS(AssetPath path, SHTextureAsset& asset) noexcept
{
tinyddsloader::Result loadResult = tinyddsloader::Result::Success;
tinyddsloader::DDSFile file;
loadResult = file.Load(path.string().c_str());
if (loadResult != tinyddsloader::Result::Success)
{
SHLOG_ERROR("Unable to load Texture file: {} at {}", TinyDDSResultToString(loadResult), path.string());
}
size_t totalBytes{ 0 };
std::vector<uint32_t> mipOff(file.GetMipCount());
for (size_t i{0}; i < file.GetMipCount(); ++i)
{
mipOff[i] = static_cast<uint32_t>(totalBytes);
totalBytes += file.GetImageData(static_cast<uint32_t>(i), 0)->m_memSlicePitch;
}
SHTexture::PixelChannel* pixel = new SHTexture::PixelChannel[totalBytes];
std::memcpy(pixel, file.GetImageData()->m_mem, totalBytes);
//pixel = std::move(reinterpret_cast<SHTexture::PixelChannel const*>(file.GetDDSData()));
asset.name = path.stem().string();
asset.compiled = false;
asset.numBytes = static_cast<uint32_t>(totalBytes);
asset.width = file.GetWidth();
asset.height = file.GetHeight();
asset.format = ddsLoaderToVkFormat(file.GetFormat(), true);
asset.mipOffsets = std::move(mipOff);
asset.pixelData = std::move(pixel);
}
void SHTextureLoader::LoadSHTexture(AssetPath path, SHTextureAsset& asset) noexcept
{
std::ifstream file{path.string(), std::ios::in | std::ios::binary};
if (!file.is_open())
{
SHLOG_ERROR("Error opening SHTexture file: {}", path.string());
}
auto const intBytes{ sizeof(uint32_t) };
uint32_t mipCount;
file.read(reinterpret_cast<char*>(&asset.numBytes), intBytes);
file.read(reinterpret_cast<char*>(&asset.width), intBytes);
file.read(reinterpret_cast<char*>(&asset.height), intBytes);
file.read(reinterpret_cast<char*>(&asset.format), sizeof(SHTexture::TextureFormat));
file.read(reinterpret_cast<char*>(&mipCount), intBytes);
std::vector<uint32_t> mips(mipCount);
file.read(reinterpret_cast<char*>(mips.data()), intBytes * mipCount);
auto pixel = new SHTexture::PixelChannel[asset.numBytes];
file.read(reinterpret_cast<char*>(pixel), asset.numBytes);
asset.mipOffsets = std::move(mips);
asset.pixelData = std::move( pixel );
asset.compiled = true;
file.close();
}
SHAssetData* SHTextureLoader::Load(AssetPath path)
{
auto result = new SHTextureAsset();
LoadImageAsset(path, *result);
return result;
}
void SHTextureLoader::LoadImageAsset(AssetPath path, SHTextureAsset& asset)
{
if (path.extension().string() == DDS_EXTENSION)
{
LoadTinyDDS(path, asset);
}
else if (path.extension().string() == TEXTURE_EXTENSION)
{
LoadSHTexture(path, asset);
}
}
}

View File

@ -1,34 +0,0 @@
/*************************************************************************//**
* \file SHTextureLoader.h
* \author Loh Xiao Qi
* \date 30 September 2022
* \brief Library to load dds textures and custom binary format
*
*
* 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
#define TINYDDSLOADER_IMPLEMENTATION
#include "../SHAssetMacros.h"
#include "../Asset Types/SHTextureAsset.h"
#include "tinyddsloader.h"
#include "SHAssetLoader.h"
namespace SHADE
{
class SHTextureLoader : public SHAssetLoader
{
private:
std::string TinyDDSResultToString(tinyddsloader::Result value);
vk::Format ddsLoaderToVkFormat(tinyddsloader::DDSFile::DXGIFormat format, bool isLinear);
void LoadTinyDDS(AssetPath path, SHTextureAsset& asset) noexcept;
public:
void LoadImageAsset(AssetPath paths, SHTextureAsset& image);
void LoadSHTexture(AssetPath path, SHTextureAsset& asset) noexcept;
SHAssetData* Load(AssetPath path) override;
};
}

View File

@ -42,20 +42,14 @@ constexpr std::string_view ASSET_META_VER { "1.0" };
// Asset type enum // Asset type enum
enum class AssetType : AssetTypeMeta enum class AssetType : AssetTypeMeta
{ {
INVALID = 0, INVALID,
AUDIO = 1,
SHADER, SHADER,
MATERIAL, SHADER_BUILT_IN,
IMAGE, TEXTURE,
TEXTURE, MESH,
MESH,
SCRIPT,
SCENE,
PREFAB,
AUDIO_WAV,
DDS,
MAX_COUNT MAX_COUNT
}; };
constexpr size_t TYPE_COUNT{ static_cast<size_t>(AssetType::MAX_COUNT) };
//Directory //Directory
#ifdef _PUBLISH #ifdef _PUBLISH
@ -67,37 +61,55 @@ constexpr std::string_view ASSET_ROOT {"../../Assets"};
// ASSET EXTENSIONS // ASSET EXTENSIONS
constexpr std::string_view META_EXTENSION {".shmeta"}; constexpr std::string_view META_EXTENSION {".shmeta"};
constexpr std::string_view IMAGE_EXTENSION {".png"};
constexpr std::string_view AUDIO_EXTENSION {".ogg"}; constexpr std::string_view AUDIO_EXTENSION {".ogg"};
constexpr std::string_view AUDIO_WAV_EXTENSION {".wav"}; constexpr std::string_view AUDIO_WAV_EXTENSION {".wav"};
constexpr std::string_view SHADER_EXTENSION {".glsl"}; constexpr std::string_view SHADER_EXTENSION{ ".shshader" };
constexpr std::string_view SHADER_BUILT_IN_EXTENSION{".shshaderb"};
constexpr std::string_view SCRIPT_EXTENSION {".cs"}; constexpr std::string_view SCRIPT_EXTENSION {".cs"};
constexpr std::string_view SCENE_EXTENSION {".SHADE"}; constexpr std::string_view SCENE_EXTENSION {".shade"};
constexpr std::string_view PREFAB_EXTENSION {".SHPrefab"}; constexpr std::string_view PREFAB_EXTENSION {".shprefab"};
constexpr std::string_view MATERIAL_EXTENSION {".SHMat"}; constexpr std::string_view MATERIAL_EXTENSION {".shmat"};
constexpr std::string_view TEXTURE_EXTENSION {".shtex"}; constexpr std::string_view TEXTURE_EXTENSION {".shtex"};
constexpr std::string_view DDS_EXTENSION {".dds"};
constexpr std::string_view FBX_EXTENSION {".fbx"};
constexpr std::string_view GLTF_EXTENSION {".gltf"};
constexpr std::string_view MESH_EXTENSION {".shmesh"}; constexpr std::string_view MESH_EXTENSION {".shmesh"};
constexpr std::string_view EXTENSIONS[] = { constexpr std::string_view EXTENSIONS[] = {
AUDIO_EXTENSION, AUDIO_EXTENSION,
SHADER_EXTENSION, SHADER_EXTENSION,
SHADER_BUILT_IN_EXTENSION,
MATERIAL_EXTENSION, MATERIAL_EXTENSION,
IMAGE_EXTENSION,
TEXTURE_EXTENSION, TEXTURE_EXTENSION,
DDS_EXTENSION,
MESH_EXTENSION, MESH_EXTENSION,
SCRIPT_EXTENSION, SCRIPT_EXTENSION,
SCENE_EXTENSION, SCENE_EXTENSION,
PREFAB_EXTENSION, PREFAB_EXTENSION,
AUDIO_WAV_EXTENSION, AUDIO_WAV_EXTENSION,
};
// EXTERNAL EXTENSIONS
constexpr std::string_view GLSL_EXTENSION{ ".glsl" };
constexpr std::string_view DDS_EXTENSION{ ".dds" };
constexpr std::string_view FBX_EXTENSION{ ".fbx" };
constexpr std::string_view GLTF_EXTENSION{ ".gltf" };
constexpr std::string_view EXTERNALS[] = {
GLSL_EXTENSION,
DDS_EXTENSION,
FBX_EXTENSION, FBX_EXTENSION,
GLTF_EXTENSION GLTF_EXTENSION
}; };
constexpr size_t TYPE_COUNT {static_cast<size_t>(AssetType::MAX_COUNT) }; // SHADER IDENTIFIERS
constexpr std::string_view VERTEX_SHADER{ "_VS" };
constexpr std::string_view FRAGMENT_SHADER{ "_FS" };
constexpr std::string_view COMPUTER_SHADER{ "_CS" };
constexpr std::string_view SHADER_IDENTIFIERS[] = {
VERTEX_SHADER,
FRAGMENT_SHADER,
COMPUTER_SHADER
};
constexpr size_t SHADER_TYPE_MAX_COUNT{ 3 };
// Error flags // Error flags
constexpr std::string_view FILE_NOT_FOUND_ERR {"FILE NOT FOUND"}; constexpr std::string_view FILE_NOT_FOUND_ERR {"FILE NOT FOUND"};

View File

@ -14,12 +14,13 @@
#include "SHAssetMetaHandler.h" #include "SHAssetMetaHandler.h"
#include "Filesystem/SHFileSystem.h" #include "Filesystem/SHFileSystem.h"
#include "Libraries/SHAssimpLibrary.h" #include "Libraries/Loaders/SHMeshLoader.h"
#include "Libraries/SHMeshLoader.h" #include "Libraries/Loaders/SHTextureLoader.h"
#include "Libraries/SHTextureLoader.h" #include "Libraries/Loaders/SHShaderSourceLoader.h"
#include "Libraries/SHMeshCompiler.h" #include "Libraries/Compilers/SHMeshCompiler.h"
#include "Libraries/SHTextureCompiler.h" #include "Libraries/Compilers/SHTextureCompiler.h"
#include "Libraries/Compilers/SHShaderSourceCompiler.h"
namespace SHADE namespace SHADE
{ {
@ -44,7 +45,13 @@ namespace SHADE
result |= unique; result |= unique;
while (result == 0) while (result == 0 ||
std::ranges::any_of(
assetCollection.begin(),
assetCollection.end(),
[result](SHAsset const& asset) { return asset.id == result; }
)
)
{ {
result = GenerateAssetID(type); result = GenerateAssetID(type);
} }
@ -64,7 +71,20 @@ namespace SHADE
// TODO // TODO
} }
AssetPath SHAssetManager::GenerateLocalPath(AssetPath path) noexcept void SHAssetManager::Exit() noexcept
{
for (auto const& loader : loaders)
{
delete loader;
}
for (auto const& data : assetData)
{
delete data.second;
}
}
AssetPath SHAssetManager::GenerateLocalPath(AssetPath path) noexcept
{ {
if (!IsRecognised(path.extension().string().c_str())) if (!IsRecognised(path.extension().string().c_str()))
{ {
@ -91,17 +111,10 @@ namespace SHADE
switch(type) switch(type)
{ {
case AssetType::SCENE: case AssetType::SHADER:
folder = "scenes/"; case AssetType::SHADER_BUILT_IN:
break; folder = "Shaders/";
break;
case AssetType::PREFAB:
folder = "prefabs/";
break;
case AssetType::MATERIAL:
folder = "materials/";
break;
default: default:
folder = "/"; folder = "/";
@ -206,7 +219,56 @@ namespace SHADE
} }
bool SHAssetManager::IsRecognised(char const* ext) noexcept std::vector<SHAssetData const*> SHAssetManager::GetAllDataOfType(AssetType type) noexcept
{
auto const toRetrieve = GetAllRecordOfType(type);
std::vector<SHAssetData const*> result;
result.reserve(toRetrieve.size());
for (auto const& get : toRetrieve)
{
result.push_back(LoadData(get));
}
return result;
}
std::vector<SHAsset> SHAssetManager::GetAllRecordOfType(AssetType type) noexcept
{
std::vector<SHAsset> result;
for (auto const& asset : assetCollection)
{
if (asset.type == type)
{
result.push_back(asset);
}
}
return result;
}
AssetID SHAssetManager::CompileAsset(AssetPath path) noexcept
{
SHAsset newAsset
{
.name = path.stem().string(),
.location = 0
};
auto const ext{ path.extension().string() };
if (ext == GLSL_EXTENSION.data())
{
newAsset.path = SHShaderSourceCompiler::LoadAndCompileShader(path).value();
newAsset.id = GenerateAssetID(AssetType::SHADER_BUILT_IN);
newAsset.type = AssetType::SHADER_BUILT_IN;
}
assetCollection.push_back(newAsset);
SHAssetMetaHandler::WriteMetaData(newAsset);
return newAsset.id;
}
bool SHAssetManager::IsRecognised(char const* ext) noexcept
{ {
for (auto const& e : EXTENSIONS) for (auto const& e : EXTENSIONS)
{ {
@ -227,23 +289,82 @@ namespace SHADE
result.type = SHAssetMetaHandler::GetTypeFromExtension(path.extension().string()); result.type = SHAssetMetaHandler::GetTypeFromExtension(path.extension().string());
result.id = GenerateAssetID(result.type); result.id = GenerateAssetID(result.type);
result.path = path; result.path = path;
result.location = 0;
return result; return result;
} }
void SHAssetManager::InitLoaders() noexcept void SHAssetManager::CompileAll() noexcept
{
std::vector<AssetPath> paths;
for (auto const& dir : std::filesystem::recursive_directory_iterator{ ASSET_ROOT })
{
if (dir.is_regular_file())
{
for (auto const& ext : EXTERNALS)
{
if (dir.path().extension().string() == ext.data())
{
paths.push_back(dir.path());
}
}
}
}
for (auto const& path : paths)
{
SHAsset newAsset
{
.name = path.stem().string(),
.location = 0
};
auto const ext{ path.extension().string() };
if (ext == GLSL_EXTENSION.data())
{
newAsset.path = SHShaderSourceCompiler::LoadAndCompileShader(path).value();
newAsset.id = GenerateAssetID(AssetType::SHADER_BUILT_IN);
newAsset.type = AssetType::SHADER_BUILT_IN;
}
else if (ext == DDS_EXTENSION.data())
{
newAsset.path = SHTextureCompiler::CompileTextureAsset(path).value();
newAsset.id = GenerateAssetID(AssetType::TEXTURE);
newAsset.type = AssetType::TEXTURE;
}
else if (ext == GLTF_EXTENSION.data() || ext == FBX_EXTENSION.data())
{
std::vector<SHMeshAsset*> meshes;
std::vector<SHAnimationAsset*> anims;
SHMeshCompiler::LoadFromFile(path, meshes, anims);
for (auto const& mesh : meshes)
{
SHAsset meshAsset{
.name = mesh->header.name,
.location = 0
};
meshAsset.path = SHMeshCompiler::CompileMeshBinary(*mesh, path).value();
meshAsset.id = GenerateAssetID(AssetType::MESH);
meshAsset.type = AssetType::MESH;
assetCollection.push_back(meshAsset);
SHAssetMetaHandler::WriteMetaData(meshAsset);
}
continue;
}
assetCollection.push_back(newAsset);
SHAssetMetaHandler::WriteMetaData(newAsset);
}
}
void SHAssetManager::InitLoaders() noexcept
{ {
loaders[static_cast<size_t>(AssetType::AUDIO)] = nullptr; loaders[static_cast<size_t>(AssetType::SHADER)] = dynamic_cast<SHAssetLoader*>(new SHShaderSourceLoader());
loaders[static_cast<size_t>(AssetType::SHADER)] = nullptr; loaders[static_cast<size_t>(AssetType::SHADER_BUILT_IN)] = loaders[static_cast<size_t>(AssetType::SHADER)];
loaders[static_cast<size_t>(AssetType::MATERIAL)] = nullptr; loaders[static_cast<size_t>(AssetType::TEXTURE)] = dynamic_cast<SHAssetLoader*>(new SHTextureLoader());
loaders[static_cast<size_t>(AssetType::IMAGE)] = dynamic_cast<SHAssetLoader*>(new SHTextureLoader()); loaders[static_cast<size_t>(AssetType::MESH)] = dynamic_cast<SHAssetLoader*>(new SHMeshLoader());
loaders[static_cast<size_t>(AssetType::TEXTURE)] = nullptr;
loaders[static_cast<size_t>(AssetType::MESH)] = dynamic_cast<SHAssetLoader*>(new SHMeshLoader());
loaders[static_cast<size_t>(AssetType::SCRIPT)] = nullptr;
loaders[static_cast<size_t>(AssetType::SCENE)] = nullptr;
loaders[static_cast<size_t>(AssetType::PREFAB)] = nullptr;
loaders[static_cast<size_t>(AssetType::AUDIO_WAV)] = nullptr;
loaders[static_cast<size_t>(AssetType::DDS)] = nullptr;
} }
/**************************************************************************** /****************************************************************************
@ -251,6 +372,7 @@ namespace SHADE
****************************************************************************/ ****************************************************************************/
void SHAssetManager::Load() noexcept void SHAssetManager::Load() noexcept
{ {
//CompileAll();
InitLoaders(); InitLoaders();
BuildAssetCollection(); BuildAssetCollection();
//LoadAllData(); //LoadAllData();

View File

@ -12,7 +12,7 @@
#include "tinyddsloader.h" #include "tinyddsloader.h"
#include "SHAsset.h" #include "SHAsset.h"
#include "Asset Types/SHAssetData.h" #include "Asset Types/SHAssetData.h"
#include "Libraries/SHAssetLoader.h" #include "Assets/Libraries/Loaders/SHAssetLoader.h"
#include <memory> #include <memory>
#include "SH_API.h" #include "SH_API.h"
@ -37,6 +37,8 @@ namespace SHADE
static void Unload() noexcept; static void Unload() noexcept;
static void Unload(AssetID assetId) noexcept; static void Unload(AssetID assetId) noexcept;
static void Exit() noexcept;
/**************************************************************************** /****************************************************************************
* \brief Load all resources that are in the folder * \brief Load all resources that are in the folder
****************************************************************************/ ****************************************************************************/
@ -77,21 +79,24 @@ namespace SHADE
template<typename T> template<typename T>
static std::enable_if_t<std::is_base_of_v<SHAssetData, T>, T const* const> GetData(AssetID id) noexcept; static std::enable_if_t<std::is_base_of_v<SHAssetData, T>, T const* const> GetData(AssetID id) noexcept;
static std::vector<SHAssetData const*> GetAllDataOfType(AssetType type) noexcept;
static std::vector<SHAsset> GetAllRecordOfType(AssetType type) noexcept;
static AssetID CompileAsset(AssetPath path) noexcept;
private: private:
/****************************************************************************
* \brief Load resource data into memory static void InitLoaders() noexcept;
****************************************************************************/
static void LoadAllData() noexcept; static void LoadAllData() noexcept;
static SHAssetData* LoadData(SHAsset const& asset) noexcept; static SHAssetData* LoadData(SHAsset const& asset) noexcept;
inline static void BuildAssetCollection() noexcept; inline static void BuildAssetCollection() noexcept;
static bool IsRecognised(char const*) noexcept; static bool IsRecognised(char const*) noexcept;
static SHAsset CreateAssetFromPath(AssetPath path) noexcept; static SHAsset CreateAssetFromPath(AssetPath path) noexcept;
static void InitLoaders() noexcept; static void CompileAll() noexcept;
static FMOD::System* audioSystem; static FMOD::System* audioSystem;
static std::unordered_map<AssetID,SHSound>* audioSoundList; static std::unordered_map<AssetID,SHSound>* audioSoundList;

View File

@ -4,6 +4,7 @@
#include "SHCameraSystem.h" #include "SHCameraSystem.h"
#include "ECS_Base/Managers/SHSystemManager.h" #include "ECS_Base/Managers/SHSystemManager.h"
#include "Math/Transform/SHTransformComponent.h" #include "Math/Transform/SHTransformComponent.h"
#include "Math/SHMath.h"
namespace SHADE namespace SHADE
{ {
@ -28,7 +29,7 @@ namespace SHADE
{ {
auto transform = SHComponentManager::GetComponent<SHTransformComponent>(GetEID()); auto transform = SHComponentManager::GetComponent<SHTransformComponent>(GetEID());
SHVec3 rotation = transform->GetWorldRotation(); SHVec3 rotation = transform->GetWorldRotation();
transform->SetWorldRotation(SHVec3{rotation.x,yaw, rotation.z}); transform->SetWorldRotation(SHVec3{rotation.x,SHMath::DegreesToRadians(yaw), rotation.z});
} }
dirtyView = true; dirtyView = true;
} }
@ -40,7 +41,7 @@ namespace SHADE
{ {
auto transform = SHComponentManager::GetComponent<SHTransformComponent>(GetEID()); auto transform = SHComponentManager::GetComponent<SHTransformComponent>(GetEID());
SHVec3 rotation = transform->GetWorldRotation(); SHVec3 rotation = transform->GetWorldRotation();
transform->SetWorldRotation(SHVec3{ pitch,rotation.y, rotation.z }); transform->SetWorldRotation(SHVec3{ SHMath::DegreesToRadians(pitch),rotation.y, rotation.z });
} }
dirtyView = true; dirtyView = true;
} }
@ -52,7 +53,7 @@ namespace SHADE
{ {
auto transform = SHComponentManager::GetComponent<SHTransformComponent>(GetEID()); auto transform = SHComponentManager::GetComponent<SHTransformComponent>(GetEID());
SHVec3 rotation = transform->GetWorldRotation(); SHVec3 rotation = transform->GetWorldRotation();
transform->SetWorldRotation(SHVec3{ rotation.x,rotation.y, roll}); transform->SetWorldRotation(SHVec3{ rotation.x,rotation.y, SHMath::DegreesToRadians(roll)});
} }
dirtyView = true; dirtyView = true;
} }
@ -102,7 +103,7 @@ namespace SHADE
} }
dirtyView = true; dirtyView = true;
} }
void SHCameraComponent::SetPosition(SHVec3& pos) noexcept void SHCameraComponent::SetPosition(SHVec3 pos) noexcept
{ {
this->position = pos; this->position = pos;
if (SHComponentManager::HasComponent<SHTransformComponent>(GetEID())) if (SHComponentManager::HasComponent<SHTransformComponent>(GetEID()))
@ -145,6 +146,17 @@ namespace SHADE
dirtyProj = true; dirtyProj = true;
} }
void SHCameraComponent::SetIsPerspective(bool persp) noexcept
{
this->perspProj = persp;
dirtyProj = true;
}
SHVec3 SHCameraComponent::GetPosition() const noexcept
{
return position;
}
float SHCameraComponent::GetYaw() const noexcept float SHCameraComponent::GetYaw() const noexcept
{ {
return yaw; return yaw;
@ -158,6 +170,27 @@ namespace SHADE
{ {
return roll; return roll;
} }
float SHCameraComponent::GetWidth() const noexcept
{
return width;
}
float SHCameraComponent::GetHeight() const noexcept
{
return height;
}
float SHCameraComponent::GetNear() const noexcept
{
return zNear;
}
float SHCameraComponent::GetFar() const noexcept
{
return zFar;
}
float SHCameraComponent::GetAspectRatio() const noexcept float SHCameraComponent::GetAspectRatio() const noexcept
{ {
return width/height; return width/height;
@ -168,6 +201,11 @@ namespace SHADE
return fov; return fov;
} }
bool SHCameraComponent::GetIsPerspective() const noexcept
{
return perspProj;
}
const SHMatrix& SHCameraComponent::GetViewMatrix() const noexcept const SHMatrix& SHCameraComponent::GetViewMatrix() const noexcept
{ {
return viewMatrix; return viewMatrix;
@ -178,12 +216,32 @@ namespace SHADE
return projMatrix; return projMatrix;
} }
void SHCameraComponent::SetMainCamera(size_t directorCameraIndex) noexcept //void SHCameraComponent::SetMainCamera(size_t directorCameraIndex) noexcept
{ //{
auto system = SHSystemManager::GetSystem<SHCameraSystem>(); // auto system = SHSystemManager::GetSystem<SHCameraSystem>();
system->GetDirector(directorCameraIndex)->SetMainCamera(*this); // system->GetDirector(directorCameraIndex)->SetMainCamera(*this);
} //}
}//namespace SHADE
RTTR_REGISTRATION
{
using namespace SHADE;
using namespace rttr;
registration::class_<SHCameraComponent>("Camera Component")
.property("Position", &SHCameraComponent::GetPosition, select_overload<void(SHVec3)>(&SHCameraComponent::SetPosition))
.property("Pitch", &SHCameraComponent::GetPitch, &SHCameraComponent::SetPitch)
.property("Yaw", &SHCameraComponent::GetYaw, &SHCameraComponent::SetYaw)
.property("Roll", &SHCameraComponent::GetRoll, &SHCameraComponent::SetRoll)
.property("Width", &SHCameraComponent::GetWidth, &SHCameraComponent::SetWidth)
.property("Height", &SHCameraComponent::GetHeight, &SHCameraComponent::SetHeight)
.property("Near", &SHCameraComponent::GetNear, &SHCameraComponent::SetNear)
.property("Far", &SHCameraComponent::GetFar, &SHCameraComponent::SetFar)
.property("Perspective", &SHCameraComponent::GetIsPerspective, &SHCameraComponent::SetIsPerspective);
} }

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <rttr/registration>
#include "ECS_Base/Components/SHComponent.h" #include "ECS_Base/Components/SHComponent.h"
#include "Math/Vector/SHVec3.h" #include "Math/Vector/SHVec3.h"
#include "Math/SHMatrix.h" #include "Math/SHMatrix.h"
@ -50,37 +52,44 @@ namespace SHADE
void SetPositionY(float y) noexcept; void SetPositionY(float y) noexcept;
void SetPositionZ(float z) noexcept; void SetPositionZ(float z) noexcept;
void SetPosition(float x, float y, float z) noexcept; void SetPosition(float x, float y, float z) noexcept;
void SetPosition(SHVec3& pos) noexcept; void SetPosition(SHVec3 pos) noexcept;
void SetWidth(float width) noexcept; void SetWidth(float width) noexcept;
void SetHeight(float height) noexcept; void SetHeight(float height) noexcept;
void SetNear(float znear) noexcept; void SetNear(float znear) noexcept;
void SetFar(float zfar) noexcept; void SetFar(float zfar) noexcept;
void SetFOV(float fov) noexcept; void SetFOV(float fov) noexcept;
void SetIsPerspective(bool persp) noexcept;
SHVec3 GetPosition() const noexcept;
float GetYaw() const noexcept; float GetYaw() const noexcept;
float GetPitch() const noexcept; float GetPitch() const noexcept;
float GetRoll() const noexcept; float GetRoll() const noexcept;
float GetWidth() const noexcept;
float GetHeight() const noexcept;
float GetNear() const noexcept;
float GetFar() const noexcept;
float GetAspectRatio() const noexcept; float GetAspectRatio() const noexcept;
float GetFOV() const noexcept; float GetFOV() const noexcept;
bool GetIsPerspective() const noexcept;
const SHMatrix& GetViewMatrix() const noexcept; const SHMatrix& GetViewMatrix() const noexcept;
const SHMatrix& GetProjMatrix() const noexcept; const SHMatrix& GetProjMatrix() const noexcept;
void SetMainCamera(size_t cameraDirectorIndex = 0) noexcept; //void SetMainCamera(size_t cameraDirectorIndex = 0) noexcept;
float movementSpeed; float movementSpeed;
SHVec3 turnSpeed; SHVec3 turnSpeed;
RTTR_ENABLE()
protected: protected:
}; };

View File

@ -154,7 +154,7 @@ namespace SHADE
SHVec3 view, right, UP; SHVec3 view, right, UP;
//ClampCameraRotation(camera); ClampCameraRotation(camera);
GetCameraAxis(camera, view, right, UP); GetCameraAxis(camera, view, right, UP);
@ -221,8 +221,8 @@ namespace SHADE
SHVec3 up = { 0.0f,1.0f,0.0f }; SHVec3 up = { 0.0f,1.0f,0.0f };
target = SHVec3::RotateX(target, SHMath::DegreesToRadians(camera.pitch));
target = SHVec3::RotateY(target, SHMath::DegreesToRadians(camera.yaw)); target = SHVec3::RotateY(target, SHMath::DegreesToRadians(camera.yaw));
target =SHVec3::RotateX(target, SHMath::DegreesToRadians(camera.pitch));
target += camera.position; target += camera.position;
////SHVec3::RotateZ(target, SHMath::DegreesToRadians(camera.roll)); ////SHVec3::RotateZ(target, SHMath::DegreesToRadians(camera.roll));

View File

@ -0,0 +1,84 @@
#include "SHpch.h"
#include "SHAssetBrowser.h"
#include "Editor/IconsMaterialDesign.h"
#include "Editor/SHImGuiHelpers.hpp"
#include <imgui.h>
#include "Assets/SHAssetManager.h"
#include "Editor/DragDrop/SHDragDrop.hpp"
namespace SHADE
{
SHAssetBrowser::SHAssetBrowser()
:SHEditorWindow("\xee\x8b\x87 Asset Browser", ImGuiWindowFlags_MenuBar)
{
}
void SHAssetBrowser::Init()
{
SHEditorWindow::Init();
}
void SHAssetBrowser::Update()
{
SHEditorWindow::Update();
if(Begin())
{
DrawMenuBar();
auto const& assets = SHAssetManager::GetAllAssets();
if(ImGui::BeginTable("AssetBrowserTable", 3))
{
ImGui::TableNextColumn();
ImGui::TableHeader("Asset ID");
ImGui::TableNextColumn();
ImGui::TableHeader("Name");
ImGui::TableNextColumn();
ImGui::TableHeader("Type");
for(SHAsset const& asset : assets)
{
DrawAsset(asset);
}
ImGui::EndTable();
}
}
ImGui::End();
}
void SHAssetBrowser::DrawMenuBar()
{
if(ImGui::BeginMenuBar())
{
ImGui::EndMenuBar();
}
}
void SHAssetBrowser::DrawAsset(SHAsset const& asset)
{
ImGui::PushID(asset.id);
ImGui::BeginGroup();
ImGui::TableNextColumn();
ImGui::Selectable(std::format("{}", asset.id).data(), false, ImGuiSelectableFlags_SpanAllColumns);
if(SHDragDrop::BeginSource())
{
auto id = asset.id;
ImGui::Text("Moving Asset: %zu", id);
SHDragDrop::SetPayload<AssetID>(DRAG_RESOURCE, &id);
SHDragDrop::EndSource();
}
ImGui::TableNextColumn();
ImGui::Text("%s", asset.name.c_str());
ImGui::TableNextColumn();
ImGui::Text("%s", "Type");
ImGui::EndGroup();
ImGui::PopID();
}
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "Assets/SHAsset.h"
#include "Editor/EditorWindow/SHEditorWindow.h"
namespace SHADE
{
class SHAssetBrowser final : public SHEditorWindow
{
public:
SHAssetBrowser();
void Init();
void Update();
void Refresh();
private:
void DrawMenuBar();
void DrawAsset(SHAsset const& asset);
float idColumnWidth, nameColumnWidth, typeColumnWidth;
};
}

View File

@ -93,8 +93,7 @@ namespace SHADE
{ {
if (ImGui::BeginMenuBar()) if (ImGui::BeginMenuBar())
{ {
ImGui::SetCursorPosX(ImGui::GetContentRegionAvail().x * 0.75f);
ImGui::SetCursorPosX(ImGui::GetContentRegionAvail().x - 35.0f);
if(ImGui::SmallButton(ICON_MD_DESELECT)) if(ImGui::SmallButton(ICON_MD_DESELECT))
{ {
auto editor = SHSystemManager::GetSystem<SHEditor>(); auto editor = SHSystemManager::GetSystem<SHEditor>();

View File

@ -39,6 +39,10 @@ namespace SHADE
{ {
SHComponentManager::RemoveComponent<T>(component->GetEID()); SHComponentManager::RemoveComponent<T>(component->GetEID());
} }
if (ImGui::Selectable(std::format("{} Reset {}", ICON_MD_RESTART_ALT, componentName.data()).data()))
{
*component = T();
}
ImGui::EndPopup(); ImGui::EndPopup();
} }
} }
@ -48,7 +52,7 @@ namespace SHADE
if (!component) if (!component)
return; return;
const auto componentType = rttr::type::get(*component); const auto componentType = rttr::type::get(*component);
SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }); SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }, "Is Component Active");
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::CollapsingHeader(componentType.get_name().data())) if (ImGui::CollapsingHeader(componentType.get_name().data()))
{ {
@ -89,7 +93,7 @@ namespace SHADE
auto metaMax = property.get_metadata(META::max); auto metaMax = property.get_metadata(META::max);
if (metaMin && metaMax) if (metaMin && metaMax)
{ {
SHEditorWidgets::SliderInt(property.get_name().data(), metaMin.template get_value<int>(), metaMin.template get_value<int>(), [component, property] {return property.get_value(component).to_int(); }, [component, property](int const& result) {property.set_value(component, result); }); SHEditorWidgets::SliderInt(property.get_name().data(), metaMin.template get_value<int>(), metaMax.template get_value<int>(), [component, property] {return property.get_value(component).to_int(); }, [component, property](int const& result) {property.set_value(component, result); });
} }
else else
{ {
@ -115,7 +119,7 @@ namespace SHADE
auto metaMax = property.get_metadata(META::max); auto metaMax = property.get_metadata(META::max);
if (metaMin.is_valid() && metaMax.is_valid()) if (metaMin.is_valid() && metaMax.is_valid())
{ {
SHEditorWidgets::SliderScalar<uint16_t>(property.get_name().data(), ImGuiDataType_U16, metaMin.template get_value<uint16_t>(), metaMin.template get_value<uint16_t>(), [component, property] {return property.get_value(component).to_uint16(); }, [component, property](uint16_t const& result) {property.set_value(component, result); }, "%zu"); SHEditorWidgets::SliderScalar<uint16_t>(property.get_name().data(), ImGuiDataType_U16, metaMin.template get_value<uint16_t>(), metaMax.template get_value<uint16_t>(), [component, property] {return property.get_value(component).to_uint16(); }, [component, property](uint16_t const& result) {property.set_value(component, result); }, "%zu");
} }
else else
{ {
@ -128,7 +132,7 @@ namespace SHADE
auto metaMax = property.get_metadata(META::max); auto metaMax = property.get_metadata(META::max);
if (metaMin.is_valid() && metaMax.is_valid()) if (metaMin.is_valid() && metaMax.is_valid())
{ {
SHEditorWidgets::SliderScalar<uint32_t>(property.get_name().data(), ImGuiDataType_U32, metaMin.template get_value<uint32_t>(), metaMin.template get_value<uint32_t>(), [component, property] { return property.get_value(component).to_uint32(); }, [component, property](uint32_t const& result) {property.set_value(component, result); }, "%zu"); SHEditorWidgets::SliderScalar<uint32_t>(property.get_name().data(), ImGuiDataType_U32, metaMin.template get_value<uint32_t>(), metaMax.template get_value<uint32_t>(), [component, property] { return property.get_value(component).to_uint32(); }, [component, property](uint32_t const& result) {property.set_value(component, result); }, "%zu");
} }
else else
{ {
@ -141,7 +145,7 @@ namespace SHADE
auto metaMax = property.get_metadata(META::max); auto metaMax = property.get_metadata(META::max);
if (metaMin.is_valid() && metaMax.is_valid()) if (metaMin.is_valid() && metaMax.is_valid())
{ {
SHEditorWidgets::SliderScalar<uint64_t>(property.get_name().data(), ImGuiDataType_U64, metaMin.template get_value<uint64_t>(), metaMin.template get_value<uint64_t>(), [component, property] {return property.get_value(component).to_uint64(); }, [component, property](uint64_t const& result) {property.set_value(component, result); }, "%zu"); SHEditorWidgets::SliderScalar<uint64_t>(property.get_name().data(), ImGuiDataType_U64, metaMin.template get_value<uint64_t>(), metaMax.template get_value<uint64_t>(), [component, property] {return property.get_value(component).to_uint64(); }, [component, property](uint64_t const& result) {property.set_value(component, result); }, "%zu");
} }
else else
{ {
@ -152,13 +156,18 @@ namespace SHADE
{ {
auto metaMin = property.get_metadata(META::min); auto metaMin = property.get_metadata(META::min);
auto metaMax = property.get_metadata(META::max); auto metaMax = property.get_metadata(META::max);
float min{}, max{};
if(metaMin.is_valid())
min = std::max(metaMin.template get_value<float>(), -FLT_MAX * 0.5f);
if(metaMax.is_valid())
max = std::min(metaMax.template get_value<float>(), FLT_MAX * 0.5f);
if (metaMin.is_valid() && metaMax.is_valid()) if (metaMin.is_valid() && metaMax.is_valid())
{ {
SHEditorWidgets::SliderFloat(property.get_name().data(), metaMin.template get_value<float>(), metaMin.template get_value<float>(), [component, property] {return property.get_value(component).to_float(); }, [component, property](float const& result) {property.set_value(component, result); }); SHEditorWidgets::SliderFloat(property.get_name().data(), min, max, [component, property] {return property.get_value(component).to_float(); }, [component, property](float const& result) {property.set_value(component, result); });
} }
else else
{ {
SHEditorWidgets::DragFloat(property.get_name().data(), [component, property] {return property.get_value(component).to_float(); }, [component, property](float const& result) {property.set_value(component, result); }); SHEditorWidgets::DragFloat(property.get_name().data(), [component, property] {return property.get_value(component).to_float(); }, [component, property](float const& result) {property.set_value(component, result); }, "Test");
} }
} }
else if (type == rttr::type::get<double>()) else if (type == rttr::type::get<double>())
@ -167,7 +176,7 @@ namespace SHADE
auto metaMax = property.get_metadata(META::max); auto metaMax = property.get_metadata(META::max);
if (metaMin.is_valid() && metaMax.is_valid()) if (metaMin.is_valid() && metaMax.is_valid())
{ {
SHEditorWidgets::SliderScalar<double>(property.get_name().data(), ImGuiDataType_Double, metaMin.template get_value<double>(), metaMin.template get_value<double>(), [component, property] {return property.get_value(component).to_double(); }, [component, property](double const& result) {property.set_value(component, result); }); SHEditorWidgets::SliderScalar<double>(property.get_name().data(), ImGuiDataType_Double, metaMin.template get_value<double>(), metaMax.template get_value<double>(), [component, property] {return property.get_value(component).to_double(); }, [component, property](double const& result) {property.set_value(component, result); });
} }
else else
{ {
@ -198,6 +207,10 @@ namespace SHADE
{ {
if (!component) if (!component)
return; return;
// Get transform component for extrapolating relative sizes
auto* transformComponent = SHComponentManager::GetComponent<SHTransformComponent>(component->GetEID());
const auto componentType = rttr::type::get(*component); const auto componentType = rttr::type::get(*component);
SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }); SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; });
ImGui::SameLine(); ImGui::SameLine();
@ -212,28 +225,41 @@ namespace SHADE
for (int i{}; i < size; ++i) for (int i{}; i < size; ++i)
{ {
ImGui::PushID(i); ImGui::PushID(i);
SHCollider& collider = component->GetCollider(i); SHCollider* collider = &component->GetCollider(i);
auto cursorPos = ImGui::GetCursorPos(); auto cursorPos = ImGui::GetCursorPos();
if (collider.GetType() == SHCollider::Type::BOX) if (collider->GetType() == SHCollider::Type::BOX)
{ {
SHEditorWidgets::BeginPanel( std::format("{} Box Collider #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); SHEditorWidgets::BeginPanel( std::format("{} Box Collider #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y });
auto box = reinterpret_cast<SHBoundingBox*>(collider.GetShape()); auto box = reinterpret_cast<SHBoundingBox*>(collider->GetShape());
SHEditorWidgets::DragVec3("Half Extents", { "X", "Y", "Z" }, [box] {return box->GetHalfExtents(); }, [box](SHVec3 const& vec) {box->SetHalfExtents(vec);}); SHEditorWidgets::DragVec3
(
"Half Extents", { "X", "Y", "Z" },
[box, transformComponent] { return (transformComponent->GetWorldScale() * 2.0f) * box->GetHalfExtents(); },
[collider](SHVec3 const& vec) { collider->SetBoundingBox(vec); });
} }
else if (collider.GetType() == SHCollider::Type::SPHERE) else if (collider->GetType() == SHCollider::Type::SPHERE)
{ {
SHEditorWidgets::BeginPanel(std::format("{} Sphere Collider #{}", ICON_MD_CIRCLE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); SHEditorWidgets::BeginPanel(std::format("{} Sphere Collider #{}", ICON_MD_CIRCLE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y });
auto sphere = reinterpret_cast<SHBoundingSphere*>(collider.GetShape()); auto sphere = reinterpret_cast<SHBoundingSphere*>(collider->GetShape());
SHEditorWidgets::DragFloat("Radius", [sphere] {return sphere->GetRadius(); }, [sphere](float const& value) {sphere->SetRadius(value);}); SHEditorWidgets::DragFloat
(
"Radius",
[sphere, transformComponent]
{
const SHVec3& TF_WORLD_SCALE = transformComponent->GetWorldScale();
const float MAX_SCALE = SHMath::Max({ TF_WORLD_SCALE.x, TF_WORLD_SCALE.y, TF_WORLD_SCALE.z });
return sphere->GetRadius() / MAX_SCALE;
},
[collider](float const& value) { collider->SetBoundingSphere(value);});
} }
else if (collider.GetType() == SHCollider::Type::CAPSULE) else if (collider->GetType() == SHCollider::Type::CAPSULE)
{ {
} }
{ {
SHEditorWidgets::BeginPanel("Offset", { ImGui::GetContentRegionAvail().x, 30.0f }); SHEditorWidgets::BeginPanel("Offset", { ImGui::GetContentRegionAvail().x, 30.0f });
SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [&collider] {return collider.GetPositionOffset(); }, [&collider](SHVec3 const& vec) {collider.SetPositionOffset(vec); }); SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [&collider] {return collider->GetPositionOffset(); }, [&collider](SHVec3 const& vec) {collider->SetPositionOffset(vec); });
SHEditorWidgets::EndPanel(); SHEditorWidgets::EndPanel();
} }
if(ImGui::Button(std::format("{} Remove Collider #{}", ICON_MD_REMOVE, i).data())) if(ImGui::Button(std::format("{} Remove Collider #{}", ICON_MD_REMOVE, i).data()))

View File

@ -20,6 +20,7 @@
#include "AudioSystem/SHAudioSystem.h" #include "AudioSystem/SHAudioSystem.h"
#include "Physics/Components/SHRigidBodyComponent.h" #include "Physics/Components/SHRigidBodyComponent.h"
#include "Physics/Components/SHColliderComponent.h" #include "Physics/Components/SHColliderComponent.h"
#include "Camera/SHCameraComponent.h"
namespace SHADE namespace SHADE
{ {
@ -35,6 +36,23 @@ namespace SHADE
return selected; return selected;
} }
template <typename ComponentType, typename EnforcedComponent, std::enable_if_t<std::is_base_of_v<SHComponent, ComponentType>, bool> = true, std::enable_if_t<std::is_base_of_v<SHComponent, EnforcedComponent>, bool> = true>
bool DrawAddComponentWithEnforcedComponentButton(EntityID const& eid)
{
bool selected = false;
if (!SHComponentManager::HasComponent<ComponentType>(eid))
{
if(selected = ImGui::Selectable(std::format("Add {}", rttr::type::get<ComponentType>().get_name().data()).data()); selected)
{
if(SHComponentManager::GetComponent_s<EnforcedComponent>(eid) == nullptr)
SHComponentManager::AddComponent<EnforcedComponent>(eid);
SHComponentManager::AddComponent<ComponentType>(eid);
}
}
return selected;
}
SHEditorInspector::SHEditorInspector() SHEditorInspector::SHEditorInspector()
:SHEditorWindow("Inspector", ImGuiWindowFlags_MenuBar) :SHEditorWindow("Inspector", ImGuiWindowFlags_MenuBar)
{ {
@ -82,23 +100,27 @@ namespace SHADE
{ {
DrawComponent(rigidbodyComponent); DrawComponent(rigidbodyComponent);
} }
if (auto cameraComponent = SHComponentManager::GetComponent_s<SHCameraComponent>(eid))
{
DrawComponent(cameraComponent);
}
ImGui::Separator(); ImGui::Separator();
// Render Scripts // Render Scripts
SHScriptEngine* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>()); SHScriptEngine* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>());
scriptEngine->RenderScriptsInInspector(eid); scriptEngine->RenderScriptsInInspector(eid);
ImGui::Separator(); ImGui::Separator();
if(ImGui::BeginMenu(std::format("{} Add Component", ICON_MD_LIBRARY_ADD).data())) if(ImGui::BeginMenu(std::format("{} Add Component", ICON_MD_LIBRARY_ADD).data()))
{ {
DrawAddComponentButton<SHTransformComponent>(eid); DrawAddComponentButton<SHTransformComponent>(eid);
DrawAddComponentButton<SHRenderable>(eid); DrawAddComponentButton<SHCameraComponent>(eid);
DrawAddComponentButton<SHColliderComponent>(eid);
if(DrawAddComponentButton<SHRigidBodyComponent>(eid)) // Components that require Transforms
{
if(SHComponentManager::GetComponent_s<SHTransformComponent>(eid) == nullptr) DrawAddComponentWithEnforcedComponentButton<SHRenderable, SHTransformComponent>(eid);
{ DrawAddComponentWithEnforcedComponentButton<SHRigidBodyComponent, SHTransformComponent>(eid);
SHComponentManager::AddComponent<SHTransformComponent>(eid); DrawAddComponentWithEnforcedComponentButton<SHColliderComponent, SHTransformComponent>(eid);
}
}
ImGui::EndMenu(); ImGui::EndMenu();
} }

View File

@ -3,4 +3,5 @@
#include "HierarchyPanel/SHHierarchyPanel.h" //Hierarchy Panel #include "HierarchyPanel/SHHierarchyPanel.h" //Hierarchy Panel
#include "Inspector/SHEditorInspector.h" //Inspector #include "Inspector/SHEditorInspector.h" //Inspector
#include "Profiling/SHEditorProfiler.h" //Profiler #include "Profiling/SHEditorProfiler.h" //Profiler
#include "ViewportWindow/SHEditorViewport.h" //Editor Viewport #include "ViewportWindow/SHEditorViewport.h" //Editor Viewport
#include "AssetBrowser/SHAssetBrowser.h" //Asset Browser

View File

@ -24,12 +24,13 @@ namespace SHADE
void SHEditorViewport::Init() void SHEditorViewport::Init()
{ {
SHEditorWindow::Init(); SHEditorWindow::Init();
transformGizmo.Init();
} }
void SHEditorViewport::Update() void SHEditorViewport::Update()
{ {
SHEditorWindow::Update(); SHEditorWindow::Update();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f,0.0f));
if(Begin()) if(Begin())
{ {
ImGuizmo::SetDrawlist(); ImGuizmo::SetDrawlist();
@ -55,7 +56,7 @@ namespace SHADE
ImGuizmo::SetRect(beginCursorPos.x , beginCursorPos.y, beginContentRegionAvailable.x, beginContentRegionAvailable.y); ImGuizmo::SetRect(beginCursorPos.x , beginCursorPos.y, beginContentRegionAvailable.x, beginContentRegionAvailable.y);
transformGizmo.Draw(); transformGizmo.Draw();
ImGui::End(); ImGui::End();
ImGui::PopStyleVar();
} }
void SHEditorViewport::Exit() void SHEditorViewport::Exit()

View File

@ -13,6 +13,12 @@
#include "Editor/EditorWindow/ViewportWindow/SHEditorViewport.h" #include "Editor/EditorWindow/ViewportWindow/SHEditorViewport.h"
namespace SHADE namespace SHADE
{ {
void SHTransformGizmo::Init()
{
auto& style = ImGuizmo::GetStyle();
style.RotationLineThickness = 2.5f;
}
void SHTransformGizmo::Draw() void SHTransformGizmo::Draw()
{ {
bool justChangedTfm = false; bool justChangedTfm = false;
@ -26,9 +32,12 @@ namespace SHADE
SHMatrix view = SHMatrix::Transpose(editorCamera->GetViewMatrix()); SHMatrix view = SHMatrix::Transpose(editorCamera->GetViewMatrix());
SHMatrix proj = SHMatrix::Transpose(editorCamera->GetProjMatrix()); SHMatrix proj = SHMatrix::Transpose(editorCamera->GetProjMatrix());
//Invert projection y-axis
proj(1, 1) *= -1; proj(1, 1) *= -1;
static SHMatrix gridMat = SHMatrix::Translate(0, -0.5f, 0.f) * SHMatrix::Identity; static SHMatrix gridMat = SHMatrix::Translate(0, -0.5f, 0.f) * SHMatrix::Identity;
//ImGuizmo::DrawGrid(&view._11, &proj._11, &gridMat._11, 100.f);
if (selectedEntityTransformComponent == nullptr) if (selectedEntityTransformComponent == nullptr)
{ {
SHEditor* editor = SHSystemManager::GetSystem<SHEditor>(); SHEditor* editor = SHSystemManager::GetSystem<SHEditor>();
@ -55,31 +64,37 @@ namespace SHADE
return; return;
SHMatrix mat = selectedEntityTransformComponent->GetTRS(); SHMatrix mat = selectedEntityTransformComponent->GetTRS();
isManipulating = ImGuizmo::Manipulate(&view._11, &proj._11, static_cast<ImGuizmo::OPERATION>(operation), ImGuizmo::MODE::WORLD, &mat._11); useSnap = ImGui::IsKeyDown(ImGuiKey_LeftCtrl);
if (!justChangedTfm) if(useSnap)
{ {
if (ImGui::IsItemClicked()) switch (operation)
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHMatrix>>(selectedEntityTransformComponent->GetTRS(), mat, [tfm = std::move(selectedEntityTransformComponent)](SHMatrix const& mtx) {
{ case Operation::TRANSLATE: snap = &translationSnap.x; break;
if (!tfm) case Operation::ROTATE: snap = &rotationSnap; break;
return; case Operation::SCALE: snap = &scaleSnap; break;
SHVec3 translate{}, rotate{}, scale{}; default: snap = &translationSnap.x;
mtx.Decompose(translate, rotate, scale); }
tfm->SetWorldPosition(translate);
tfm->SetWorldRotation(rotate);
tfm->SetWorldScale(scale);
})));
else if (ImGui::IsItemHovered(ImGuiMouseButton_Left) && ImGui::IsMouseDown(ImGuiMouseButton_Left) && isManipulating)
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHMatrix>>(selectedEntityTransformComponent->GetTRS(), mat, [tfm = std::move(selectedEntityTransformComponent)](SHMatrix const& mtx)
{
if (!tfm)
return;
SHVec3 translate{}, rotate{}, scale{};
mtx.Decompose(translate, rotate, scale);
tfm->SetWorldPosition(translate);
tfm->SetWorldRotation(rotate);
tfm->SetWorldScale(scale);
})), true);
} }
ImGuizmo::Manipulate(&view._11, &proj._11, static_cast<ImGuizmo::OPERATION>(operation), ImGuizmo::MODE::WORLD, &mat._11, nullptr, useSnap ? snap : nullptr);
static bool startRecording = false;
if (!justChangedTfm && ImGuizmo::IsUsing())
{
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHMatrix>>(selectedEntityTransformComponent->GetTRS(), mat, [tfm = (selectedEntityTransformComponent)](SHMatrix const& mtx)
{
if (!tfm)
return;
SHVec3 translate{}, rotate{}, scale{};
mtx.Decompose(translate, rotate, scale);
tfm->SetWorldPosition(translate);
tfm->SetWorldRotation(rotate);
tfm->SetWorldScale(scale);
})), startRecording);
if(!startRecording)
startRecording = true;
}
isManipulating = ImGuizmo::IsUsing() || startRecording;
if(startRecording && ImGui::IsMouseReleased(ImGuiMouseButton_Left))
startRecording = false;
} }
} }

View File

@ -37,11 +37,17 @@ namespace SHADE
UNIVERSAL = TRANSLATE | ROTATE | SCALEU UNIVERSAL = TRANSLATE | ROTATE | SCALEU
}; };
void Init();
void Draw(); void Draw();
bool isManipulating = false; bool isManipulating = false;
bool useSnap = false;
Mode mode = Mode::WORLD; Mode mode = Mode::WORLD;
Operation operation = Operation::TRANSLATE; Operation operation = Operation::TRANSLATE;
private: private:
float scaleSnap = 0.25f;
float rotationSnap = 1.0f;
SHVec3 translationSnap = SHVec3(0.25f, 0.25f, 0.25f);
float* snap = nullptr;
SHTransformComponent* selectedEntityTransformComponent{nullptr}; SHTransformComponent* selectedEntityTransformComponent{nullptr};
SHCameraComponent* editorCamera{nullptr}; SHCameraComponent* editorCamera{nullptr};
}; };

View File

@ -92,6 +92,7 @@ namespace SHADE
SHEditorWindowManager::CreateEditorWindow<SHHierarchyPanel>(); SHEditorWindowManager::CreateEditorWindow<SHHierarchyPanel>();
SHEditorWindowManager::CreateEditorWindow<SHEditorInspector>(); SHEditorWindowManager::CreateEditorWindow<SHEditorInspector>();
SHEditorWindowManager::CreateEditorWindow<SHEditorProfiler>(); SHEditorWindowManager::CreateEditorWindow<SHEditorProfiler>();
SHEditorWindowManager::CreateEditorWindow<SHAssetBrowser>();
SHEditorWindowManager::CreateEditorWindow<SHEditorViewport>(); SHEditorWindowManager::CreateEditorWindow<SHEditorViewport>();
io = &ImGui::GetIO(); io = &ImGui::GetIO();
@ -134,7 +135,7 @@ namespace SHADE
} }
} }
PollPicking(); //PollPicking();
if(ImGui::IsKeyDown(ImGuiKey_LeftShift) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_Z)) if(ImGui::IsKeyDown(ImGuiKey_LeftShift) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_Z))
{ {

View File

@ -54,7 +54,7 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
bool SHEditorUI::CollapsingHeader(const std::string& title) bool SHEditorUI::CollapsingHeader(const std::string& title)
{ {
return ImGui::CollapsingHeader(title.c_str()); return ImGui::CollapsingHeader(title.c_str(), ImGuiTreeNodeFlags_DefaultOpen);
} }
void SHEditorUI::SameLine() void SHEditorUI::SameLine()

View File

@ -40,7 +40,6 @@ namespace SHADE
{ {
ImGui::BeginGroup(); ImGui::BeginGroup();
auto cursorPos = ImGui::GetCursorScreenPos();
auto itemSpacing = ImGui::GetStyle().ItemSpacing; auto itemSpacing = ImGui::GetStyle().ItemSpacing;
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
@ -158,7 +157,7 @@ namespace SHADE
} }
template <typename T, std::size_t N> template <typename T, std::size_t N>
static bool DragN(const std::string& fieldLabel, std::vector<std::string>const& componentLabels, static bool DragN(const std::string& label, std::vector<std::string>const& componentLabels,
std::vector<T*> values, float speed = 0.1f, const char* displayFormat = "", T valueMin = T(), T valueMax = T(), std::vector<T*> values, float speed = 0.1f, const char* displayFormat = "", T valueMin = T(), T valueMax = T(),
ImGuiSliderFlags flags = 0, bool* isHovered = nullptr) ImGuiSliderFlags flags = 0, bool* isHovered = nullptr)
{ {
@ -169,13 +168,13 @@ namespace SHADE
const ImGuiContext& g = *GImGui; const ImGuiContext& g = *GImGui;
bool valueChanged = false; bool valueChanged = false;
ImGui::BeginGroup(); ImGui::BeginGroup();
ImGui::PushID(fieldLabel.c_str()); ImGui::PushID(label.c_str());
PushMultiItemsWidthsAndLabels(componentLabels, 0.0f); PushMultiItemsWidthsAndLabels(componentLabels, 0.0f);
ImGui::BeginColumns("DragVecCol", 2, ImGuiOldColumnFlags_NoBorder | ImGuiOldColumnFlags_NoResize); ImGui::BeginColumns("DragVecCol", 2, ImGuiOldColumnFlags_NoBorder | ImGuiOldColumnFlags_NoResize);
ImGui::SetColumnWidth(-1, 80.0f); ImGui::SetColumnWidth(-1, 80.0f);
ImGui::Text(fieldLabel.c_str()); ImGui::Text(label.c_str());
if (isHovered) if (isHovered)
*isHovered = ImGui::IsItemHovered(); *isHovered = ImGui::IsItemHovered();
ImGui::NextColumn(); ImGui::NextColumn();
for (std::size_t i = 0; i < N; ++i) for (std::size_t i = 0; i < N; ++i)
{ {
@ -203,75 +202,91 @@ namespace SHADE
return valueChanged; return valueChanged;
} }
static bool DragVec2(const std::string& fieldLabel, std::vector<std::string>const& componentLabels, std::function<SHVec2(void)> get, static bool DragVec2(const std::string& label, std::vector<std::string>const& componentLabels, std::function<SHVec2(void)> get,
std::function<void(SHVec2)> set, float speed = 0.1f, const char* displayFormat = "%.3f", float valueMin = 0.0f, float valueMax = 0.0f, std::function<void(SHVec2)> set, float speed = 0.1f, const char* displayFormat = "%.3f", std::string_view const& tooltip = {}, float valueMin = 0.0f, float valueMax = 0.0f,
ImGuiSliderFlags flags = 0) ImGuiSliderFlags flags = 0)
{ {
SHVec2 values = get(); SHVec2 values = get();
bool changed = false; bool const changed = DragN<float, 2>(label, componentLabels, { &values.x, &values.y }, speed, displayFormat, valueMin, valueMax, flags);
if (DragN<float, 2>(fieldLabel, componentLabels, { &values.x, &values.y }, speed, displayFormat, valueMin, valueMax, flags)) static bool startRecording = false;
{
changed = true;
}
if (changed) if (changed)
{ {
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !ImGui::IsMouseDragging(ImGuiMouseButton_Left)) SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHVec2>>(get(), values, set)), startRecording);
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHVec2>>(get(), values, set)), false); if (!startRecording)
else if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) startRecording = true;
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHVec2>>(get(), values, set)), true); }
else if (ImGui::IsItemDeactivatedAfterEdit()) if (startRecording && ImGui::IsMouseReleased(ImGuiMouseButton_Left))
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHVec2>>(get(), values, set)), false); startRecording = false;
if(!tooltip.empty())
{
if(ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text(tooltip.data());
ImGui::EndTooltip();
}
} }
return changed; return changed;
} }
static bool DragVec3(const std::string& fieldLabel, std::vector<std::string>const& componentLabels, std::function<SHVec3(void)> get, static bool DragVec3(const std::string& label, std::vector<std::string>const& componentLabels, std::function<SHVec3(void)> get,
std::function<void(SHVec3)> set, float speed = 0.1f, const char* displayFormat = "%.3f", float valueMin = 0.0f, float valueMax = 0.0f, std::function<void(SHVec3)> set, float speed = 0.1f, const char* displayFormat = "%.3f", std::string_view const& tooltip = {}, float valueMin = 0.0f, float valueMax = 0.0f,
ImGuiSliderFlags flags = 0) ImGuiSliderFlags flags = 0)
{ {
SHVec3 values = get(); SHVec3 values = get();
bool changed = false; bool const changed = DragN<float, 3>(label, componentLabels, { &values.x, &values.y, &values.z }, speed, displayFormat, valueMin, valueMax, flags);
if (DragN<float, 3>(fieldLabel, componentLabels, { &values.x, &values.y, &values.z }, speed, displayFormat, valueMin, valueMax, flags))
{
changed = true;
}
static bool startRecording = false;
if (changed) if (changed)
{ {
if (ImGui::IsMouseDown(ImGuiMouseButton_Left) && !ImGui::IsMouseDragging(ImGuiMouseButton_Left, -0.2f)) SHVec3 old = get();
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHVec3>>(get(), values, set)), false); SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHVec3>>(old, values, set)), startRecording);
else if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) if (!startRecording)
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHVec3>>(get(), values, set)), true); startRecording = true;
else if (ImGui::IsItemDeactivatedAfterEdit()) }
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHVec3>>(get(), values, set)), false); if (startRecording && ImGui::IsMouseReleased(ImGuiMouseButton_Left))
{
startRecording = false;
}
if(!tooltip.empty())
{
if(ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text(tooltip.data());
ImGui::EndTooltip();
}
} }
return changed; return changed;
} }
static bool DragVec4(const std::string& fieldLabel, std::vector<std::string>const& componentLabels, std::function<SHVec4(void)> get, static bool DragVec4(const std::string& label, std::vector<std::string>const& componentLabels, std::function<SHVec4(void)> get,
std::function<void(SHVec4)> set, float speed = 0.1f, const char* displayFormat = "%.3f", float valueMin = 0.0f, float valueMax = 0.0f, std::function<void(SHVec4)> set, float speed = 0.1f, const char* displayFormat = "%.3f", std::string_view const& tooltip = {}, float valueMin = 0.0f, float valueMax = 0.0f,
ImGuiSliderFlags flags = 0) ImGuiSliderFlags flags = 0)
{ {
SHVec4 values = get(); SHVec4 values = get();
bool changed = false; bool const changed = DragN<float, 4>(label, componentLabels, { &values.x, &values.y, &values.z, &values.w }, speed, displayFormat, valueMin, valueMax, flags);
if (DragN<float, 4>(fieldLabel, componentLabels, { &values.x, &values.y, &values.z, &values.w }, speed, displayFormat, valueMin, valueMax, flags)) static bool startRecording = false;
{
changed = true;
}
if (changed) if (changed)
{ {
if (ImGui::IsMouseDown(ImGuiMouseButton_Left) && !ImGui::IsMouseDragging(ImGuiMouseButton_Left, -0.2f)) SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHVec4>>(get(), values, set)), startRecording);
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHVec4>>(get(), values, set)), false); if (!startRecording)
else if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) startRecording = true;
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHVec4>>(get(), values, set)), true); }
else if (ImGui::IsItemDeactivatedAfterEdit()) if (startRecording && ImGui::IsMouseReleased(ImGuiMouseButton_Left))
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<SHVec4>>(get(), values, set)), false); {
startRecording = false;
}
if(!tooltip.empty())
{
if(ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text(tooltip.data());
ImGui::EndTooltip();
}
} }
return changed; return changed;
} }
@ -279,173 +294,325 @@ namespace SHADE
//|| Widget Extensions || //|| Widget Extensions ||
//#==============================================================# //#==============================================================#
static bool CheckBox(std::string const& label, std::function<bool(void)> get, std::function<void(bool const&)> set) static void TextLabel(std::string_view const& text, bool sameLine = true)
{
const ImVec2 textSize = ImGui::CalcTextSize(text.data(), NULL, true);
if(textSize.x > 0.0f)
{
ImGui::Text(text.data());
ImGui::SameLine();
}
}
static bool CheckBox(std::string_view const& label, std::function<bool(void)> get, std::function<void(bool const&)> set, std::string_view const& tooltip = {})
{ {
bool value = get(); bool value = get();
if (ImGui::Checkbox(label.c_str(), &value)) ImGui::BeginGroup();
ImGui::PushID(label.data());
TextLabel(label);
if (ImGui::Checkbox("##", &value))
{ {
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<bool>>(get(), value, set)), false); SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<bool>>(get(), value, set)), false);
return true; return true;
} }
ImGui::PopID();
ImGui::EndGroup();
if(!tooltip.empty())
{
if(ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text(tooltip.data());
ImGui::EndTooltip();
}
}
return false; return false;
} }
template<typename T> template<typename T>
static bool RadioButton(std::vector<std::string> const& listLabels, std::vector<T> const& listTypes, std::function<T(void)> get, std::function<void(T const&)> set) static bool RadioButton(std::vector<std::string> const& label, std::vector<T> const& listTypes, std::function<T(void)> get, std::function<void(T const&)> set ,std::string_view const& tooltip = {})
{ {
T type = get(); T type = get();
ImGui::BeginGroup();
ImGui::PushID(label.data());
TextLabel(label);
for (size_t i = 0; i < listTypes.size(); i++) for (size_t i = 0; i < listTypes.size(); i++)
{ {
if (ImGui::RadioButton(listLabels[i].c_str(), type == listTypes[i])) if (ImGui::RadioButton(label[i].c_str(), type == listTypes[i]))
{ {
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<T>>(get(), listTypes[i], set)), false); SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<T>>(get(), listTypes[i], set)), false);
} }
ImGui::SameLine(); ImGui::SameLine();
} }
ImGui::PopID();
ImGui::EndGroup();
if (!tooltip.empty())
{
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text(tooltip.data());
ImGui::EndTooltip();
}
}
return true; return true;
} }
static bool InputText(const std::string& label, const std::function<std::string(void)> get, static bool InputText(const std::string& label, const std::function<std::string(void)> get,
const std::function<void(std::string)> set, ImGuiInputTextFlags flag = 0, const std::function<void(std::string)> set, std::string_view const& tooltip = {},
ImGuiInputTextCallback callback = (ImGuiInputTextCallback)0, void* userData = (void*)0) ImGuiInputTextFlags flag = 0, ImGuiInputTextCallback callback = (ImGuiInputTextCallback)0, void* userData = (void*)0)
{ {
std::string text = get(); std::string text = get();
if (ImGui::InputText(label.c_str(), &text, flag, callback, userData)) ImGui::BeginGroup();
ImGui::PushID(label.data());
TextLabel(label);
if (ImGui::InputText("##", &text, flag, callback, userData))
{ {
if (ImGui::IsItemDeactivatedAfterEdit()) if (ImGui::IsItemDeactivatedAfterEdit())
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<std::string>>(get(), text, set)), false); SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<std::string>>(get(), text, set)), false);
return true; return true;
} }
return false; ImGui::PopID();
} ImGui::EndGroup();
if (!tooltip.empty())
template <typename T>
static bool DragScalar(const std::string& fieldLabel, ImGuiDataType data_type, std::function<T(void)> get, std::function<void(T const&)> set,
float speed = 1.0f, T p_min = T(), T p_max = T(), const char* displayFormat = "%.3f", ImGuiSliderFlags flags = 0)
{
T value = get();
std::cout << value << " \n";
//bool hasChange = ImGui::DragScalar(fieldLabel.c_str(), data_type, &value, speed, &p_min, &p_max, displayFormat, flags);
if (ImGui::DragScalar(fieldLabel.c_str(), data_type, &value, speed, &p_min, &p_max, displayFormat, flags))
{ {
if (ImGui::IsMouseDown(ImGuiMouseButton_Left) && !ImGui::IsMouseDragging(ImGuiMouseButton_Left)) if (ImGui::IsItemHovered())
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<T>>(get(), value, set)), false); {
else if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) ImGui::BeginTooltip();
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<T>>(get(), value, set)), true); ImGui::Text(tooltip.data());
else if (ImGui::IsItemDeactivatedAfterEdit()) ImGui::EndTooltip();
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<T>>(get(), value, set)), false); }
return true;
} }
return false; return false;
} }
static bool DragFloat(const std::string& fieldLabel, std::function<float(void)> get, std::function<void(float const&)> set, template <typename T>
static bool DragScalar(const std::string& label, ImGuiDataType data_type, std::function<T(void)> get, std::function<void(T const&)> set,
float speed = 1.0f, T p_min = T(), T p_max = T(), const char* displayFormat = "%.3f", std::string_view const& tooltip = {}, ImGuiSliderFlags flags = 0)
{
T value = get();
ImGui::BeginGroup();
ImGui::PushID(label.data());
TextLabel(label);
const bool hasChange = ImGui::DragScalar("##", data_type, &value, speed, &p_min, &p_max, displayFormat, flags);
static bool startRecording = false;
if (hasChange)
{
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<T>>(get(), value, set)), startRecording);
if (!startRecording)
startRecording = true;
}
if (startRecording && ImGui::IsMouseReleased(ImGuiMouseButton_Left))
{
startRecording = false;
}
ImGui::PopID();
ImGui::EndGroup();
if (!tooltip.empty())
{
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text(tooltip.data());
ImGui::EndTooltip();
}
}
return hasChange;
}
static bool DragFloat(const std::string_view& label, std::function<float(void)> get, std::function<void(float const&)> set, std::string_view const& tooltip = {},
float speed = 0.1f, float p_min = float(), float p_max = float(), const char* displayFormat = "%.3f", ImGuiSliderFlags flags = 0) float speed = 0.1f, float p_min = float(), float p_max = float(), const char* displayFormat = "%.3f", ImGuiSliderFlags flags = 0)
{ {
float value = get(); float value = get();
//bool hasChange = ImGui::DragFloat(fieldLabel.c_str(), &value, speed, p_min, p_max, displayFormat, flags); ImGui::BeginGroup();
if (ImGui::DragFloat(fieldLabel.c_str(), &value, speed, p_min, p_max, displayFormat, flags)) ImGui::PushID(label.data());
TextLabel(label);
const bool hasChange = ImGui::DragFloat("##", &value, speed, p_min, p_max, displayFormat, flags);
static bool startRecording = false;
if (hasChange)
{ {
if (ImGui::IsMouseDown(ImGuiMouseButton_Left) && !ImGui::IsMouseDragging(ImGuiMouseButton_Left)) SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<float>>(get(), value, set)), startRecording);
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<float>>(get(), value, set)), false); if (!startRecording)
else if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) startRecording = true;
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<float>>(get(), value, set)), true);
else if (ImGui::IsItemDeactivatedAfterEdit())
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<float>>(get(), value, set)), false);
return true;
} }
if (startRecording && ImGui::IsMouseReleased(ImGuiMouseButton_Left))
return false; {
startRecording = false;
}
ImGui::PopID();
ImGui::EndGroup();
if(!tooltip.empty())
{
if(ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text(tooltip.data());
ImGui::EndTooltip();
}
}
return hasChange;
} }
static bool DragInt(const std::string& fieldLabel, std::function<int(void)> get, std::function<void(int const&)> set, static bool DragInt(const std::string& label, std::function<int(void)> get, std::function<void(int const&)> set, std::string_view const& tooltip = {},
float speed = 1.0f, int p_min = int(), int p_max = int(), const char* displayFormat = "%d", ImGuiSliderFlags flags = 0) float speed = 1.0f, int p_min = int(), int p_max = int(), const char* displayFormat = "%d", ImGuiSliderFlags flags = 0)
{ {
int value = get(); int value = get();
//bool hasChange = ImGui::DragFloat(fieldLabel.c_str(), &value, speed, p_min, p_max, displayFormat, flags); ImGui::BeginGroup();
if (ImGui::DragInt(fieldLabel.c_str(), &value, speed, p_min, p_max, displayFormat, flags)) ImGui::PushID(label.data());
TextLabel(label);
const bool hasChange = ImGui::DragInt("##", &value, speed, p_min, p_max, displayFormat, flags);
static bool startRecording = false;
if (hasChange)
{ {
if (ImGui::IsMouseDown(ImGuiMouseButton_Left) && !ImGui::IsMouseDragging(ImGuiMouseButton_Left)) SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<int>>(get(), value, set)), startRecording);
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<int>>(get(), value, set)), false); if (!startRecording)
else if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) startRecording = true;
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<int>>(get(), value, set)), true);
else if (ImGui::IsItemDeactivatedAfterEdit())
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<int>>(get(), value, set)), false);
return true;
} }
if (startRecording && ImGui::IsMouseReleased(ImGuiMouseButton_Left))
return false; {
startRecording = false;
}
ImGui::PopID();
ImGui::EndGroup();
if (!tooltip.empty())
{
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text(tooltip.data());
ImGui::EndTooltip();
}
}
return hasChange;
} }
template <typename T> template <typename T>
static bool SliderScalar(const std::string& fieldLabel, ImGuiDataType data_type, T min, T max, std::function<T(void)> get, std::function<void(T const&)> set, static bool SliderScalar(const std::string& label, ImGuiDataType data_type, T min, T max, std::function<T(void)> get, std::function<void(T const&)> set, std::string_view const& tooltip = {},
const char* displayFormat = "%.3f", ImGuiSliderFlags flags = 0) const char* displayFormat = "%.3f", ImGuiSliderFlags flags = 0)
{ {
T value = get(); T value = get();
if (ImGui::SliderScalar(fieldLabel.c_str(), data_type, &value, &min, &max, displayFormat, flags)) ImGui::BeginGroup();
ImGui::PushID(label.data());
TextLabel(label);
bool const hasChange = ImGui::SliderScalar("##", data_type, &value, &min, &max, displayFormat, flags);
static bool startRecording = false;
if (hasChange)
{ {
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left, false) && !ImGui::IsMouseDragging(ImGuiMouseButton_Left, -0.2f)) SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<T>>(get(), value, set)), startRecording);
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<T>>(get(), value, set)), false); if (!startRecording)
else if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) startRecording = true;
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<T>>(get(), value, set)), true);
return true;
} }
if (startRecording && ImGui::IsMouseReleased(ImGuiMouseButton_Left))
return false; {
startRecording = false;
}
ImGui::PopID();
ImGui::EndGroup();
if (!tooltip.empty())
{
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text(tooltip.data());
ImGui::EndTooltip();
}
}
return hasChange;
} }
static bool SliderFloat(const std::string& fieldLabel, float min, float max, std::function<float(void)> get, std::function<void(float const&)> set, static bool SliderFloat(const std::string& label, float const& min, float const& max, std::function<float(void)> get, std::function<void(float const&)> set, std::string_view const& tooltip = {},
const char* displayFormat = "%.3f", ImGuiSliderFlags flags = 0) const char* displayFormat = "%.3f", ImGuiSliderFlags flags = 0)
{ {
float value = get(); float value = get();
if (ImGui::SliderFloat(fieldLabel.c_str(), &value, min, max, displayFormat, flags)) ImGui::BeginGroup();
ImGui::PushID(label.data());
TextLabel(label);
bool const hasChange = ImGui::SliderFloat("##", &value, min, max, displayFormat, flags);
static bool startRecording = false;
if (hasChange)
{ {
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left, false) && !ImGui::IsMouseDragging(ImGuiMouseButton_Left, -0.2f)) SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<float>>(get(), value, set)), startRecording);
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<float>>(get(), value, set)), false); if (!startRecording)
else if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) startRecording = true;
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<float>>(get(), value, set)), true);
return true;
} }
if (startRecording && ImGui::IsMouseReleased(ImGuiMouseButton_Left))
return false; {
startRecording = false;
}
ImGui::PopID();
ImGui::EndGroup();
if (!tooltip.empty())
{
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text(tooltip.data());
ImGui::EndTooltip();
}
}
return hasChange;
} }
static bool SliderInt(const std::string& fieldLabel, int min, int max, std::function<int(void)> get, std::function<void(int const&)> set, static bool SliderInt(const std::string& label, int min, int max, std::function<int(void)> get, std::function<void(int const&)> set, std::string_view const& tooltip = {},
const char* displayFormat = "%d", ImGuiSliderFlags flags = 0) const char* displayFormat = "%d", ImGuiSliderFlags flags = 0)
{ {
int value = get(); int value = get();
if (ImGui::SliderInt(fieldLabel.c_str(), &value, min, max, displayFormat, flags)) ImGui::BeginGroup();
ImGui::PushID(label.data());
TextLabel(label);
bool const hasChange = ImGui::SliderInt("##", &value, min, max, displayFormat, flags);
static bool startRecording = false;
if (hasChange)
{ {
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left, false) && !ImGui::IsMouseDragging(ImGuiMouseButton_Left, -0.2f))
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<int>>(get(), value, set)), false);
else if (ImGui::IsMouseDragging(ImGuiMouseButton_Left))
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<int>>(get(), value, set)), true);
return true; SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<int>>(get(), value, set)), startRecording);
if (!startRecording)
startRecording = true;
} }
if (startRecording && ImGui::IsMouseReleased(ImGuiMouseButton_Left))
return false; {
startRecording = false;
}
ImGui::PopID();
ImGui::EndGroup();
if (!tooltip.empty())
{
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text(tooltip.data());
ImGui::EndTooltip();
}
}
return hasChange;
} }
static bool ComboBox(const std::string& fieldLabel, std::vector<const char*> list, std::function<int(void)> get, std::function<void(int const&)> set) static bool ComboBox(const std::string& label, std::vector<const char*> list, std::function<int(void)> get, std::function<void(int const&)> set, std::string_view const& tooltip = {})
{ {
bool edited = false; bool edited = false;
int selected = get(); int selected = get();
ImGui::PushID(fieldLabel.c_str()); ImGui::BeginGroup();
ImGui::Text(fieldLabel.c_str()); ImGui::SameLine(); ImGui::PushID(label.c_str());
TextLabel(label);
ImGui::SameLine();
if (edited = ImGui::Combo("##Combo", &selected, list.data(), static_cast<int>(list.size()))) if (edited = ImGui::Combo("##Combo", &selected, list.data(), static_cast<int>(list.size())))
{ {
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<int>>(get(), selected, set)), false); SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCommand<int>>(get(), selected, set)), false);
} }
ImGui::PopID(); ImGui::PopID();
ImGui::EndGroup();
if (!tooltip.empty())
{
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text(tooltip.data());
ImGui::EndTooltip();
}
}
return edited; return edited;
} }
}; };

View File

@ -11,3 +11,6 @@ constexpr SHEventIdentifier SH_ENTITY_CREATION_EVENT { 2 };
constexpr SHEventIdentifier SH_COMPONENT_ADDED_EVENT { 3 }; constexpr SHEventIdentifier SH_COMPONENT_ADDED_EVENT { 3 };
constexpr SHEventIdentifier SH_COMPONENT_REMOVED_EVENT { 4 }; constexpr SHEventIdentifier SH_COMPONENT_REMOVED_EVENT { 4 };
constexpr SHEventIdentifier SH_SCENEGRAPH_CHANGE_PARENT_EVENT { 5 }; constexpr SHEventIdentifier SH_SCENEGRAPH_CHANGE_PARENT_EVENT { 5 };
constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_ADDED_EVENT { 6 };
constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_REMOVED_EVENT { 7 };

View File

@ -12,6 +12,7 @@
#include "SHpch.h" #include "SHpch.h"
#include "SHEvent.h" #include "SHEvent.h"
#include "SHEventReceiver.h" #include "SHEventReceiver.h"
#include "SH_API.h"
/****************************************************************************** /******************************************************************************
INSTRUCTIONS FOR USE: INSTRUCTIONS FOR USE:
@ -67,7 +68,7 @@ namespace SHADE
using EventManagerListener = std::function<void(SHEvent)>; using EventManagerListener = std::function<void(SHEvent)>;
class SHEventManager class SH_API SHEventManager
{ {
private: private:

View File

@ -49,7 +49,7 @@ namespace SHADE
SHVkSampler::~SHVkSampler() noexcept SHVkSampler::~SHVkSampler() noexcept
{ {
if (vkSampler) if (vkSampler)
device->GetVkLogicalDevice().destroySampler(); device->GetVkLogicalDevice().destroySampler(vkSampler);
} }
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/

View File

@ -155,7 +155,7 @@ namespace SHADE
SHVkDebugMessenger::GenMessengerType(SH_DEBUG_MSG_TYPE::T_GENERAL, SH_DEBUG_MSG_TYPE::T_VALIDATION, SH_DEBUG_MSG_TYPE::T_PERFORMANCE)); SHVkDebugMessenger::GenMessengerType(SH_DEBUG_MSG_TYPE::T_GENERAL, SH_DEBUG_MSG_TYPE::T_VALIDATION, SH_DEBUG_MSG_TYPE::T_PERFORMANCE));
instanceDbgInfo.pfnUserCallback = SHVulkanDebugUtil::GenericDebugCallback; instanceDbgInfo.pfnUserCallback = SHVulkanDebugUtil::GenericDebugCallback;
instanceInfo.pNext = static_cast<vk::DebugUtilsMessengerCreateInfoEXT*>(&instanceDbgInfo); //instanceInfo.pNext = static_cast<vk::DebugUtilsMessengerCreateInfoEXT*>(&instanceDbgInfo);
} }
// Finally create the instance // Finally create the instance

View File

@ -30,185 +30,214 @@ of DigiPen Institute of Technology is prohibited.
namespace SHADE namespace SHADE
{ {
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* SHBatch - Usage Functions */ /* SHBatch - Usage Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
SHBatch::SHBatch(Handle<SHVkPipeline> pipeline) SHBatch::SHBatch(Handle<SHVkPipeline> pipeline)
: pipeline{ pipeline } : pipeline{ pipeline }
{ {
if (!pipeline) if (!pipeline)
throw std::invalid_argument("Attempted to create a SHBatch with an invalid SHPipeline!"); throw std::invalid_argument("Attempted to create a SHBatch with an invalid SHPipeline!");
// Mark all as dirty // Mark all as dirty
setAllDirtyFlags(); setAllDirtyFlags();
}
void SHBatch::Add(const SHRenderable* renderable)
{
// Ignore if null
if (!renderable->GetMesh())
return;
// Check if we have a SubBatch with the same mesh yet
auto subBatch = std::find_if(subBatches.begin(), subBatches.end(), [&](const SHSubBatch& batch)
{
return batch.Mesh == renderable->GetMesh();
});
// Create one if not found
if (subBatch == subBatches.end())
{
subBatches.emplace_back(renderable->GetMesh());
subBatch = subBatches.end() - 1;
} }
void SHBatch::Add(const SHRenderable* renderable) // Add renderable in
subBatch->Renderables.insert(renderable->GetEID());
// Also add material instance in
referencedMatInstances.insert(renderable->GetMaterial());
// Mark all as dirty
setAllDirtyFlags();
}
void SHBatch::Remove(const SHRenderable* renderable)
{
// Check if we have a SubBatch with the same mesh yet
auto subBatch = std::find_if(subBatches.begin(), subBatches.end(), [&](const SHSubBatch& batch)
{
return batch.Mesh == renderable->GetMesh();
});
// Attempt to remove if it exists
if (subBatch == subBatches.end())
return;
subBatch->Renderables.erase(renderable->GetEID());
// Check if other renderables in subBatches contain the same material instance
bool matUnused = true;
Handle<SHMaterialInstance> matToCheck = renderable->HasMaterialChanged() ? renderable->GetPrevMaterial() : renderable->GetMaterial();
for (const auto& sb : subBatches)
{ {
// Check if we have a SubBatch with the same mesh yet // Check material usage
auto subBatch = std::find_if(subBatches.begin(), subBatches.end(), [&](const SHSubBatch& batch) for (const auto& rendId : sb.Renderables)
{
auto rend = SHComponentManager::GetComponent<SHRenderable>(rendId);
if (rend)
{ {
return batch.Mesh == renderable->Mesh; if (rend->GetMaterial() == matToCheck)
}); {
matUnused = false;
// Create one if not found break;
if (subBatch == subBatches.end()) }
{
subBatches.emplace_back(renderable->Mesh);
subBatch = subBatches.end() - 1;
} }
else
// Add renderable in {
subBatch->Renderables.insert(renderable); SHLOG_WARNING("[SHBatch] Entity with a missing SHRenderable found!");
}
// Also add material instance in }
referencedMatInstances.insert(renderable->GetMaterial());
// Mark all as dirty
setAllDirtyFlags();
} }
void SHBatch::Remove(const SHRenderable* renderable) // Material is no longer in this library, so we remove it
if (matUnused)
referencedMatInstances.erase(renderable->HasChanged() ? renderable->GetPrevMaterial() : renderable->GetMaterial());
// Mesh is no longer in this batch, so we remove the associated sub batch
if (subBatch->Renderables.empty())
subBatches.erase(subBatch);
// Mark all as dirty
setAllDirtyFlags();
}
void SHBatch::Clear()
{
subBatches.clear();
// Clear CPU buffers
drawData.clear();
transformData.clear();
instancedIntegerData.clear();
matPropsData.reset();
matPropsDataSize = 0;
// Clear GPU buffers
for (int i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i)
{ {
// Check if we have a SubBatch with the same mesh yet drawDataBuffer[i].Free();
auto subBatch = std::find_if(subBatches.begin(), subBatches.end(), [&](const SHSubBatch& batch) transformDataBuffer[i].Free();
{ instancedIntegerBuffer[i].Free();
return batch.Mesh == renderable->Mesh; matPropsBuffer[i].Free();
}); }
}
// Attempt to remove if it exists void SHBatch::UpdateMaterialBuffer(uint32_t frameIndex, Handle<SHVkDescriptorPool> descPool)
if (subBatch == subBatches.end()) {
return; if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS)
{
subBatch->Renderables.erase(renderable); SHLOG_WARNING("[SHBatch] Attempted to update transform buffers with an invalid frame index.");
return;
// Check if other renderables in subBatches contain the same material instance
bool matUnused = true;
for (const auto& sb : subBatches)
for (const auto& rend : sb.Renderables)
{
if (rend->GetMaterial() == renderable->GetMaterial())
{
matUnused = false;
break;
}
}
// Material is no longer in this library, so we remove it
if (matUnused)
referencedMatInstances.erase(renderable->GetMaterial());
// Mark all as dirty
for (bool& dirt : isDirty)
dirt = true;
} }
void SHBatch::Clear() // Check if there are even material properties to update
if (!matPropsData)
return;
// Check if any materials have changed
bool hasChanged = false;
for (const auto& material : referencedMatInstances)
{ {
subBatches.clear(); if (material->HasChanged())
{
// Clear CPU buffers hasChanged = true;
drawData.clear(); break;
transformData.clear(); }
eidData.clear();
matPropsData.reset();
matPropsDataSize = 0;
// Clear GPU buffers
for (int i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i)
{
drawDataBuffer[i].Free();
transformDataBuffer[i].Free();
eidBuffer[i].Free();
matPropsBuffer[i].Free();
}
} }
void SHBatch::UpdateMaterialBuffer(uint32_t frameIndex, Handle<SHVkDescriptorPool> descPool) // We need to update all the material buffers if the materials have changed
{ if (hasChanged)
if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS)
{
SHLOG_WARNING("[SHBatch] Attempted to update transform buffers with an invalid frame index.");
return;
}
// Check if there are even material properties to update
if (!matPropsData)
return;
// Check if any materials have changed
bool hasChanged = false;
for (const auto& material : referencedMatInstances)
{
if (material->HasChanged())
{
hasChanged = true;
break;
}
}
// We need to update all the material buffers if the materials have changed
if (hasChanged)
{
for (auto& dirt : matBufferDirty)
dirt = true;
}
// Check if this frame's buffer is dirty
if (!matBufferDirty[frameIndex])
return;
// Build CPU Buffer
char* propsCurrPtr = matPropsData.get();
for (auto& subBatch : subBatches)
for (const SHRenderable* renderable : subBatch.Renderables)
{
renderable->GetMaterial()->ExportProperties(propsCurrPtr);
propsCurrPtr += singleMatPropAlignedSize;
}
// Transfer to GPU
rebuildMaterialBuffers(frameIndex, descPool);
// This frame is updated
matBufferDirty[frameIndex] = false;
}
void SHBatch::UpdateTransformBuffer(uint32_t frameIndex)
{ {
if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) for (auto& dirt : matBufferDirty)
{ dirt = true;
SHLOG_WARNING("[SHBatch] Attempted to update transform buffers with an invalid frame index.");
return;
}
// Reset Transform Data
transformData.clear();
// Populate on the CPU
for (auto& subBatch : subBatches)
for (const SHRenderable* renderable : subBatch.Renderables)
{
// Transform
auto transform = SHComponentManager::GetComponent<SHTransformComponent>(renderable->GetEID());
if (!transform)
{
SHLOG_WARNING("[SHBatch] Entity contianing a SHRenderable with no SHTransformComponent found!");
transformData.emplace_back();
}
else
{
transformData.emplace_back(transform->GetTRS());
}
}
// Transfer to GPU
if (transformDataBuffer[frameIndex])
transformDataBuffer[frameIndex]->WriteToMemory(transformData.data(), static_cast<uint32_t>(transformData.size() * sizeof(SHMatrix)), 0, 0);
} }
void SHBatch::UpdateEIDBuffer(uint32_t frameIndex) // Check if this frame's buffer is dirty
if (!matBufferDirty[frameIndex])
return;
// Build CPU Buffer
char* propsCurrPtr = matPropsData.get();
for (auto& subBatch : subBatches)
for (auto rendId : subBatch.Renderables)
{
const SHRenderable* renderable = SHComponentManager::GetComponent<SHRenderable>(rendId);
if (renderable)
{
renderable->GetMaterial()->ExportProperties(propsCurrPtr);
}
else
{
SHLOG_WARNING("[SHBatch] Entity with a missing SHRenderable found!");
}
propsCurrPtr += singleMatPropAlignedSize;
}
// Transfer to GPU
rebuildMaterialBuffers(frameIndex, descPool);
// This frame is updated
matBufferDirty[frameIndex] = false;
}
void SHBatch::UpdateTransformBuffer(uint32_t frameIndex)
{
if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS)
{
SHLOG_WARNING("[SHBatch] Attempted to update transform buffers with an invalid frame index.");
return;
}
// Reset Transform Data
transformData.clear();
// Populate on the CPU
for (auto& subBatch : subBatches)
for (auto rendId : subBatch.Renderables)
{
// Transform
auto transform = SHComponentManager::GetComponent<SHTransformComponent>(rendId);
if (transform)
{
transformData.emplace_back(transform->GetTRS());
}
else
{
SHLOG_WARNING("[SHBatch] Entity contianing a SHRenderable with no SHTransformComponent found!");
transformData.emplace_back();
}
}
// Transfer to GPU
if (transformDataBuffer[frameIndex])
transformDataBuffer[frameIndex]->WriteToMemory(transformData.data(), static_cast<uint32_t>(transformData.size() * sizeof(SHMatrix)), 0, 0);
}
void SHBatch::UpdateInstancedIntegerBuffer(uint32_t frameIndex)
{ {
if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS)
{ {
@ -217,224 +246,244 @@ namespace SHADE
} }
// Reset Transform Data // Reset Transform Data
eidData.clear(); instancedIntegerData.clear();
// Populate on the CPU // Populate on the CPU
for (auto& subBatch : subBatches) for (auto& subBatch : subBatches)
for (const SHRenderable* renderable : subBatch.Renderables) for (auto rendId : subBatch.Renderables)
{ {
eidData.emplace_back(renderable->GetEID()); auto* renderable = SHComponentManager::GetComponent<SHRenderable>(rendId);
instancedIntegerData.emplace_back(SHInstancedIntegerData
{
rendId,
renderable->GetLightLayer()
}
);
} }
// Transfer to GPU // Transfer to GPU
if (eidBuffer[frameIndex]) if (instancedIntegerBuffer[frameIndex])
eidBuffer[frameIndex]->WriteToMemory(eidData.data(), static_cast<EntityID>(eidData.size() * sizeof(EntityID)), 0, 0); instancedIntegerBuffer[frameIndex]->WriteToMemory(instancedIntegerData.data(), static_cast<uint32_t>(instancedIntegerData.size() * sizeof(SHInstancedIntegerData)), 0, 0);
} }
void SHBatch::Build(Handle<SHVkLogicalDevice> _device, Handle<SHVkDescriptorPool> descPool, uint32_t frameIndex) void SHBatch::Build(Handle<SHVkLogicalDevice> _device, Handle<SHVkDescriptorPool> descPool, uint32_t frameIndex)
{
if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS)
{ {
if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) SHLOG_WARNING("[SHBatch] Attempted to update build batch buffers with an invalid frame index.");
{ return;
SHLOG_WARNING("[SHBatch] Attempted to update build batch buffers with an invalid frame index.");
return;
}
// Save logical device
device = _device;
// No need to build as there are no changes
if (!isDirty[frameIndex])
return;
// Count number of elements
size_t numTotalElements = 0;
for (const auto& subBatch : subBatches)
{
numTotalElements += subBatch.Renderables.size();
}
// Generate CPU buffers if there are changes
if (isCPUBuffersDirty)
{
// - Draw data
drawData.reserve(subBatches.size());
drawData.clear();
// - Transform data
transformData.reserve(numTotalElements);
transformData.clear();
// - EID data
eidData.reserve(numTotalElements);
eidData.clear();
// - Material Properties Data
const Handle<SHShaderBlockInterface> SHADER_INFO = pipeline->GetPipelineLayout()->GetShaderBlockInterface
(
SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE,
SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA,
vk::ShaderStageFlagBits::eFragment
);
const bool EMPTY_MAT_PROPS = !SHADER_INFO;
Byte matPropTotalBytes = 0;
if (!EMPTY_MAT_PROPS)
{
singleMatPropSize = SHADER_INFO->GetBytesRequired();
singleMatPropAlignedSize = device->PadSSBOSize(static_cast<uint32_t>(singleMatPropSize));
matPropTotalBytes = numTotalElements * singleMatPropAlignedSize;
if (matPropsDataSize < matPropTotalBytes)
{
matPropsData.reset(new char[matPropTotalBytes]);
matPropsDataSize = matPropTotalBytes;
}
}
// Build Sub Batches
uint32_t nextInstanceIndex = 0;
char* propsCurrPtr = matPropsData.get();
for (auto& subBatch : subBatches)
{
// Create command
drawData.emplace_back(vk::DrawIndexedIndirectCommand
{
.indexCount = subBatch.Mesh->IndexCount,
.instanceCount = static_cast<uint32_t>(subBatch.Renderables.size()),
.firstIndex = subBatch.Mesh->FirstIndex,
.vertexOffset = subBatch.Mesh->FirstVertex,
.firstInstance = nextInstanceIndex++
});
// Fill in buffers (CPU)
for (const SHRenderable* renderable : subBatch.Renderables)
{
// Transform
EntityID eid = renderable->GetEID();
auto transform = SHComponentManager::GetComponent_s<SHTransformComponent>(eid);
if (!transform)
{
SHLOG_WARNING("[SHBatch] Entity contianing a SHRenderable with no SHTransformComponent found!");
transformData.emplace_back();
}
else
{
transformData.emplace_back(transform->GetTRS());
}
eidData.emplace_back(eid);
// Material Properties
if (!EMPTY_MAT_PROPS)
{
renderable->GetMaterial()->ExportProperties(propsCurrPtr);
propsCurrPtr += singleMatPropAlignedSize;
}
}
}
// Successfully update CPU buffers
isCPUBuffersDirty = false;
}
// Send all buffered data to the GPU buffers
using BuffUsage = vk::BufferUsageFlagBits;
// - Draw Data
const uint32_t DRAW_DATA_BYTES = static_cast<uint32_t>(drawData.size() * sizeof(vk::DrawIndexedIndirectCommand));
SHVkUtil::EnsureBufferAndCopyHostVisibleData
(
device, drawDataBuffer[frameIndex], drawData.data(), DRAW_DATA_BYTES,
BuffUsage::eIndirectBuffer
);
// - Transform Buffer
const uint32_t TF_DATA_BYTES = static_cast<uint32_t>(transformData.size() * sizeof(SHMatrix));
SHVkUtil::EnsureBufferAndCopyHostVisibleData
(
device, transformDataBuffer[frameIndex], transformData.data(), TF_DATA_BYTES,
BuffUsage::eVertexBuffer
);
const uint32_t EID_DATA_BYTES = static_cast<uint32_t>(eidData.size() * sizeof(EntityID));
SHVkUtil::EnsureBufferAndCopyHostVisibleData
(
device, eidBuffer[frameIndex], eidData.data(), EID_DATA_BYTES,
BuffUsage::eVertexBuffer
);
// - Material Properties Buffer
rebuildMaterialBuffers(frameIndex, descPool);
// Mark this frame as no longer dirty
isDirty[frameIndex] = false;
} }
/*---------------------------------------------------------------------------------*/ // Save logical device
/* SHBatch - Usage Functions */ device = _device;
/*---------------------------------------------------------------------------------*/
void SHBatch::Draw(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex)
{
if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS)
{
SHLOG_WARNING("[SHBatch] Attempted to draw a batch with an invalid frame index.");
return;
}
// Bind all required objects before drawing // No need to build as there are no changes
static std::array<uint32_t, 1> dynamicOffset { 0 }; if (!isDirty[frameIndex])
cmdBuffer->BindPipeline(pipeline); return;
cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::TRANSFORM, transformDataBuffer[frameIndex], 0);
cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::EID, eidBuffer[frameIndex], 0); // Count number of elements
if (matPropsDescSet[frameIndex]) size_t numTotalElements = 0;
for (const auto& subBatch : subBatches)
{
numTotalElements += subBatch.Renderables.size();
}
// Generate CPU buffers if there are changes
if (isCPUBuffersDirty)
{
// - Draw data
drawData.reserve(subBatches.size());
drawData.clear();
// - Transform data
transformData.reserve(numTotalElements);
transformData.clear();
// - EID data
instancedIntegerData.reserve(numTotalElements);
instancedIntegerData.clear();
// - Material Properties Data
const Handle<SHShaderBlockInterface> SHADER_INFO = pipeline->GetPipelineLayout()->GetShaderBlockInterface
(
SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE,
SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA,
vk::ShaderStageFlagBits::eFragment
);
const bool EMPTY_MAT_PROPS = !SHADER_INFO;
Byte matPropTotalBytes = 0;
if (!EMPTY_MAT_PROPS)
{
singleMatPropSize = SHADER_INFO->GetBytesRequired();
singleMatPropAlignedSize = device->PadSSBOSize(static_cast<uint32_t>(singleMatPropSize));
matPropTotalBytes = numTotalElements * singleMatPropAlignedSize;
if (matPropsDataSize < matPropTotalBytes)
{ {
cmdBuffer->BindDescriptorSet matPropsData.reset(new char[matPropTotalBytes]);
( matPropsDataSize = matPropTotalBytes;
matPropsDescSet[frameIndex], }
SH_PIPELINE_TYPE::GRAPHICS, }
SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE,
dynamicOffset // Build Sub Batches
uint32_t nextInstanceIndex = 0;
char* propsCurrPtr = matPropsData.get();
for (auto& subBatch : subBatches)
{
// Create command
const uint32_t CURR_INSTANCES = static_cast<uint32_t>(subBatch.Renderables.size());
drawData.emplace_back(vk::DrawIndexedIndirectCommand
{
.indexCount = subBatch.Mesh->IndexCount,
.instanceCount = CURR_INSTANCES,
.firstIndex = subBatch.Mesh->FirstIndex,
.vertexOffset = subBatch.Mesh->FirstVertex,
.firstInstance = nextInstanceIndex
});
nextInstanceIndex += CURR_INSTANCES;
// Fill in buffers (CPU)
for (auto rendId : subBatch.Renderables)
{
// Transform
auto transform = SHComponentManager::GetComponent_s<SHTransformComponent>(rendId);
if (!transform)
{
SHLOG_WARNING("[SHBatch] Entity contianing a SHRenderable with no SHTransformComponent found!");
transformData.emplace_back();
}
else
{
transformData.emplace_back(transform->GetTRS());
}
const SHRenderable* renderable = SHComponentManager::GetComponent<SHRenderable>(rendId);
instancedIntegerData.emplace_back(SHInstancedIntegerData
{
rendId,
renderable->GetLightLayer()
}
); );
}
cmdBuffer->DrawMultiIndirect(drawDataBuffer[frameIndex], static_cast<uint32_t>(drawData.size()));
}
/*---------------------------------------------------------------------------------*/ // Material Properties
/* SHBatch - Helper Functions */ if (!EMPTY_MAT_PROPS)
/*---------------------------------------------------------------------------------*/ {
void SHBatch::setAllDirtyFlags() if (renderable)
{
for (bool& dirt : isDirty)
dirt = true;
isCPUBuffersDirty = true;
}
void SHBatch::rebuildMaterialBuffers(uint32_t frameIndex, Handle<SHVkDescriptorPool> descPool)
{
if (matPropsData)
{
SHVkUtil::EnsureBufferAndCopyHostVisibleData
(
device, matPropsBuffer[frameIndex], matPropsData.get(), static_cast<uint32_t>(matPropsDataSize),
vk::BufferUsageFlagBits::eStorageBuffer
);
if (!matPropsDescSet[frameIndex])
{ {
matPropsDescSet[frameIndex] = descPool->Allocate renderable->GetMaterial()->ExportProperties(propsCurrPtr);
(
{ SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE] },
{ 0 }
);
} }
std::array<Handle<SHVkBuffer>, 1> bufferList = { matPropsBuffer[frameIndex] }; else
matPropsDescSet[frameIndex]->ModifyWriteDescBuffer {
( SHLOG_WARNING("[SHBatch] Entity with a missing SHRenderable found!");
SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, }
SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA, propsCurrPtr += singleMatPropAlignedSize;
bufferList, }
0, static_cast<uint32_t>(matPropsDataSize)
);
matPropsDescSet[frameIndex]->UpdateDescriptorSetBuffer
(
SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE,
SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA
);
} }
}
// Successfully update CPU buffers
isCPUBuffersDirty = false;
} }
// Send all buffered data to the GPU buffers
using BuffUsage = vk::BufferUsageFlagBits;
// - Draw Data
const uint32_t DRAW_DATA_BYTES = static_cast<uint32_t>(drawData.size() * sizeof(vk::DrawIndexedIndirectCommand));
SHVkUtil::EnsureBufferAndCopyHostVisibleData
(
device, drawDataBuffer[frameIndex], drawData.data(), DRAW_DATA_BYTES,
BuffUsage::eIndirectBuffer
);
// - Transform Buffer
const uint32_t TF_DATA_BYTES = static_cast<uint32_t>(transformData.size() * sizeof(SHMatrix));
SHVkUtil::EnsureBufferAndCopyHostVisibleData
(
device, transformDataBuffer[frameIndex], transformData.data(), TF_DATA_BYTES,
BuffUsage::eVertexBuffer
);
const uint32_t EID_DATA_BYTES = static_cast<uint32_t>(instancedIntegerData.size() * sizeof(SHInstancedIntegerData));
SHVkUtil::EnsureBufferAndCopyHostVisibleData
(
device, instancedIntegerBuffer[frameIndex], instancedIntegerData.data(), EID_DATA_BYTES,
BuffUsage::eVertexBuffer
);
// - Material Properties Buffer
rebuildMaterialBuffers(frameIndex, descPool);
// Mark this frame as no longer dirty
isDirty[frameIndex] = false;
}
/*---------------------------------------------------------------------------------*/
/* SHBatch - Usage Functions */
/*---------------------------------------------------------------------------------*/
void SHBatch::Draw(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex)
{
if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS)
{
SHLOG_WARNING("[SHBatch] Attempted to draw a batch with an invalid frame index.");
return;
}
// Bind all required objects before drawing
static std::array<uint32_t, 1> dynamicOffset{ 0 };
cmdBuffer->BindPipeline(pipeline);
cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::TRANSFORM, transformDataBuffer[frameIndex], 0);
cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::INTEGER_DATA, instancedIntegerBuffer[frameIndex], 0);
if (matPropsDescSet[frameIndex])
{
cmdBuffer->BindDescriptorSet
(
matPropsDescSet[frameIndex],
SH_PIPELINE_TYPE::GRAPHICS,
SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE,
dynamicOffset
);
}
cmdBuffer->DrawMultiIndirect(drawDataBuffer[frameIndex], static_cast<uint32_t>(drawData.size()));
}
/*---------------------------------------------------------------------------------*/
/* SHBatch - Helper Functions */
/*---------------------------------------------------------------------------------*/
void SHBatch::setAllDirtyFlags()
{
for (bool& dirt : isDirty)
dirt = true;
isCPUBuffersDirty = true;
}
void SHBatch::rebuildMaterialBuffers(uint32_t frameIndex, Handle<SHVkDescriptorPool> descPool)
{
if (matPropsData)
{
SHVkUtil::EnsureBufferAndCopyHostVisibleData
(
device, matPropsBuffer[frameIndex], matPropsData.get(), static_cast<uint32_t>(matPropsDataSize),
vk::BufferUsageFlagBits::eStorageBuffer
);
if (!matPropsDescSet[frameIndex])
{
matPropsDescSet[frameIndex] = descPool->Allocate
(
{ SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE] },
{ 0 }
);
}
std::array<Handle<SHVkBuffer>, 1> bufferList = { matPropsBuffer[frameIndex] };
matPropsDescSet[frameIndex]->ModifyWriteDescBuffer
(
SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE,
SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA,
bufferList,
0, static_cast<uint32_t>(matPropsDataSize)
);
matPropsDescSet[frameIndex]->UpdateDescriptorSetBuffer
(
SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE,
SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA
);
}
}
} }

View File

@ -23,6 +23,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Math/SHMatrix.h" #include "Math/SHMatrix.h"
#include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h" #include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h"
#include "ECS_Base/SHECSMacros.h" #include "ECS_Base/SHECSMacros.h"
#include "Graphics/MiddleEnd/Interface/SHInstancedIntegerData.h"
namespace SHADE namespace SHADE
{ {
@ -55,7 +56,7 @@ namespace SHADE
/* Data Members */ /* Data Members */
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
Handle<SHMesh> Mesh; Handle<SHMesh> Mesh;
std::unordered_set<const SHRenderable*> Renderables; std::unordered_set<EntityID> Renderables;
}; };
/***********************************************************************************/ /***********************************************************************************/
/*! /*!
@ -79,7 +80,7 @@ namespace SHADE
void Clear(); void Clear();
void UpdateMaterialBuffer(uint32_t frameIndex, Handle<SHVkDescriptorPool> descPool); void UpdateMaterialBuffer(uint32_t frameIndex, Handle<SHVkDescriptorPool> descPool);
void UpdateTransformBuffer(uint32_t frameIndex); void UpdateTransformBuffer(uint32_t frameIndex);
void UpdateEIDBuffer(uint32_t frameIndex); void UpdateInstancedIntegerBuffer(uint32_t frameIndex);
void Build(Handle<SHVkLogicalDevice> device, Handle<SHVkDescriptorPool> descPool, uint32_t frameIndex) ; void Build(Handle<SHVkLogicalDevice> device, Handle<SHVkDescriptorPool> descPool, uint32_t frameIndex) ;
void Draw(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex); void Draw(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex);
@ -111,7 +112,7 @@ namespace SHADE
// CPU Buffers // CPU Buffers
std::vector<vk::DrawIndexedIndirectCommand> drawData; std::vector<vk::DrawIndexedIndirectCommand> drawData;
std::vector<SHMatrix> transformData; std::vector<SHMatrix> transformData;
std::vector<EntityID> eidData; std::vector<SHInstancedIntegerData> instancedIntegerData;
std::unique_ptr<char> matPropsData; std::unique_ptr<char> matPropsData;
Byte matPropsDataSize = 0; Byte matPropsDataSize = 0;
Byte singleMatPropAlignedSize = 0; Byte singleMatPropAlignedSize = 0;
@ -120,7 +121,7 @@ namespace SHADE
// GPU Buffers // GPU Buffers
TripleBuffer drawDataBuffer; TripleBuffer drawDataBuffer;
TripleBuffer transformDataBuffer; TripleBuffer transformDataBuffer;
TripleBuffer eidBuffer; TripleBuffer instancedIntegerBuffer;
TripleBuffer matPropsBuffer; TripleBuffer matPropsBuffer;
TripleDescSet matPropsDescSet; TripleDescSet matPropsDescSet;

View File

@ -85,7 +85,7 @@ namespace SHADE
{ {
batch.UpdateMaterialBuffer(frameIndex, descPool); batch.UpdateMaterialBuffer(frameIndex, descPool);
batch.UpdateTransformBuffer(frameIndex); batch.UpdateTransformBuffer(frameIndex);
batch.UpdateEIDBuffer(frameIndex); batch.UpdateInstancedIntegerBuffer(frameIndex);
} }
} }

View File

@ -4,6 +4,8 @@
#include "Graphics/Pipeline/SHPipelineState.h" #include "Graphics/Pipeline/SHPipelineState.h"
#include "Graphics/Pipeline/SHVkPipelineLayout.h" #include "Graphics/Pipeline/SHVkPipelineLayout.h"
#include "Graphics/Descriptors/SHVkDescriptorSetLayout.h" #include "Graphics/Descriptors/SHVkDescriptorSetLayout.h"
#include "Graphics/MiddleEnd/Lights/SHLightData.h"
#include "Tools/SHUtilities.h"
namespace SHADE namespace SHADE
{ {
@ -45,16 +47,35 @@ namespace SHADE
// For global data (generic data and textures) // For global data (generic data and textures)
Handle<SHVkDescriptorSetLayout> staticGlobalLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::STATIC_GLOBALS,{ genericDataBinding, texturesBinding }); Handle<SHVkDescriptorSetLayout> staticGlobalLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::STATIC_GLOBALS,{ genericDataBinding, texturesBinding });
SHVkDescriptorSetLayout::Binding lightBinding std::vector<SHVkDescriptorSetLayout::Binding> lightBindings{};
for (uint32_t i = 0; i < SHUtilities::ToUnderlying(SH_LIGHT_TYPE::NUM_TYPES); ++i)
{ {
.Type = vk::DescriptorType::eStorageBufferDynamic, lightBindings.push_back (SHVkDescriptorSetLayout::Binding
.Stage = vk::ShaderStageFlagBits::eFragment, {
.BindPoint = SHGraphicsConstants::DescriptorSetBindings::LIGHTS_DATA, .Type = vk::DescriptorType::eStorageBufferDynamic,
.DescriptorCount = 1, .Stage = vk::ShaderStageFlagBits::eFragment,
}; .BindPoint = i,
.DescriptorCount = 1,
});
}
//SHVkDescriptorSetLayout::Binding pointLightBinding
//{
// .Type = vk::DescriptorType::eStorageBufferDynamic,
// .Stage = vk::ShaderStageFlagBits::eFragment,
// .BindPoint = SHGraphicsConstants::DescriptorSetBindings::POINT_LIGHT_DATA,
// .DescriptorCount = 1,
//};
//SHVkDescriptorSetLayout::Binding spotLightBinding
//{
// .Type = vk::DescriptorType::eStorageBufferDynamic,
// .Stage = vk::ShaderStageFlagBits::eFragment,
// .BindPoint = SHGraphicsConstants::DescriptorSetBindings::SPOT_LIGHT_DATA,
// .DescriptorCount = 1,
//};
// For Dynamic global data (lights) // For Dynamic global data (lights)
Handle<SHVkDescriptorSetLayout> dynamicGlobalLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, { lightBinding }); Handle<SHVkDescriptorSetLayout> dynamicGlobalLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, lightBindings);
SHVkDescriptorSetLayout::Binding cameraDataBinding SHVkDescriptorSetLayout::Binding cameraDataBinding
{ {
@ -94,7 +115,7 @@ namespace SHADE
defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Normals at binding 2 defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Normals at binding 2
defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Tangents at binding 3 defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Tangents at binding 3
defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::MAT_4D) }); // Transform at binding 4 - 7 (4 slots) defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::MAT_4D) }); // Transform at binding 4 - 7 (4 slots)
defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::UINT32_1D) }); // EID at binding 8 defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::UINT32_2D) }); // Instanced integer data at index 8
} }
void SHGraphicsGlobalData::Init(Handle<SHVkLogicalDevice> logicalDevice) noexcept void SHGraphicsGlobalData::Init(Handle<SHVkLogicalDevice> logicalDevice) noexcept

View File

@ -94,14 +94,32 @@ namespace SHADE
/***************************************************************************/ /***************************************************************************/
static constexpr uint32_t IMAGE_AND_SAMPLERS_DATA = 1; static constexpr uint32_t IMAGE_AND_SAMPLERS_DATA = 1;
/***************************************************************************/ ///***************************************************************************/
/*! ///*!
\brief // \brief
DescriptorSet binding for lights. // DescriptorSet binding for directional lights.
*/ //*/
/***************************************************************************/ ///***************************************************************************/
static constexpr uint32_t LIGHTS_DATA = 0; //static constexpr uint32_t DIRECTIONAL_LIGHT_DATA = 0;
///***************************************************************************/
///*!
// \brief
// DescriptorSet binding for directional lights.
//*/
///***************************************************************************/
//static constexpr uint32_t POINT_LIGHT_DATA = 1;
///***************************************************************************/
///*!
// \brief
// DescriptorSet binding for directional lights.
//*/
///***************************************************************************/
//static constexpr uint32_t SPOT_LIGHT_DATA = 2;
/***************************************************************************/ /***************************************************************************/
/*! /*!
@ -164,7 +182,7 @@ namespace SHADE
Vertex buffer bindings for the eid buffer. Vertex buffer bindings for the eid buffer.
*/ */
/***************************************************************************/ /***************************************************************************/
static constexpr uint32_t EID = 5; static constexpr uint32_t INTEGER_DATA = 5;
}; };

View File

@ -36,6 +36,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Graphics/Images/SHVkSampler.h" #include "Graphics/Images/SHVkSampler.h"
#include "Assets/Asset Types/SHTextureAsset.h" #include "Assets/Asset Types/SHTextureAsset.h"
#include "Graphics/MiddleEnd/Interface/SHMousePickSystem.h" #include "Graphics/MiddleEnd/Interface/SHMousePickSystem.h"
#include "Graphics/MiddleEnd/Lights/SHLightingSubSystem.h"
namespace SHADE namespace SHADE
{ {
@ -117,21 +118,8 @@ namespace SHADE
graphicsTexCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); graphicsTexCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY);
// TODO: This is VERY temporarily here until a more solid resource management system is implemented shaderModuleLibrary.ImportAllShaderSource(device);
shaderSourceLibrary.Init("../../TempShaderFolder/"); shaderModuleLibrary.ReflectAllShaderModules();
shaderSourceLibrary.LoadShader(0, "TestCubeVs.glsl", SH_SHADER_TYPE::VERTEX, true);
shaderSourceLibrary.LoadShader(1, "TestCubeFs.glsl", SH_SHADER_TYPE::FRAGMENT, true);
shaderSourceLibrary.LoadShader(2, "KirschCs.glsl", SH_SHADER_TYPE::COMPUTE, true);
shaderModuleLibrary.ImportFromSourceLibrary(device, shaderSourceLibrary);
auto cubeVS = shaderModuleLibrary.GetShaderModule("TestCubeVs.glsl");
auto cubeFS = shaderModuleLibrary.GetShaderModule("TestCubeFs.glsl");
auto greyscale = shaderModuleLibrary.GetShaderModule("KirschCs.glsl");
cubeVS->Reflect();
cubeFS->Reflect();
greyscale->Reflect();
} }
void SHGraphicsSystem::InitSceneRenderGraph(void) noexcept void SHGraphicsSystem::InitSceneRenderGraph(void) noexcept
@ -172,21 +160,32 @@ namespace SHADE
// Initialize world render graph // Initialize world render graph
worldRenderGraph->Init(device, swapchain); worldRenderGraph->Init(device, swapchain);
worldRenderGraph->AddResource("Scene Pre-Process", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second); worldRenderGraph->AddResource("Scene Pre-Process", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second);
worldRenderGraph->AddResource("Scene", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second); worldRenderGraph->AddResource("Scene", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second);
worldRenderGraph->AddResource("Depth Buffer", { SH_ATT_DESC_TYPE_FLAGS::DEPTH_STENCIL }, windowDims.first, windowDims.second, vk::Format::eD32SfloatS8Uint); worldRenderGraph->AddResource("Depth Buffer", { SH_ATT_DESC_TYPE_FLAGS::DEPTH_STENCIL }, windowDims.first, windowDims.second, vk::Format::eD32SfloatS8Uint);
worldRenderGraph->AddResource("Entity ID", { SH_ATT_DESC_TYPE_FLAGS::COLOR }, windowDims.first, windowDims.second, vk::Format::eR32Uint, 1, vk::ImageUsageFlagBits::eTransferSrc); worldRenderGraph->AddResource("Entity ID", { SH_ATT_DESC_TYPE_FLAGS::COLOR }, windowDims.first, windowDims.second, vk::Format::eR32Uint, 1, vk::ImageUsageFlagBits::eTransferSrc);
worldRenderGraph->AddResource("Light Layer Indices", { SH_ATT_DESC_TYPE_FLAGS::COLOR }, windowDims.first, windowDims.second, vk::Format::eR32Uint, 1, vk::ImageUsageFlagBits::eTransferSrc);
auto node = worldRenderGraph->AddNode("G-Buffer", { "Entity ID", "Depth Buffer", "Scene", "Scene Pre-Process"}, {}); // no predecessors auto gBufferNode = worldRenderGraph->AddNode("G-Buffer", { "Light Layer Indices", "Entity ID", "Depth Buffer", "Scene", "Scene Pre-Process"}, {}); // no predecessors
auto gBufferSubpass = gBufferNode->AddSubpass("G-Buffer Write");
gBufferSubpass->AddColorOutput("Scene Pre-Process");
gBufferSubpass->AddColorOutput("Entity ID");
gBufferSubpass->AddColorOutput("Light Layer Indices");
gBufferSubpass->AddDepthOutput("Depth Buffer", SH_ATT_DESC_TYPE_FLAGS::DEPTH_STENCIL);
//First subpass to write to G-Buffer //// kirsch
auto gBufferWriteSubpass = node->AddSubpass("G-Buffer Write"); //auto kirschShader = shaderModuleLibrary.GetShaderModule("KirschCs");
gBufferWriteSubpass->AddColorOutput("Scene Pre-Process"); //gBufferNode->AddNodeCompute(kirschShader, { "Scene Pre-Process", "Scene" });
gBufferWriteSubpass->AddColorOutput("Entity ID");
gBufferWriteSubpass->AddDepthOutput("Depth Buffer", SH_ATT_DESC_TYPE_FLAGS::DEPTH_STENCIL); // copy
auto pureCopyShader = shaderModuleLibrary.GetBuiltInShaderModule("PureCopy_CS");
gBufferNode->AddNodeCompute(pureCopyShader, { "Scene Pre-Process", "Scene" });
auto dummyNode = worldRenderGraph->AddNode("Dummy Pass", { "Scene" }, {"G-Buffer"}); // no predecessors
auto dummySubpass = dummyNode->AddSubpass("Dummy Subpass");
dummySubpass->AddInput("Scene");
auto greyscale = shaderModuleLibrary.GetShaderModule("KirschCs.glsl");
node->AddNodeCompute (greyscale, {"Scene Pre-Process", "Scene"});
// Generate world render graph // Generate world render graph
worldRenderGraph->Generate(); worldRenderGraph->Generate();
@ -197,11 +196,10 @@ namespace SHADE
worldRenderer->SetCameraDirector(cameraSystem->CreateDirector()); worldRenderer->SetCameraDirector(cameraSystem->CreateDirector());
auto cubeVS = shaderModuleLibrary.GetShaderModule("TestCubeVs.glsl"); auto cubeVS = shaderModuleLibrary.GetBuiltInShaderModule("TestCube_VS");
auto cubeFS = shaderModuleLibrary.GetShaderModule("TestCubeFs.glsl"); auto cubeFS = shaderModuleLibrary.GetBuiltInShaderModule("TestCube_FS");
defaultMaterial = AddMaterial(cubeVS, cubeFS, gBufferWriteSubpass);
defaultMaterial = AddMaterial(cubeVS, cubeFS, gBufferSubpass);
} }
void SHGraphicsSystem::InitMiddleEnd(void) noexcept void SHGraphicsSystem::InitMiddleEnd(void) noexcept
@ -231,11 +229,15 @@ namespace SHADE
for (uint32_t i = 0; i < swapchain->GetNumImages(); ++i) for (uint32_t i = 0; i < swapchain->GetNumImages(); ++i)
cmdPools.push_back(renderContext.GetFrameData(i).cmdPoolHdls[0]); cmdPools.push_back(renderContext.GetFrameData(i).cmdPoolHdls[0]);
// Mouse picking system for the editor (Will still run with editor disabled)
mousePickSystem->Init(device, cmdPools, worldRenderGraph->GetRenderGraphResource("Entity ID")); mousePickSystem->Init(device, cmdPools, worldRenderGraph->GetRenderGraphResource("Entity ID"));
// Register the post offscreen render to the system // Register the post offscreen render to the system
postOffscreenRender = resourceManager.Create<SHPostOffscreenRenderSystem>(); postOffscreenRender = resourceManager.Create<SHPostOffscreenRenderSystem>();
postOffscreenRender->Init(device, worldRenderGraph->GetRenderGraphResource("Scene"), descPool); postOffscreenRender->Init(device, worldRenderGraph->GetRenderGraphResource("Scene"), descPool);
lightingSubSystem = resourceManager.Create<SHLightingSubSystem>();
lightingSubSystem->Init(device, descPool);
} }
#ifdef SHEDITOR #ifdef SHEDITOR
@ -353,10 +355,12 @@ namespace SHADE
// Begin recording the command buffer // Begin recording the command buffer
currentCmdBuffer->BeginRecording(); currentCmdBuffer->BeginRecording();
// set viewport and scissor
uint32_t w = static_cast<uint32_t>(viewports[vpIndex]->GetWidth()); uint32_t w = static_cast<uint32_t>(viewports[vpIndex]->GetWidth());
uint32_t h = static_cast<uint32_t>(viewports[vpIndex]->GetHeight()); uint32_t h = static_cast<uint32_t>(viewports[vpIndex]->GetHeight());
currentCmdBuffer->SetViewportScissor (static_cast<float>(w), static_cast<float>(h), w, h); currentCmdBuffer->SetViewportScissor (static_cast<float>(w), static_cast<float>(h), w, h);
// Force set the pipeline layout
currentCmdBuffer->ForceSetPipelineLayout(SHGraphicsGlobalData::GetDummyPipelineLayout(), SH_PIPELINE_TYPE::GRAPHICS); currentCmdBuffer->ForceSetPipelineLayout(SHGraphicsGlobalData::GetDummyPipelineLayout(), SH_PIPELINE_TYPE::GRAPHICS);
// Bind all the buffers required for meshes // Bind all the buffers required for meshes
@ -368,6 +372,8 @@ namespace SHADE
currentCmdBuffer->BindIndexBuffer(buffer, 0); currentCmdBuffer->BindIndexBuffer(buffer, 0);
} }
// Bind the descriptor set for lights
lightingSubSystem->Run(currentCmdBuffer, frameIndex);
// Bind textures // Bind textures
auto textureDescSet = texLibrary.GetTextureDescriptorSetGroup(); auto textureDescSet = texLibrary.GetTextureDescriptorSetGroup();
@ -401,7 +407,7 @@ namespace SHADE
renderers[renIndex]->UpdateDataAndBind(currentCmdBuffer, frameIndex); renderers[renIndex]->UpdateDataAndBind(currentCmdBuffer, frameIndex);
#endif #endif
// Draw first // Draw the scene
renderers[renIndex]->Draw(frameIndex, descPool); renderers[renIndex]->Draw(frameIndex, descPool);
// End the command buffer recording // End the command buffer recording
@ -673,14 +679,14 @@ namespace SHADE
auto& renderables = SHComponentManager::GetDense<SHRenderable>(); auto& renderables = SHComponentManager::GetDense<SHRenderable>();
for (auto& renderable : renderables) for (auto& renderable : renderables)
{ {
if (!renderable.WasMaterialChanged()) if (!renderable.HasChanged())
continue; continue;
// Remove from old material's SuperBatch // Remove from old material's SuperBatch
Handle<SHMaterial> prevMaterial = renderable.GetPrevMaterial(); Handle<SHMaterialInstance> prevMaterial = renderable.GetPrevMaterial();
if (prevMaterial) if (prevMaterial)
{ {
Handle<SHSuperBatch> oldSuperBatch = prevMaterial->GetPipeline()->GetPipelineState().GetSubpass()->GetSuperBatch(); Handle<SHSuperBatch> oldSuperBatch = prevMaterial->GetBaseMaterial()->GetPipeline()->GetPipelineState().GetSubpass()->GetSuperBatch();
oldSuperBatch->Remove(&renderable); oldSuperBatch->Remove(&renderable);
} }
@ -761,5 +767,10 @@ namespace SHADE
} }
Handle<SHRenderGraphNode> SHGraphicsSystem::GetPrimaryRenderpass() const noexcept
{
return worldRenderGraph->GetNode(G_BUFFER_RENDER_GRAPH_NODE_NAME.data());
}
#pragma endregion MISC #pragma endregion MISC
} }

View File

@ -25,13 +25,13 @@ of DigiPen Institute of Technology is prohibited.
#include "ECS_Base/System/SHSystemRoutine.h" #include "ECS_Base/System/SHSystemRoutine.h"
#include "Graphics/Descriptors/SHVkDescriptorPool.h" #include "Graphics/Descriptors/SHVkDescriptorPool.h"
#include "Graphics/RenderGraph/SHRenderGraph.h" #include "Graphics/RenderGraph/SHRenderGraph.h"
#include "Graphics/MiddleEnd/Shaders/SHShaderSourceLibrary.h"
#include "Graphics/MiddleEnd/Shaders/SHShaderModuleLibrary.h" #include "Graphics/MiddleEnd/Shaders/SHShaderModuleLibrary.h"
#include "SHMeshLibrary.h" #include "SHMeshLibrary.h"
#include "Graphics/MiddleEnd/Materials/SHMaterialInstanceCache.h" #include "Graphics/MiddleEnd/Materials/SHMaterialInstanceCache.h"
#include "../Textures/SHTextureLibrary.h" #include "../Textures/SHTextureLibrary.h"
#include "../Textures/SHVkSamplerCache.h" #include "../Textures/SHVkSamplerCache.h"
#include "Graphics/MiddleEnd/Interface/SHPostOffscreenRenderSystem.h" #include "Graphics/MiddleEnd/Interface/SHPostOffscreenRenderSystem.h"
#include "Graphics/MiddleEnd/Lights/SHLightingSubSystem.h"
namespace SHADE namespace SHADE
{ {
@ -135,7 +135,7 @@ namespace SHADE
void RemoveViewport(Handle<SHViewport> viewport); void RemoveViewport(Handle<SHViewport> viewport);
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
/* Material Creation Functions */ /* Material Functions */
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
Handle<SHMaterial> AddMaterial(Handle<SHVkShaderModule> vertShader, Handle<SHVkShaderModule> fragShader, Handle<SHSubpass> subpass); Handle<SHMaterial> AddMaterial(Handle<SHVkShaderModule> vertShader, Handle<SHVkShaderModule> fragShader, Handle<SHSubpass> subpass);
void RemoveMaterial(Handle<SHMaterial> material); void RemoveMaterial(Handle<SHMaterial> material);
@ -143,6 +143,8 @@ namespace SHADE
Handle<SHMaterialInstance> AddOrGetBaseMaterialInstance(Handle<SHMaterial> material); Handle<SHMaterialInstance> AddOrGetBaseMaterialInstance(Handle<SHMaterial> material);
Handle<SHMaterialInstance> AddMaterialInstanceCopy(Handle<SHMaterialInstance> materialInst); Handle<SHMaterialInstance> AddMaterialInstanceCopy(Handle<SHMaterialInstance> materialInst);
void RemoveMaterialInstance(Handle<SHMaterialInstance> materialInstance); void RemoveMaterialInstance(Handle<SHMaterialInstance> materialInstance);
Handle<SHMaterial> GetDefaultMaterial() { return defaultMaterial; }
Handle<SHMaterialInstance> GetDefaultMaterialInstance() { return AddOrGetBaseMaterialInstance(defaultMaterial); }
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
/* Mesh Registration Functions */ /* Mesh Registration Functions */
@ -286,12 +288,17 @@ namespace SHADE
#endif #endif
Handle<SHMousePickSystem> GetMousePickSystem(void) const noexcept {return mousePickSystem;}; Handle<SHMousePickSystem> GetMousePickSystem(void) const noexcept {return mousePickSystem;};
Handle<SHPostOffscreenRenderSystem> GetPostOffscreenRenderSystem(void) const noexcept {return postOffscreenRender;}; Handle<SHPostOffscreenRenderSystem> GetPostOffscreenRenderSystem(void) const noexcept {return postOffscreenRender;};
Handle<SHRenderGraphNode> GetPrimaryRenderpass() const noexcept;
//SHRenderGraph const& GetRenderGraph(void) const noexcept; //SHRenderGraph const& GetRenderGraph(void) const noexcept;
//Handle<SHVkRenderpass> GetRenderPass() const { return renderPass; } //Handle<SHVkRenderpass> GetRenderPass() const { return renderPass; }
private: private:
/*-----------------------------------------------------------------------------*/
/* Constants */
/*-----------------------------------------------------------------------------*/
static constexpr std::string_view G_BUFFER_RENDER_GRAPH_NODE_NAME = "G-Buffer";
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
/* Data Members */ /* Data Members */
@ -318,7 +325,7 @@ namespace SHADE
SHTextureLibrary texLibrary; SHTextureLibrary texLibrary;
SHSamplerCache samplerCache; SHSamplerCache samplerCache;
SHMaterialInstanceCache materialInstanceCache; SHMaterialInstanceCache materialInstanceCache;
// Viewports // Viewports
#ifdef SHEDITOR #ifdef SHEDITOR
Handle<SHViewport> editorViewport; Handle<SHViewport> editorViewport;
Handle<SHRenderer> editorRenderer; Handle<SHRenderer> editorRenderer;
@ -338,9 +345,7 @@ namespace SHADE
// Temp Cameras // Temp Cameras
Handle<SHCamera> worldCamera; Handle<SHCamera> worldCamera;
Handle<SHCamera> screenCamera; Handle<SHCamera> screenCamera;
// TODO: Temporary only until resource library from Xiao Qi is implemented
SHShaderSourceLibrary shaderSourceLibrary;
SHShaderModuleLibrary shaderModuleLibrary; SHShaderModuleLibrary shaderModuleLibrary;
// Temp Materials // Temp Materials
@ -351,6 +356,7 @@ namespace SHADE
// Sub systems // Sub systems
Handle<SHMousePickSystem> mousePickSystem; Handle<SHMousePickSystem> mousePickSystem;
Handle<SHPostOffscreenRenderSystem> postOffscreenRender; Handle<SHPostOffscreenRenderSystem> postOffscreenRender;
Handle<SHLightingSubSystem> lightingSubSystem;
uint32_t resizeWidth; uint32_t resizeWidth;
uint32_t resizeHeight; uint32_t resizeHeight;

View File

@ -0,0 +1,12 @@
#pragma once
#include "ECS_Base/SHECSMacros.h"
namespace SHADE
{
struct SHInstancedIntegerData
{
EntityID eid;
uint32_t lightLayer;
};
}

View File

@ -4,6 +4,8 @@
#include "Graphics/Pipeline/SHVkPipeline.h" #include "Graphics/Pipeline/SHVkPipeline.h"
#include "SHGraphicsConstants.h" #include "SHGraphicsConstants.h"
#include "Graphics/Shaders/BlockInterface/SHShaderBlockInterface.h" #include "Graphics/Shaders/BlockInterface/SHShaderBlockInterface.h"
#include "Math/Vector/SHVec3.h"
#include "Math/Vector/SHVec4.h"
namespace SHADE namespace SHADE
{ {
@ -23,7 +25,7 @@ namespace SHADE
} }
// Allocate memory for properties // Allocate memory for properties
const Handle<SHShaderBlockInterface> SHADER_INFO = getShaderBlockInterface(); const Handle<SHShaderBlockInterface> SHADER_INFO = GetShaderBlockInterface();
propMemorySize = SHADER_INFO ? SHADER_INFO->GetBytesRequired() : 0; propMemorySize = SHADER_INFO ? SHADER_INFO->GetBytesRequired() : 0;
if (propMemorySize <= 0) if (propMemorySize <= 0)
{ {
@ -49,6 +51,22 @@ namespace SHADE
// Reset all the properties to default values // Reset all the properties to default values
if (propMemory) if (propMemory)
memset(propMemory.get(), 0, propMemorySize); memset(propMemory.get(), 0, propMemorySize);
// Initialize Vectors to all 1.0 by default
const Handle<SHShaderBlockInterface> SHADER_INFO = GetShaderBlockInterface();
for (int i = 0; i < SHADER_INFO->GetVariableCount(); ++i)
{
const auto& VAR = SHADER_INFO->GetVariable(i);
switch (VAR->type)
{
case SHShaderBlockInterface::Variable::Type::VECTOR3:
setPropertyUnsafe(VAR->offset, SHVec3::One);
break;
case SHShaderBlockInterface::Variable::Type::VECTOR4:
setPropertyUnsafe(VAR->offset, SHVec4::One);
break;
}
}
} }
void SHMaterial::ExportProperties(void* dest) const noexcept void SHMaterial::ExportProperties(void* dest) const noexcept
@ -59,14 +77,14 @@ namespace SHADE
size_t SHMaterial::GetPropertiesMemorySize() const noexcept size_t SHMaterial::GetPropertiesMemorySize() const noexcept
{ {
const Handle<SHShaderBlockInterface> SHADER_INFO = getShaderBlockInterface(); const Handle<SHShaderBlockInterface> SHADER_INFO = GetShaderBlockInterface();
return SHADER_INFO ? SHADER_INFO->GetBytesRequired() : 0; return SHADER_INFO ? SHADER_INFO->GetBytesRequired() : 0;
} }
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Helper Functions */ /* Helper Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
Handle<SHShaderBlockInterface> SHMaterial::getShaderBlockInterface() const noexcept Handle<SHShaderBlockInterface> SHMaterial::GetShaderBlockInterface() const noexcept
{ {
return pipeline->GetPipelineLayout()->GetShaderBlockInterface return pipeline->GetPipelineLayout()->GetShaderBlockInterface
( (

View File

@ -50,13 +50,24 @@ namespace SHADE
template<typename T> template<typename T>
void SetProperty(const std::string& key, const T& value); void SetProperty(const std::string& key, const T& value);
template<typename T> template<typename T>
void SetProperty(uint32_t memOffset, const T& value);
template<typename T>
T& GetProperty(const std::string& key); T& GetProperty(const std::string& key);
template<typename T> template<typename T>
const T& GetProperty(const std::string& key) const; const T& GetProperty(const std::string& key) const;
template<typename T>
T& GetProperty(uint32_t memOffset);
template<typename T>
const T& GetProperty(uint32_t memOffset) const;
void ResetProperties(); void ResetProperties();
void ExportProperties(void* dest) const noexcept; void ExportProperties(void* dest) const noexcept;
Byte GetPropertiesMemorySize() const noexcept; Byte GetPropertiesMemorySize() const noexcept;
/*-----------------------------------------------------------------------------*/
/* Query Functions */
/*-----------------------------------------------------------------------------*/
Handle<SHShaderBlockInterface> GetShaderBlockInterface() const noexcept;
private: private:
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
/* Data Members */ /* Data Members */
@ -68,7 +79,8 @@ namespace SHADE
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
/* Helper Functions */ /* Helper Functions */
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
Handle<SHShaderBlockInterface> getShaderBlockInterface() const noexcept; template<typename T>
inline void setPropertyUnsafe(uint32_t memOffset, const T& value); // SetProperty() but without checks
}; };
} }

View File

@ -25,7 +25,7 @@ namespace SHADE
template<typename T> template<typename T>
void SHMaterial::SetProperty(const std::string& key, const T& value) void SHMaterial::SetProperty(const std::string& key, const T& value)
{ {
const auto SHADER_INFO = getShaderBlockInterface(); const auto SHADER_INFO = GetShaderBlockInterface();
const auto PROP_INFO = SHADER_INFO->GetVariable(key); const auto PROP_INFO = SHADER_INFO->GetVariable(key);
if (PROP_INFO == nullptr) if (PROP_INFO == nullptr)
{ {
@ -33,21 +33,32 @@ namespace SHADE
} }
// Get offset and modify the memory directly // Get offset and modify the memory directly
T* dataPtr = propMemory.get() + PROP_INFO->offset; T* dataPtr = reinterpret_cast<T*>(propMemory.get() + PROP_INFO->offset);
*dataPtr = value; *dataPtr = value;
} }
template<typename T>
void SHMaterial::SetProperty(uint32_t memOffset, const T& value)
{
// Check if out of bounds
if (memOffset + sizeof(T) > propMemorySize)
throw std::invalid_argument("Attempted to set an invalid property!");
// Set
setPropertyUnsafe(memOffset, value);
}
template<typename T> template<typename T>
T& SHMaterial::GetProperty(const std::string& key) T& SHMaterial::GetProperty(const std::string& key)
{ {
const auto SHADER_INFO = getShaderBlockInterface(); const auto SHADER_INFO = GetShaderBlockInterface();
const auto PROP_INFO = SHADER_INFO->GetVariable(key); const auto PROP_INFO = SHADER_INFO->GetVariable(key);
if (PROP_INFO == nullptr) if (PROP_INFO == nullptr)
{ {
throw std::invalid_argument("Attempted to set an invalid property!"); throw std::invalid_argument("Attempted to retrieve an invalid property!");
} }
// Get offset and return the memory directly // Get offset and return the memory directly
T* dataPtr = propMemory.get() + PROP_INFO->offset; T* dataPtr = reinterpret_cast<T*>(propMemory.get() + PROP_INFO->offset);
return *dataPtr; return *dataPtr;
} }
template<typename T> template<typename T>
@ -55,5 +66,25 @@ namespace SHADE
{ {
return const_cast<const T&>(const_cast<SHMaterial*>(this)->GetProperty(key)); return const_cast<const T&>(const_cast<SHMaterial*>(this)->GetProperty(key));
} }
template<typename T>
const T& SHMaterial::GetProperty(uint32_t memOffset) const
{
// Check if out of bounds
if (memOffset + sizeof(T) > propMemorySize)
throw std::invalid_argument("Attempted to retrieve an invalid property!");
return *(reinterpret_cast<T*>(propMemory.get() + memOffset));
}
template<typename T>
T& SHMaterial::GetProperty(uint32_t memOffset)
{
return const_cast<const T&>(const_cast<SHMaterial*>(this)->GetProperty(memOffset));
}
template<typename T>
void SHMaterial::setPropertyUnsafe(uint32_t memOffset, const T& value)
{
(*reinterpret_cast<T*>(propMemory.get() + memOffset)) = value;
}
} }

View File

@ -69,15 +69,15 @@ namespace SHADE
// Search Override Data for the property // Search Override Data for the property
uint32_t PROP_IDX = SHADER_INFO->GetVariableIndex(key); uint32_t PROP_IDX = SHADER_INFO->GetVariableIndex(key);
auto prop = std::find(overrideData.begin(), overrideData.end(), [&](const OverrideData& data) auto prop = std::find_if(overrideData.begin(), overrideData.end(), [&](const OverrideData& data)
{ {
return PROP_IDX == data.Index; return PROP_IDX == data.Index;
}); });
if (prop == overrideData.end()) if (prop == overrideData.end())
throw std::invalid_argument("Attempted to get an property that was not set previously!"); throw std::invalid_argument("Attempted to get an property that was not set previously!");
// Get offset and return the memory directly // Get offset and return the memory directly
T* dataPtr = dataStore.get() + prop->StoredDataOffset; T* dataPtr = reinterpret_cast<T*>(dataStore.get() + prop->StoredDataOffset);
return *dataPtr; return *dataPtr;
} }
template<typename T> template<typename T>

View File

@ -17,7 +17,8 @@ of DigiPen Institute of Technology is prohibited.
// Project Includes // Project Includes
#include "Resource/SHHandle.h" #include "Resource/SHHandle.h"
#include "Resource/SHResourceLibrary.h" #include "Resource/SHResourceLibrary.h"
#include "Math/SHMath.h" #include "Math/Vector/SHVec2.h"
#include "Math/Vector/SHVec3.h"
namespace SHADE namespace SHADE
{ {

View File

@ -7,6 +7,7 @@
#include "Graphics/Buffers/SHVkBuffer.h" #include "Graphics/Buffers/SHVkBuffer.h"
#include "Graphics/SHVkUtil.h" #include "Graphics/SHVkUtil.h"
#include "Graphics/MiddleEnd/Interface/SHViewport.h" #include "Graphics/MiddleEnd/Interface/SHViewport.h"
//#include "Graphics/MiddleEnd/Interface/SHInstancedIntegerData.h"
namespace SHADE namespace SHADE
{ {
@ -53,7 +54,7 @@ namespace SHADE
// wait for the copy to be done // wait for the copy to be done
afterCopyFence->Wait(true, std::numeric_limits<uint64_t>::max()); afterCopyFence->Wait(true, std::numeric_limits<uint64_t>::max());
pickedEID = imageDataDstBuffer->GetDataFromMappedPointer<uint32_t>(static_cast<uint32_t>(viewportMousePos.y) * entityIDAttachment->GetWidth() + static_cast<uint32_t>(viewportMousePos.x)); pickedEID = imageDataDstBuffer->GetDataFromMappedPointer<EntityID>(static_cast<uint32_t>(viewportMousePos.y) * entityIDAttachment->GetWidth() + static_cast<uint32_t>(viewportMousePos.x));
} }
} }

View File

@ -68,7 +68,7 @@ namespace SHADE
{ {
std::vector combinedImageSampler std::vector combinedImageSampler
{ {
std::make_tuple(offscreenRender->GetImageView(), offscreenRenderSampler, vk::ImageLayout::eGeneral), std::make_tuple(offscreenRender->GetImageView(), offscreenRenderSampler, vk::ImageLayout::eShaderReadOnlyOptimal),
}; };
// Register the image view and sampler with the descriptor set. Now whenever rendering to the offscreen image is done, the descriptor set will see the change // Register the image view and sampler with the descriptor set. Now whenever rendering to the offscreen image is done, the descriptor set will see the change

View File

@ -23,23 +23,26 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
void SHRenderable::OnCreate() void SHRenderable::OnCreate()
{ {
materialChanged = true; matChanged = true;
sharedMaterial = {}; sharedMaterial = {};
material = {}; material = {};
oldMaterial = {}; oldMaterial = {};
lightLayer = 0;
} }
void SHRenderable::OnDestroy() void SHRenderable::OnDestroy()
{ {
// Remove from SuperBatch
Handle<SHSuperBatch> superBatch = sharedMaterial->GetBaseMaterial()->GetPipeline()->GetPipelineState().GetSubpass()->GetSuperBatch();
superBatch->Remove(this);
// Free resources
if (material) if (material)
{ {
material.Free(); material.Free();
material = {}; material = {};
} }
// Remove from SuperBatch
Handle<SHSuperBatch> superBatch = sharedMaterial->GetBaseMaterial()->GetPipeline()->GetPipelineState().GetSubpass()->GetSuperBatch();
superBatch->Remove(this);
} }
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
@ -51,20 +54,23 @@ namespace SHADE
if (!material && sharedMaterial == materialInstance) if (!material && sharedMaterial == materialInstance)
return; return;
// Flag that material was changed
matChanged = true;
// Free copies of materials if any // Free copies of materials if any
if (material) if (material)
{ {
oldMaterial = material;
material.Free(); material.Free();
material = {}; material = {};
} }
else if (sharedMaterial)
// Flag that material was changed {
materialChanged = true; oldMaterial = sharedMaterial;
if (sharedMaterial) }
oldMaterial = sharedMaterial->GetBaseMaterial();
// Update the material // Update the material
sharedMaterial = materialInstance; sharedMaterial = materialInstance;
} }
Handle<SHMaterialInstance> SHRenderable::GetMaterial() const Handle<SHMaterialInstance> SHRenderable::GetMaterial() const
@ -86,10 +92,41 @@ namespace SHADE
return material; return material;
} }
/*-----------------------------------------------------------------------------------*/
/* Mesh Functions */
/*-----------------------------------------------------------------------------------*/
void SHRenderable::SetMesh(Handle<SHMesh> newMesh)
{
oldMesh = mesh;
mesh = newMesh;
meshChanged = true;
}
/*-----------------------------------------------------------------------------------*/
/* Light Functions */
/*-----------------------------------------------------------------------------------*/
uint8_t SHRenderable::GetLightLayer(void) const noexcept
{
return lightLayer;
}
/*-----------------------------------------------------------------------------------*/
/* Batcher Dispatcher Functions */
/*-----------------------------------------------------------------------------------*/
void SHRenderable::ResetChangedFlag() void SHRenderable::ResetChangedFlag()
{ {
materialChanged = false; matChanged = false;
oldMaterial = {}; meshChanged = false;
oldMaterial = {};
oldMesh = {};
} }
} }
RTTR_REGISTRATION
{
using namespace SHADE;
using namespace rttr;
registration::class_<SHRenderable>("Renderable Component");
}

View File

@ -11,9 +11,10 @@ of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/ *//*************************************************************************************/
#pragma once #pragma once
// External Dependencies
#include <rttr/registration>
// Project Includes // Project Includes
#include "Resource/SHHandle.h" #include "Resource/SHHandle.h"
//#include "SHTransform.h"
#include "ECS_Base/Components/SHComponent.h" #include "ECS_Base/Components/SHComponent.h"
#include "Math/SHMatrix.h" #include "Math/SHMatrix.h"
#include "SH_API.h" #include "SH_API.h"
@ -50,31 +51,42 @@ namespace SHADE
void SetMaterial(Handle<SHMaterialInstance> materialInstance); void SetMaterial(Handle<SHMaterialInstance> materialInstance);
Handle<SHMaterialInstance> GetMaterial() const; Handle<SHMaterialInstance> GetMaterial() const;
Handle<SHMaterialInstance> GetModifiableMaterial(); Handle<SHMaterialInstance> GetModifiableMaterial();
Handle<SHMaterialInstance> GetPrevMaterial() const noexcept { return oldMaterial; }
bool HasMaterialChanged() const noexcept { return matChanged; }
/*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/
/* Getter Functions */ /* Mesh Functions */
/*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/
bool WasMaterialChanged() const noexcept { return materialChanged; } void SetMesh(Handle<SHMesh> newMesh);
Handle<SHMaterial> GetPrevMaterial() const noexcept { return oldMaterial; } Handle<SHMesh> GetMesh() const noexcept { return mesh; }
Handle<SHMesh> GetPrevMesh() const noexcept { return oldMesh; }
bool HasMeshChanged() const noexcept { return meshChanged; }
/*-------------------------------------------------------------------------------*/
/* Light Functions */
/*-------------------------------------------------------------------------------*/
uint8_t GetLightLayer (void) const noexcept;
/*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/
/* Batcher Dispatcher Functions */ /* Batcher Dispatcher Functions */
/*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/
bool HasChanged() const noexcept { return matChanged || meshChanged; } // Whether or not the mesh or material has changed
void ResetChangedFlag(); // TODO: Lock it so that only SHBatcherDispatcher can access this void ResetChangedFlag(); // TODO: Lock it so that only SHBatcherDispatcher can access this
/*-------------------------------------------------------------------------------*/
/* Data Members */
/*-------------------------------------------------------------------------------*/
Handle<SHMesh> Mesh;
private: private:
/*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/
/* Data Members */ /* Data Members */
/*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/
Handle<SHMesh> mesh;
Handle<SHMesh> oldMesh;
bool meshChanged = true;
Handle<SHMaterialInstance> sharedMaterial; Handle<SHMaterialInstance> sharedMaterial;
Handle<SHMaterialInstance> material; Handle<SHMaterialInstance> material;
bool materialChanged = true; bool matChanged = true;
Handle<SHMaterial> oldMaterial; Handle<SHMaterialInstance> oldMaterial;
uint8_t lightLayer;
RTTR_ENABLE()
}; };
} }

View File

@ -0,0 +1,123 @@
#include "SHpch.h"
#include "SHLightComponent.h"
namespace SHADE
{
void SHLightComponent::OnCreate(void)
{
lightData.Reset();
SetType(SH_LIGHT_TYPE::DIRECTIONAL);
indexInBuffer = std::numeric_limits<uint32_t>::max();
active = true;
Unbind();
}
void SHLightComponent::OnDestroy(void)
{
}
void SHLightComponent::SetPosition(SHVec3 position) noexcept
{
lightData.position = position;
MakeDirty();
}
void SHLightComponent::SetType(SH_LIGHT_TYPE type) noexcept
{
lightData.type = type;
MakeDirty();
}
void SHLightComponent::SetDirection(SHVec3 direction) noexcept
{
lightData.direction = direction;
MakeDirty();
}
void SHLightComponent::SetDiffuseColor(SHVec4 diffuseColor) noexcept
{
lightData.diffuseColor = diffuseColor;
MakeDirty();
}
void SHLightComponent::ModifyLayer(uint8_t layerIndex, bool value) noexcept
{
if (value)
lightData.cullingMask |= (1u << layerIndex);
else
lightData.cullingMask &= ~(1u << layerIndex);
MakeDirty();
}
void SHLightComponent::SetAllLayers(void) noexcept
{
lightData.cullingMask = std::numeric_limits<uint32_t>::max();
MakeDirty();
}
void SHLightComponent::ClearAllLayers(void) noexcept
{
lightData.cullingMask = 0;
MakeDirty();
}
void SHLightComponent::MakeDirty(void) noexcept
{
dirty = true;
}
void SHLightComponent::ClearDirtyFlag(void) noexcept
{
dirty = false;
}
void SHLightComponent::Unbind(void) noexcept
{
bound = false;
MakeDirty();
}
void SHLightComponent::SetBound(uint32_t inIndexInBuffer) noexcept
{
bound = true;
indexInBuffer = inIndexInBuffer;
}
void SHLightComponent::SetActive(bool flag) noexcept
{
MakeDirty();
active = flag;
}
SHLightData const& SHLightComponent::GetLightData(void) const noexcept
{
return lightData;
}
bool SHLightComponent::IsDirty(void) const noexcept
{
return dirty;
}
bool SHLightComponent::GetBound(void) const noexcept
{
return bound;
}
uint32_t SHLightComponent::GetIndexInBuffer(void) const noexcept
{
return indexInBuffer;
}
}

View File

@ -0,0 +1,60 @@
#pragma once
#include "ECS_Base/Components/SHComponent.h"
#include "SHLightData.h"
namespace SHADE
{
class SH_API SHLightComponent final : public SHComponent
{
private:
//! General data for the light. This will purely be CPU bound. Whatever gets sent to the
//! GPU depends on the type of the light.
SHLightData lightData;
//! Since the lighting system is gonna be self contained and light weight, we store this
//! so that we only write this to the CPU buffer when this light component change, we don't
//! rewrite everything. However we still write to the GPU buffer when everything changes.
uint32_t indexInBuffer;
//! If the light component changed some value we mark this true.
bool dirty;
//! If the light's data is already in the buffers, this will be set to true.
bool bound;
//! If the light is active, this is true.
bool active;
public:
/*-----------------------------------------------------------------------*/
/* LIFECYCLE FUNCTIONS */
/*-----------------------------------------------------------------------*/
void OnCreate (void) override final;
void OnDestroy (void) override final;
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
void SetPosition (SHVec3 position) noexcept;
void SetType (SH_LIGHT_TYPE type) noexcept;
void SetDirection (SHVec3 direction) noexcept;
void SetDiffuseColor (SHVec4 diffuseColor) noexcept;
void ModifyLayer (uint8_t layerIndex, bool value) noexcept;
void SetAllLayers (void) noexcept;
void ClearAllLayers (void) noexcept;
void MakeDirty (void) noexcept;
void ClearDirtyFlag (void) noexcept;
void Unbind (void) noexcept;
void SetBound (uint32_t inIndexInBuffer) noexcept;
void SetActive (bool flag) noexcept;
SHLightData const& GetLightData (void) const noexcept;
bool IsDirty (void) const noexcept;
bool GetBound (void) const noexcept;
uint32_t GetIndexInBuffer (void) const noexcept;
};
}

View File

@ -0,0 +1,21 @@
#include "SHpch.h"
#include "SHLightData.h"
namespace SHADE
{
void SHLightData::Reset(void) noexcept
{
// no culling is done.
cullingMask = std::numeric_limits<uint32_t>::max();
// reset position to 0
position = SHVec3::Zero;
// direction just point in positive z axis
direction = SHVec3::Forward;
// Diffuse color set to 1
diffuseColor = SHVec4::One;
}
}

View File

@ -0,0 +1,55 @@
#pragma once
#include "Math/Vector/SHVec3.h"
#include "Math/Vector/SHVec4.h"
namespace SHADE
{
enum class SH_LIGHT_TYPE : uint32_t
{
DIRECTIONAL = 0,
POINT,
SPOT,
NUM_TYPES
};
/***************************************************************************/
/*!
\class
Every light will essentially be using this struct. However, when passing
light data over to the GPU, the light data will be split according to
type for more optimal cache access.
*/
/***************************************************************************/
struct SHLightData
{
//! position of the light
SHVec3 position;
//! Type of the light
SH_LIGHT_TYPE type;
//! direction of the light
SHVec3 direction;
//! Each bit in this 32 bit field will represent a layer. If the bit is set,
//! when a fragment is being evaluated, the shader will use the fragment's
//! layer value to AND with the light's. If result is 1, do lighting calculations.
uint32_t cullingMask;
//! Diffuse color emitted by the light
SHVec4 diffuseColor;
void Reset (void) noexcept;
//! TODO:
//! - Add cut off. (inner and outer).
//! - Add constant, linear and quadratic for attenuation
//! - Specular color if needed. see below.
//! Specular color
//SHVec4 specularColor;
};
}

View File

@ -0,0 +1,431 @@
#include "SHpch.h"
#include "SHLightingSubSystem.h"
#include "Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.h"
#include "Tools/SHUtilities.h"
#include "Graphics/Devices/SHVkLogicalDevice.h"
#include "Graphics/Buffers/SHVkBuffer.h"
#include "Graphics/Descriptors/SHVkDescriptorSetGroup.h"
#include "SHLightComponent.h"
#include "ECS_Base/Managers/SHComponentManager.h"
#include "SHLightComponent.h"
namespace SHADE
{
/***************************************************************************/
/*!
\brief
This function takes an address in the CPU container and writes light
component data to it. What gets written depends on the light type.
\param address
The address to write to.
\param lightComp
The light component with the data to write from.
\param lightType
The type of the light
\return
*/
/***************************************************************************/
void SHLightingSubSystem::PerTypeData::WriteLightToAddress(void* address, SHLightComponent* lightComp) noexcept
{
auto const& lightData = lightComp->GetLightData();
switch (lightData.type)
{
case SH_LIGHT_TYPE::DIRECTIONAL:
{
SHDirectionalLightData* lightPtr = reinterpret_cast<SHDirectionalLightData*>(address);
lightPtr->cullingMask = lightData.cullingMask;
lightPtr->direction = lightData.direction;
lightPtr->diffuseColor = lightData.diffuseColor;
break;
}
case SH_LIGHT_TYPE::POINT:
break;
case SH_LIGHT_TYPE::SPOT:
break;
case SH_LIGHT_TYPE::NUM_TYPES:
break;
default:
break;
}
}
/***************************************************************************/
/*!
\brief
Initializes type, intermediate data and buffer. dirty will be true.
\param lightType
type of the light.
*/
/***************************************************************************/
void SHLightingSubSystem::PerTypeData::InitializeData(Handle<SHVkLogicalDevice> logicalDevice, SH_LIGHT_TYPE type) noexcept
{
// initialize the type
lightType = type;
// boilerplate
intermediateData = nullptr;
// initialize alignment
lightDataAlignmentSize = logicalDevice->PadSSBOSize(GetLightTypeSize(type));
// So create some data!
Expand(logicalDevice);
}
/***************************************************************************/
/*!
\brief
Expands both the CPU container and the GPU buffer when the number of
lights have exceeded the capacity.
*/
/***************************************************************************/
void SHLightingSubSystem::PerTypeData::Expand(Handle<SHVkLogicalDevice> logicalDevice) noexcept
{
if (lightDataAlignmentSize == 0)
{
SHLOG_ERROR ("One of the types of lights have not been accounted for. Make sure lightDataAlignmentSize is not nullptr.");
return;
}
// we want to wait for the command buffers to finish using the buffers first
logicalDevice->WaitIdle();
// First time we are initializing lights
if (intermediateData == nullptr)
{
// max lights should start of at STARTING_NUM_LIGHTS lights
maxLights = STARTING_NUM_LIGHTS;
numLights = 0;
// Initialize the data for lights
intermediateData = std::make_unique<uint8_t[]>(lightDataAlignmentSize * maxLights);
// We want to initialize 3 times the amount of data required.
dataBuffer = logicalDevice->CreateBuffer(maxLights * lightDataAlignmentSize * SHGraphicsConstants::NUM_FRAME_BUFFERS, nullptr, maxLights * lightDataAlignmentSize * SHGraphicsConstants::NUM_FRAME_BUFFERS, vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT);
}
else
{
// save old number of lights
uint32_t const OLD_MAX_LIGHTS = maxLights;
// before we increase the number of lights, create space to store old data.
std::unique_ptr<uint8_t[]> oldData = std::make_unique<uint8_t[]>(lightDataAlignmentSize * OLD_MAX_LIGHTS);
// copy data over.
std::memcpy (oldData.get(), intermediateData.get(), lightDataAlignmentSize * OLD_MAX_LIGHTS);
// now we start to expand....
// double space for lights
maxLights *= 2;
// destroy old data and initialize container for double the amount of data.
intermediateData = std::make_unique<uint8_t[]>(lightDataAlignmentSize * maxLights);
// copy old data to new container
std::memcpy(intermediateData.get(), oldData.get(), lightDataAlignmentSize * OLD_MAX_LIGHTS);
// Resize the GPU buffer. TODO: Replace with Resize no copy here
dataBuffer->ResizeReplace(maxLights * lightDataAlignmentSize * SHGraphicsConstants::NUM_FRAME_BUFFERS, oldData.get(), lightDataAlignmentSize * OLD_MAX_LIGHTS);
}
}
/***************************************************************************/
/*!
\brief
Gets the size required to store data for a light type.
\param type
Type of a light.
\return
Size required to store a light based on type.
*/
/***************************************************************************/
uint32_t SHLightingSubSystem::PerTypeData::GetLightTypeSize(SH_LIGHT_TYPE type) noexcept
{
switch (type)
{
case SH_LIGHT_TYPE::DIRECTIONAL:
// TOOD: Change after creating point light struct
return sizeof(SHDirectionalLightData);
case SH_LIGHT_TYPE::POINT:
return 4;
case SH_LIGHT_TYPE::SPOT:
// TOOD: Change after creating spot light struct
return 4;
case SH_LIGHT_TYPE::NUM_TYPES:
default:
return 4;
}
}
Handle<SHVkBuffer> SHLightingSubSystem::PerTypeData::GetDataBuffer(void) const noexcept
{
return dataBuffer;
}
uint32_t SHLightingSubSystem::PerTypeData::GetAlignmentSize(void) const noexcept
{
return lightDataAlignmentSize;
}
uint32_t SHLightingSubSystem::PerTypeData::GetNumLights(void) const noexcept
{
return numLights;
}
uint32_t SHLightingSubSystem::PerTypeData::GetMaxLights(void) const noexcept
{
return maxLights;
}
/***************************************************************************/
/*!
\brief
This function takes in a light comp in the event that its data has not
been placed in the buffer yet. It also checks if the size of the buffer
is big enough to hold the new light. If the buffer is too small, expand
it.
\param lightComp
The light component to add.
*/
/***************************************************************************/
void SHLightingSubSystem::PerTypeData::AddLight(Handle<SHVkLogicalDevice> logicalDevice, SHLightComponent* unboundLight, bool expanded) noexcept
{
if (unboundLight)
{
// capacity is full
if (numLights == maxLights)
{
// expand first
Expand(logicalDevice);
expanded = true;
}
// Now that the container is big enough, bind the new light
// Get address of write location
void* writeLocation = reinterpret_cast<uint8_t*>(intermediateData.get()) + (lightDataAlignmentSize * numLights);
// Write the light data to address
WriteLightToAddress(writeLocation, unboundLight);
// Set the light component to be bound to that location
unboundLight->SetBound(numLights);
// Increase light count
++numLights;
}
}
/***************************************************************************/
/*!
\brief
Modify the data at a specific light address.
\param lightComp
The light component to write.
*/
/***************************************************************************/
void SHLightingSubSystem::PerTypeData::ModifyLight(SHLightComponent* lightComp) noexcept
{
void* writeLocation = reinterpret_cast<uint8_t*>(intermediateData.get()) + (lightDataAlignmentSize * lightComp->GetIndexInBuffer());
WriteLightToAddress(writeLocation, lightComp);
}
void SHLightingSubSystem::PerTypeData::WriteToGPU(uint32_t frameIndex) noexcept
{
if (intermediateData)
{
// we want to write to the offset of the current frame
dataBuffer->WriteToMemory(intermediateData.get(), lightDataAlignmentSize * numLights, 0, lightDataAlignmentSize * maxLights * frameIndex);
}
}
/***************************************************************************/
/*!
\brief
Update descriptor sets. We want to call this every time we expand buffers.
\param binding
The binding in the set we want to update.
*/
/***************************************************************************/
void SHLightingSubSystem::UpdateDescSet(uint32_t binding) noexcept
{
auto buffer = perTypeData[binding].GetDataBuffer();
// We bind the buffer with the correct desc set binding
lightingDataDescSet->ModifyWriteDescBuffer(SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS,
binding,
{ &buffer, 1 },
0,
perTypeData[binding].GetAlignmentSize() * perTypeData[binding].GetMaxLights());
lightingDataDescSet->UpdateDescriptorSetBuffer(SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, binding);
}
/***************************************************************************/
/*!
\brief
Computes dynamic offsets.
*/
/***************************************************************************/
void SHLightingSubSystem::ComputeDynamicOffsets(void) noexcept
{
for (uint32_t i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i)
{
for (uint32_t j = 0; j < dynamicOffsets.size(); ++j)
{
auto const& typeData = perTypeData[j];
{
dynamicOffsets[i][j] = j * typeData.GetAlignmentSize() * typeData.GetMaxLights();
}
}
}
}
/***************************************************************************/
/*!
\brief
Initializes per light type data. This includes buffers and descriptor
sets.
*/
/***************************************************************************/
void SHLightingSubSystem::Init(Handle<SHVkLogicalDevice> device, Handle<SHVkDescriptorPool> descPool) noexcept
{
SHComponentManager::CreateComponentSparseSet<SHLightComponent>();
logicalDevice = device;
uint32_t constexpr NUM_LIGHT_TYPES = SHUtilities::ToUnderlying(SH_LIGHT_TYPE::NUM_TYPES);
std::vector<uint32_t> variableSizes{ NUM_LIGHT_TYPES };
std::fill (variableSizes.begin(), variableSizes.end(), 1);
// Create the descriptor set
lightingDataDescSet = descPool->Allocate({SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS]}, variableSizes);
for (uint32_t i = 0; i < NUM_LIGHT_TYPES; ++i)
{
// initialize all the data first. We add more lights here as we add more types.
perTypeData[i].InitializeData(logicalDevice, static_cast<SH_LIGHT_TYPE>(i));
UpdateDescSet(i);
}
for (uint32_t i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i)
{
dynamicOffsets[i].resize(NUM_LIGHT_TYPES);
}
}
/***************************************************************************/
/*!
\brief
Loops through every single light component and checks for dirty light
data. If light data is dirty, rewrite to the CPU container. We also want
to bind the descriptor set for the light data.
*/
/***************************************************************************/
void SHLightingSubSystem::Run(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex) noexcept
{
auto& lightComps = SHComponentManager::GetDense<SHLightComponent>();
bool expanded = false;
for (auto& light : lightComps)
{
auto enumValue = SHUtilities::ToUnderlying(light.GetLightData().type);
// First we want to make sure the light is already bound to the system. if it
// isn't, we write it to the correct buffer.
if (!light.GetBound())
{
perTypeData[enumValue].AddLight(logicalDevice, &light, expanded);
}
// if there was modification to the light data
if (light.IsDirty())
{
// Write the data to the CPU
perTypeData[enumValue].ModifyLight(&light);
// Light is now updated in the container
light.ClearDirtyFlag();
}
}
// Write data to GPU
for (auto& data : perTypeData)
{
data.WriteToGPU(frameIndex);
}
// If any of the buffers got expanded, the descriptor set is invalid because the expanded buffer
// is a new buffer. If some expansion was detected, update descriptor sets.
if (expanded)
{
uint32_t constexpr NUM_LIGHT_TYPES = SHUtilities::ToUnderlying(SH_LIGHT_TYPE::NUM_TYPES);
for (uint32_t i = 0; i < NUM_LIGHT_TYPES; ++i)
{
UpdateDescSet(i);
}
}
// compute dynamic offsets. We don't actually have to compute every frame but its pretty lightweight,
// so we do it anyway. #NoteToSelf: if at any point it affects performance, do a check before computing.
ComputeDynamicOffsets();
// Bind descriptor set (We bind at an offset because the buffer holds NUM_FRAME_BUFFERS sets of data).
cmdBuffer->BindDescriptorSet(lightingDataDescSet, SH_PIPELINE_TYPE::GRAPHICS, SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, {dynamicOffsets[frameIndex]});
}
/***************************************************************************/
/*!
\brief
Does nothing for now.
*/
/***************************************************************************/
void SHLightingSubSystem::Exit(void) noexcept
{
}
}

View File

@ -0,0 +1,124 @@
#pragma once
#include "Resource/SHHandle.h"
#include "Math/Vector/SHVec3.h"
#include "Math/Vector/SHVec4.h"
#include "SHLightData.h"
#include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h"
namespace SHADE
{
class SHVkLogicalDevice;
class SHVkDescriptorPool;
class SHVkDescriptorSetGroup;
class SHVkDescriptorSetLayout;
class SHVkBuffer;
class SHLightComponent;
class SHVkCommandBuffer;
// 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
SHVec3 direction;
//! Represents if the light is active or not
uint32_t active;
//! Each bit in this 32 bit field will represent a layer. If the bit is set,
//! when a fragment is being evaluated, the shader will use the fragment's
//! layer value to AND with the light's. If result is 1, do lighting calculations.
uint32_t cullingMask;
//! Diffuse color emitted by the light
SHVec4 diffuseColor;
};
class SH_API SHLightingSubSystem
{
private:
class PerTypeData
{
private:
/*-----------------------------------------------------------------------*/
/* STATIC MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
static constexpr uint32_t STARTING_NUM_LIGHTS = 20;
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! Capacity of the container.
uint32_t maxLights;
//! SSBOs need to be aligned. This is to pad lighting structs
uint32_t lightDataAlignmentSize;
//! type of the light. Will be used later when we want to expand
SH_LIGHT_TYPE lightType;
//! number of lights currently alive.
uint32_t numLights;
//! GPU buffer required to store GPU data
Handle<SHVkBuffer> dataBuffer;
//! Before data gets copied to the GPU, it goes into here first. Data here is aligned to whatever struct is
//! used to represent data in this container. Note this will store only 1 copy of all the lights, compared
//! to the GPU that stores NUM_FRAME_BUFFERS copies.
std::unique_ptr<uint8_t[]> intermediateData;
void WriteLightToAddress (void* address, SHLightComponent* lightComp) noexcept;
public:
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void InitializeData (Handle<SHVkLogicalDevice> logicalDevice, SH_LIGHT_TYPE type) noexcept;
void Expand (Handle<SHVkLogicalDevice> logicalDevice) noexcept;
void AddLight (Handle<SHVkLogicalDevice> logicalDevice, SHLightComponent* unboundLight, bool expanded) noexcept;
void ModifyLight (SHLightComponent* lightComp) noexcept;
void WriteToGPU (uint32_t frameIndex) noexcept;
/*-----------------------------------------------------------------------*/
/* GETTERS */
/*-----------------------------------------------------------------------*/
static uint32_t GetLightTypeSize (SH_LIGHT_TYPE type) noexcept;
Handle<SHVkBuffer> GetDataBuffer (void) const noexcept;
uint32_t GetAlignmentSize (void) const noexcept;
uint32_t GetNumLights (void) const noexcept;
uint32_t GetMaxLights (void) const noexcept;
};
private:
//! logical device used for creation
Handle<SHVkLogicalDevice> logicalDevice;
//! The descriptor set that will hold the lighting data. Each binding will hold a buffer, NUM_FRAMES times the size required.
Handle<SHVkDescriptorSetGroup> lightingDataDescSet;
//! Each type will have some data associated with it for processing
std::array<PerTypeData, static_cast<uint32_t>(SH_LIGHT_TYPE::NUM_TYPES)> perTypeData;
//! Container to store dynamic offsets for binding descriptor sets
std::array<std::vector<uint32_t>, static_cast<uint32_t>(SHGraphicsConstants::NUM_FRAME_BUFFERS)> dynamicOffsets;
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void UpdateDescSet (uint32_t binding) noexcept;
void ComputeDynamicOffsets (void) noexcept;
public:
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void Init (Handle<SHVkLogicalDevice> device, Handle<SHVkDescriptorPool> descPool) noexcept;
void Run (Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex) noexcept;
void Exit (void) noexcept;
};
}

View File

@ -1,6 +1,7 @@
#include "SHPch.h" #include "SHPch.h"
#include "SHShaderModuleLibrary.h" #include "SHShaderModuleLibrary.h"
#include "Graphics/Devices/SHVkLogicalDevice.h" #include "Graphics/Devices/SHVkLogicalDevice.h"
#include "Assets/SHAssetManager.h"
namespace SHADE namespace SHADE
{ {
@ -18,33 +19,33 @@ namespace SHADE
*/ */
/***************************************************************************/ /***************************************************************************/
void SHShaderModuleLibrary::ImportFromSourceLibrary(Handle<SHVkLogicalDevice>& logicalDeviceHdl, SHShaderSourceLibrary const& sourceLib) noexcept //void SHShaderModuleLibrary::ImportFromSourceLibrary(Handle<SHVkLogicalDevice>& logicalDeviceHdl, SHShaderSourceLibrary const& sourceLib) noexcept
{ //{
auto const& sources = sourceLib.GetSourceLibrary(); // auto const& sources = sourceLib.GetSourceLibrary();
for (auto const& source : sources) // for (auto const& source : sources)
{ // {
vk::ShaderStageFlagBits shaderType{}; // vk::ShaderStageFlagBits shaderType{};
switch (source.shaderType) // switch (source.shaderType)
{ // {
case SH_SHADER_TYPE::VERTEX: // case SH_SHADER_TYPE::VERTEX:
shaderType = vk::ShaderStageFlagBits::eVertex; // shaderType = vk::ShaderStageFlagBits::eVertex;
break; // break;
case SH_SHADER_TYPE::FRAGMENT: // case SH_SHADER_TYPE::FRAGMENT:
shaderType = vk::ShaderStageFlagBits::eFragment; // shaderType = vk::ShaderStageFlagBits::eFragment;
break; // break;
case SH_SHADER_TYPE::COMPUTE: // case SH_SHADER_TYPE::COMPUTE:
shaderType = vk::ShaderStageFlagBits::eCompute; // shaderType = vk::ShaderStageFlagBits::eCompute;
break; // break;
default: // default:
shaderType = vk::ShaderStageFlagBits::eVertex; // shaderType = vk::ShaderStageFlagBits::eVertex;
break; // break;
} // }
Handle<SHVkShaderModule> newShaderModule = logicalDeviceHdl->CreateShaderModule(source.spirvBinary, "main", shaderType, source.name); // Handle<SHVkShaderModule> newShaderModule = logicalDeviceHdl->CreateShaderModule(source.spirvBinary, "main", shaderType, source.name);
shaderModules.emplace(source.id, newShaderModule); // shaderModules.emplace(source.id, newShaderModule);
stringToID.emplace(source.name, source.id); // stringToID.emplace(source.name, source.id);
} // }
} //}
/***************************************************************************/ /***************************************************************************/
/*! /*!
@ -58,12 +59,81 @@ namespace SHADE
*/ */
/***************************************************************************/ /***************************************************************************/
Handle<SHVkShaderModule> SHShaderModuleLibrary::GetShaderModule(std::string shaderName) const noexcept //Handle<SHVkShaderModule> SHShaderModuleLibrary::GetShaderModule(std::string shaderName) const noexcept
//{
// if (stringToID.contains(shaderName))
// return shaderModules.at(stringToID.at(shaderName));
// else
// return {};
//}
vk::ShaderStageFlagBits SHShaderModuleLibrary::GetVkShaderFlag(SH_SHADER_TYPE type) noexcept
{ {
if (stringToID.contains(shaderName)) vk::ShaderStageFlagBits shaderType{};
return shaderModules.at(stringToID.at(shaderName)); switch (type)
else {
return {}; case SH_SHADER_TYPE::VERTEX:
shaderType = vk::ShaderStageFlagBits::eVertex;
break;
case SH_SHADER_TYPE::FRAGMENT:
shaderType = vk::ShaderStageFlagBits::eFragment;
break;
case SH_SHADER_TYPE::COMPUTE:
shaderType = vk::ShaderStageFlagBits::eCompute;
break;
default:
shaderType = vk::ShaderStageFlagBits::eVertex;
break;
}
return shaderType;
} }
} Handle<SHVkShaderModule> SHShaderModuleLibrary::GetBuiltInShaderModule(std::string shaderName) const noexcept
{
if (builtInShaderModules.contains(shaderName))
return builtInShaderModules.at(shaderName);
else
return {};
}
void SHShaderModuleLibrary::ImportAllShaderSource(Handle<SHVkLogicalDevice>& logicalDeviceHdl) noexcept
{
uint32_t idCounter{ 0 };
auto const data = SHAssetManager::GetAllDataOfType(AssetType::SHADER);
for (auto const& dataPtr : data)
{
auto const shader = dynamic_cast<SHShaderAsset const*>(dataPtr);
Handle<SHVkShaderModule> newShaderModule =
logicalDeviceHdl->CreateShaderModule(shader->spirvBinary, "main", GetVkShaderFlag(shader->shaderType), shader->name);
shaderModules.emplace(idCounter++, newShaderModule);
}
auto const builtIn = SHAssetManager::GetAllDataOfType(AssetType::SHADER_BUILT_IN);
for (auto const& dataPtr : builtIn)
{
auto const shader = dynamic_cast<SHShaderAsset const*>(dataPtr);
Handle<SHVkShaderModule> newShaderModule =
logicalDeviceHdl->CreateShaderModule(shader->spirvBinary, "main", GetVkShaderFlag(shader->shaderType), shader->name);
builtInShaderModules.emplace(shader->name, newShaderModule);
}
}
void SHShaderModuleLibrary::ReflectAllShaderModules() noexcept
{
for (auto& module : shaderModules)
{
module.second->Reflect();
}
for (auto& module : builtInShaderModules)
{
module.second->Reflect();
}
}
}

View File

@ -2,8 +2,7 @@
#define SH_SHADER_MODULE_LIBRARY_H #define SH_SHADER_MODULE_LIBRARY_H
#include "Graphics/Shaders/SHVkShaderModule.h" #include "Graphics/Shaders/SHVkShaderModule.h"
#include "SHShaderSourceLibrary.h" #include "Assets/Asset Types/SHShaderAsset.h"
#include <map>
namespace SHADE namespace SHADE
{ {
@ -22,20 +21,23 @@ namespace SHADE
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
//! Stored shader modules //! Stored shader modules
std::unordered_map<uint32_t, Handle<SHVkShaderModule>> shaderModules; std::unordered_map<uint32_t, Handle<SHVkShaderModule>> shaderModules;
std::unordered_map<std::string, Handle<SHVkShaderModule>> builtInShaderModules;
//! We want some sort of interface with strings, instead of ints inline vk::ShaderStageFlagBits GetVkShaderFlag(SH_SHADER_TYPE type) noexcept;
std::map<std::string, uint32_t> stringToID;
public: public:
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */ /* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
void ImportFromSourceLibrary(Handle<SHVkLogicalDevice>& logicalDeviceHdl, SHShaderSourceLibrary const& sourceLib) noexcept; //void ImportFromSourceLibrary(Handle<SHVkLogicalDevice>& logicalDeviceHdl, SHShaderSourceLibrary const& sourceLib) noexcept;
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */ /* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
Handle<SHVkShaderModule> GetShaderModule(std::string shaderName) const noexcept; Handle<SHVkShaderModule> GetBuiltInShaderModule(std::string shaderName) const noexcept;
void ImportAllShaderSource(Handle<SHVkLogicalDevice>& logicalDeviceHdl) noexcept;
void ReflectAllShaderModules() noexcept;
}; };
} }

View File

@ -1,308 +0,0 @@
#include "SHPch.h"
#include <filesystem>
#include <fstream>
#include "SHShaderSourceLibrary.h"
#include "Tools/SHLogger.h"
namespace SHADE
{
/***************************************************************************/
/*!
\brief
Initializes the directory to take assets from. TODO: Only temporary until
the resource manager is implemented.
\param directory
\return
*/
/***************************************************************************/
void SHShaderSourceLibrary::Init (std::string directory) noexcept
{
shaderDirectory = directory;
if (shaderDirectory.back() != '/')
{
shaderDirectory += '/';
}
}
/***************************************************************************/
/*!
\brief
Private member function to compile a shader STRING source to binary and
returns a vector of 4 bytes.
\param glslSource
The GLSL string source.
\param type
Type of the shader: vertex, fragment, compute, etc.
\param opLevel
Optimization level.
\return
Returns a vector of the binary data.
*/
/***************************************************************************/
std::vector<uint32_t> SHShaderSourceLibrary::CompileToBinary(std::string const& glslSource, char const* const spirvFilename, SH_SHADER_TYPE type, shaderc_optimization_level opLevel /*= shaderc_optimization_level_zero*/)
{
// shaderc compiler
shaderc::Compiler compiler;
shaderc::CompileOptions options;
options.AddMacroDefinition("MY_DEFINE", "1");
// Set optimization levels
if (opLevel != shaderc_optimization_level_zero)
options.SetOptimizationLevel(opLevel);
// Attempt to get the shaderc equivalent shader stage
shaderc_shader_kind shaderKind;
switch (type)
{
case SH_SHADER_TYPE::VERTEX:
shaderKind = shaderc_shader_kind::shaderc_glsl_vertex_shader;
break;
case SH_SHADER_TYPE::FRAGMENT:
shaderKind = shaderc_shader_kind::shaderc_glsl_fragment_shader;
break;
case SH_SHADER_TYPE::COMPUTE:
shaderKind = shaderc_shader_kind::shaderc_glsl_compute_shader;
break;
default:
shaderKind = shaderc_shader_kind::shaderc_glsl_vertex_shader;
break;
}
// Compile the shader and get the result
shaderc::SpvCompilationResult compileResult = compiler.CompileGlslToSpv(glslSource, shaderKind, spirvFilename, options);
if (compileResult.GetCompilationStatus() != shaderc_compilation_status_success)
{
SHLOG_ERROR("Shaderc failed to compile GLSL shader to binary | " + compileResult.GetErrorMessage());
}
return { compileResult.begin(), compileResult.end() };
}
/***************************************************************************/
/*!
\brief
TODO: Delete after file IO is implemented. Loads a shader from disk.
\param filePath
file path to the shader in the asset directory.
\return
Returns the data in the file in string form.
*/
/***************************************************************************/
std::string SHShaderSourceLibrary::GetStringFromFile(char const* filePath) noexcept
{
// Retrieve contents from filePath
// Ensure ifstream objects can throw exceptions
std::ifstream iFile;
iFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
std::string fileContent = "";
try
{
// Open file
// Read file's buffer contents into streams
iFile.open(filePath);
std::stringstream fileStream;
fileStream << iFile.rdbuf();
fileContent = fileStream.str();
// Close file handler
iFile.close();
}
catch (std::ifstream::failure e)
{
std::cerr << "File was not successfully read" << filePath << std::endl;
}
return fileContent;
}
/***************************************************************************/
/*!
\brief
Load a shader into the library.
\param filePath
file path to the shader in the asset directory.
*/
/***************************************************************************/
bool SHShaderSourceLibrary::LoadShader (uint32_t id, std::string glslFile, SH_SHADER_TYPE type, bool checkSpirvOutdated/* = true*/, bool recompileAnyway /*= false*/) noexcept
{
//if (sourceLibrary.contains(id))
//{
// SHLOG_ERROR("Shader with ID passed in already exists. Use a different ID");
// return false;
//}
std::string fullGLSLPath = shaderDirectory + glslFile;
auto path = std::filesystem::path(fullGLSLPath);
if (path.extension() != ".glsl")
{
SHLOG_ERROR("Shader is not GLSL file, failed to load shader. ");
return false;
}
std::string spirvFilepath = path.replace_extension("spv").string();
SHShaderData newShaderData{};
newShaderData.shaderType = type;
// spirv file
std::ifstream spirvFile(spirvFilepath, std::ios::ate | std::ios::binary);
// If we disable spirv validation, file is not checked
if (!recompileAnyway &&
spirvFile.is_open() &&
(checkSpirvOutdated ? (std::filesystem::last_write_time(spirvFilepath) > std::filesystem::last_write_time(fullGLSLPath)) : true))
{
// Get file size of binary
uint32_t fileSize = static_cast<uint32_t>(spirvFile.tellg());
// resize container to store binary
newShaderData.spirvBinary.resize(fileSize / sizeof(uint32_t));
// Read data from binary file to container
spirvFile.seekg(0);
spirvFile.read(reinterpret_cast<char*>(newShaderData.spirvBinary.data()), fileSize);
// close file
spirvFile.close();
}
else
{
// Use glslc to generate spirv file
newShaderData.spirvBinary = CompileToBinary(GetStringFromFile(fullGLSLPath.c_str()), spirvFilepath.c_str(), type);
std::ofstream binaryFile(spirvFilepath, std::ios::binary);
if (binaryFile.is_open())
{
// write all data to binary file
binaryFile.write(reinterpret_cast<const char*>(newShaderData.spirvBinary.data()), newShaderData.spirvBinary.size() * sizeof(uint32_t));
}
else
{
SHLOG_ERROR("Failed to modify spirv file. ");
return false;
}
}
newShaderData.name = glslFile;
newShaderData.id = id;
sourceLibrary.emplace_back(std::move (newShaderData));
return true;
}
/***************************************************************************/
/*!
\brief
Gets the entire source library.
\return
The container of binary data.
*/
/***************************************************************************/
std::vector<SHShaderData> const& SHShaderSourceLibrary::GetSourceLibrary(void) const noexcept
{
return sourceLibrary;
}
/***************************************************************************/
/*!
\brief
Move ctor for shader data.
\param rhs
The other shader data
*/
/***************************************************************************/
SHShaderData::SHShaderData(SHShaderData&& rhs) noexcept
: spirvBinary{ std::move (rhs.spirvBinary)}
, shaderType{std::move (rhs.shaderType)}
, name{ std::move (rhs.name)}
, id {std::move (rhs.id)}
{
}
/***************************************************************************/
/*!
\brief
Default ctor for shader data. Does nothing.
*/
/***************************************************************************/
SHShaderData::SHShaderData(void) noexcept
: spirvBinary{}
, shaderType{SH_SHADER_TYPE::VERTEX}
, name{ }
, id{ }
{
}
SHShaderData::SHShaderData(SHShaderData const& rhs) noexcept
: spirvBinary{rhs.spirvBinary}
, shaderType{ rhs.shaderType}
, name{rhs.name }
, id{rhs.id }
{
}
SHShaderData& SHShaderData::operator=(SHShaderData const& rhs) noexcept
{
if (this == &rhs)
return *this;
spirvBinary = rhs.spirvBinary;
shaderType = rhs.shaderType;
name = rhs.name;
id = rhs.id;
return *this;
}
SHShaderData& SHShaderData::operator=(SHShaderData&& rhs) noexcept
{
if (this == &rhs)
return *this;
spirvBinary = std::move(rhs.spirvBinary);
shaderType = std::move (rhs.shaderType);
name = std::move (rhs.name);
id = std::move (rhs.id);
return *this;
}
}

View File

@ -1,70 +0,0 @@
#ifndef SH_SHADER_SOURCE_LIBRARY_H
#define SH_SHADER_SOURCE_LIBRARY_H
#include <map>
#include "SHShaderType.h"
#include "shaderc/shaderc.hpp"
namespace SHADE
{
struct SHShaderData
{
/*-----------------------------------------------------------------------*/
/* MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! container storing the spirv binary
std::vector<uint32_t> spirvBinary;
//! For the compilation of the shader. Vulkan backend will use it too
SH_SHADER_TYPE shaderType;
//! Name of the shader file (without parent path)
std::string name;
//! id of the shader
uint32_t id;
SHShaderData(void) noexcept;
SHShaderData(SHShaderData const& rhs) noexcept;
SHShaderData(SHShaderData&& rhs) noexcept;
SHShaderData& operator= (SHShaderData&& rhs) noexcept;
SHShaderData& operator= (SHShaderData const& rhs) noexcept;
};
// TODO: This class is purely temporary and will be converted/changed when XQ implements his resource manager
class SHShaderSourceLibrary
{
private:
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! Stores all the source data. Take note that the source here is GLSL source and NOT binary data.
//! Binary data gets passed to the backend to convert to spirv.
std::vector<SHShaderData> sourceLibrary;
//! The directory where the shaders are located.
std::string shaderDirectory;
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
std::vector<uint32_t> CompileToBinary(std::string const& glslSource, char const* const spirvFilename, SH_SHADER_TYPE type, shaderc_optimization_level opLevel = shaderc_optimization_level_zero);
// TODO: Delete after file IO is implemented
std::string GetStringFromFile(char const* filePath) noexcept;
public:
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void Init (std::string directory) noexcept;
bool LoadShader (uint32_t id, std::string glslFile, SH_SHADER_TYPE type, bool checkSpirvOutdated = true, bool recompileAnyway = false) noexcept;
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
std::vector<SHShaderData> const& GetSourceLibrary(void) const noexcept;
};
}
#endif

Some files were not shown because too many files have changed in this diff Show More