diff --git a/.gitignore b/.gitignore index fba41f1e..c7fefb6e 100644 --- a/.gitignore +++ b/.gitignore @@ -353,4 +353,9 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ +# Generated Files [Dd]ependencies/ +*.vcxproj +*.vcxproj.filters +*.sln +*.csproj diff --git a/Dependencies.bat b/Dependencies.bat index bbce926b..0b1e1a54 100644 --- a/Dependencies.bat +++ b/Dependencies.bat @@ -8,34 +8,36 @@ echo "A - All" echo "B - VMA" echo "C - msdf" echo "D - assimp" -echo "E - ktx" -echo "F - spdlog" -echo "G - reactphysics3d" -echo "H - imgui" -echo "I - imguizmo" -echo "J - imnodes" -echo "K - tracy" -echo "L - RTTR" -echo "M - yamlcpp" +echo "E - spdlog" +echo "F - reactphysics3d" +echo "G - imgui" +echo "H - imguizmo" +echo "I - imnodes" +echo "J - tracy" +echo "K - RTTR" +echo "L - yamlcpp" +echo "M - SDL" +echo "N - dotnet" echo --------------------------------------------------- echo. -choice /C ABCDEFGHIJKLM /T 10 /D A +choice /C ABCDEFGHIJKLMN /T 10 /D A set _e=%ERRORLEVEL% if %_e%==1 goto VMA if %_e%==2 goto VMA if %_e%==3 goto MSDF if %_e%==4 goto assimp -if %_e%==5 goto ktx -if %_e%==6 goto spdlog -if %_e%==7 goto reactphysics3d -if %_e%==8 goto imgui -if %_e%==9 goto imguizmo -if %_e%==10 goto imnodes -if %_e%==11 goto tracy -if %_e%==12 goto RTTR -if %_e%==13 goto yamlcpp +if %_e%==5 goto spdlog +if %_e%==6 goto reactphysics3d +if %_e%==7 goto imgui +if %_e%==8 goto imguizmo +if %_e%==9 goto imnodes +if %_e%==10 goto tracy +if %_e%==11 goto RTTR +if %_e%==12 goto yamlcpp +if %_e%==13 goto SDL +if %_e%==14 goto dotnet :VMA echo -----------------------VMA---------------------------- @@ -53,60 +55,87 @@ if %_e%==3 (goto :done) else (goto :assimp) echo -----------------------assimp---------------------------- rmdir "Dependencies/assimp" /S /Q git clone https://github.com/SHADE-DP/assimp.git "Dependencies/assimp" -if %_e%==4 (goto :done) else (goto :ktx) +if %_e%==4 (goto :done) else (goto :spdlog) -:ktx -rmdir "Dependencies/ktx" /S /Q -echo -----------------------ktx---------------------------- -git clone https://github.com/SHADE-DP/ktx.git "Dependencies/ktx" -if %_e%==5 (goto :done) else (goto :spdlog) +@REM :ktx +@REM rmdir "Dependencies/ktx" /S /Q +@REM echo -----------------------ktx---------------------------- +@REM git clone https://github.com/SHADE-DP/ktx.git "Dependencies/ktx" +@REM if %_e%==5 (goto :done) else (goto :spdlog) :spdlog echo -----------------------spdlog---------------------------- rmdir "Dependencies/spdlog" /S /Q git clone https://github.com/SHADE-DP/spdlog.git "Dependencies/spdlog" -if %_e%==6 (goto :done) else (goto :reactphysics3d) +if %_e%==5 (goto :done) else (goto :reactphysics3d) :reactphysics3d echo -----------------------reactphysics3d---------------------------- rmdir "Dependencies/reactphysics3d" /S /Q git clone https://github.com/SHADE-DP/reactphysics3d.git "Dependencies/reactphysics3d" -if %_e%==7 (goto :done) else (goto :imgui) +if %_e%==6 (goto :done) else (goto :imgui) :imgui echo -----------------------imgui---------------------------- rmdir "Dependencies/imgui" /S /Q git clone https://github.com/SHADE-DP/imgui.git "Dependencies/imgui" -if %_e%==8 (goto :done) else (goto :imguizmo) +if %_e%==7 (goto :done) else (goto :imguizmo) :imguizmo echo -----------------------imguizmo---------------------------- rmdir "Dependencies/imguizmo" /S /Q git clone https://github.com/SHADE-DP/ImGuizmo.git "Dependencies/imguizmo" -if %_e%==9 (goto :done) else (goto :imnodes) +if %_e%==8 (goto :done) else (goto :imnodes) :imnodes echo -----------------------imnodes---------------------------- rmdir "Dependencies/imnodes" /S /Q git clone https://github.com/SHADE-DP/imnodes.git "Dependencies/imnodes" -if %_e%==10 (goto :done) else (goto :tracy) +if %_e%==9 (goto :done) else (goto :tracy) :tracy echo -----------------------tracy---------------------------- rmdir "Dependencies/tracy" /S /Q git clone https://github.com/SHADE-DP/tracy.git "Dependencies/tracy" -if %_e%==11 (goto :done) else (goto :RTTR) +if %_e%==10 (goto :done) else (goto :RTTR) :RTTR echo -----------------------RTTR---------------------------- rmdir "Dependencies/RTTR" /S /Q git clone https://github.com/SHADE-DP/RTTR.git "Dependencies/RTTR" -if %_e%==12 (goto :done) else (goto :yamlcpp) +if %_e%==11 (goto :done) else (goto :yamlcpp) :yamlcpp echo -----------------------yamlcpp---------------------------- rmdir "Dependencies/yamlcpp" /S /Q git clone https://github.com/SHADE-DP/yaml-cpp.git "Dependencies/yamlcpp" +if %_e%==12 (goto :done) else (goto :SDL) + +:SDL +echo -----------------------SDL---------------------------- +rmdir "Dependencies/SDL" /S /Q +mkdir "Dependencies/SDL/include" +mkdir "Dependencies/SDL/lib" +powershell -Command "& {wget https://github.com/libsdl-org/SDL/releases/download/release-2.24.0/SDL2-devel-2.24.0-VC.zip -OutFile "Dependencies/SDL/SDL.zip"}" +powershell -Command "& {Expand-Archive -LiteralPath Dependencies/SDL/SDL.zip -DestinationPath Dependencies/SDL/tmp}" +robocopy "Dependencies/SDL/tmp/SDL2-2.24.0/lib/x64" "Dependencies/SDL/lib/" /ns /nfl /ndl /nc /njh +robocopy "Dependencies/SDL/tmp/SDL2-2.24.0/include/" "Dependencies/SDL/include/" /ns /nfl /ndl /nc /njh +rmdir "Dependencies/SDL/tmp/" /s /q +powershell -Command "& {Remove-Item "Dependencies/SDL/SDL.zip"}" +if %_e%==13 (goto :done) else (goto :dotnet) + +:dotnet +echo -----------------------dotnet---------------------------- +rmdir "Dependencies/dotnet" /S /Q +mkdir "Dependencies/dotnet/include" +mkdir "Dependencies/dotnet/bin" +powershell -Command "& {wget https://raw.githubusercontent.com/dotnet/runtime/main/src/coreclr/hosts/inc/coreclrhost.h -OutFile "Dependencies/dotnet/include/coreclrhost.h"}" +powershell -Command "& {wget https://download.visualstudio.microsoft.com/download/pr/8686fa48-b378-424e-908b-afbd66d6e120/2d75d5c3574fb5d917c5a3cd3f624287/dotnet-sdk-6.0.400-win-x64.zip -OutFile "Dependencies/dotnet/dotnet.zip"}" +powershell -Command "& {Expand-Archive -LiteralPath Dependencies/dotnet/dotnet.zip -DestinationPath Dependencies/dotnet/tmp}" +robocopy "Dependencies/dotnet/tmp/shared/Microsoft.NETCore.App/6.0.8/" "Dependencies/dotnet/bin/" *.dll /ns /nfl /ndl /nc /njh +rmdir "Dependencies/dotnet/tmp/" /s /q +del "Dependencies/dotnet/dotnet.zip" +powershell -Command "& {Remove-Item "Dependencies/dotnet/dotnet.zip"}" :done echo DONE! diff --git a/Dependencies.lua b/Dependencies.lua index 32382cf9..9877a70e 100644 --- a/Dependencies.lua +++ b/Dependencies.lua @@ -10,6 +10,7 @@ IncludeDir["tracy"] = "%{wks.location}/Dependencies/tracy" IncludeDir["VMA"] = "%{wks.location}/Dependencies/VMA" IncludeDir["yamlcpp"] = "%{wks.location}/Dependencies/yamlcpp/include" IncludeDir["RTTR"] = "%{wks.location}/Dependencies/RTTR" -IncludeDir["ktx"] = "%{wks.location}/Dependencies/ktx" IncludeDir["reactphysics3d"] = "%{wks.location}/Dependencies/reactphysics3d" +IncludeDir["SDL"] = "%{wks.location}/Dependencies/SDL" IncludeDir["VULKAN"] = "$(VULKAN_SDK)" +IncludeDir["dotnet"] = "%{wks.location}/Dependencies/dotnet" diff --git a/Premake/premake5.exe b/Premake/premake5.exe index 1a637aa9..c25bb3fb 100644 Binary files a/Premake/premake5.exe and b/Premake/premake5.exe differ diff --git a/SHADE.sln b/SHADE.sln deleted file mode 100644 index 96aa6455..00000000 --- a/SHADE.sln +++ /dev/null @@ -1,79 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SHADE_Application", "SHADE_Application\SHADE_Application.vcxproj", "{BDC70008-29DE-FE9D-7255-8ABFDEAACF25}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dependencies", "Dependencies", "{53E47842-3FC8-3998-A828-34EB942B241A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImGui", "Dependencies\imgui\ImGui.vcxproj", "{C0FF640D-2C14-8DBE-F595-301E616989EF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "freetype", "Dependencies\msdf\msdfgen\freetype\freetype.vcxproj", "{89895BD8-7556-B6E3-9E6F-A48B8A9BEB71}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msdf-atlas-gen", "Dependencies\msdf\msdf-atlas-gen.vcxproj", "{38BD587B-248B-4C81-0D1F-BDA7F98B28E6}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msdfgen", "Dependencies\msdf\msdfgen\msdfgen.vcxproj", "{8900D8DD-F5DF-5679-FEF7-E14F6A56BDDA}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "reactphysics3d", "Dependencies\reactphysics3d\reactphysics3d.vcxproj", "{2ECAB41A-1A98-A820-032C-1947EF988485}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yaml-cpp", "Dependencies\yamlcpp\yaml-cpp.vcxproj", "{88F1A057-74BE-FB62-9DD7-E90A890331F1}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SHADE_Engine", "SHADE_Engine\SHADE_Engine.vcxproj", "{3F92E998-2BF5-783D-D47A-B1F3C0BC44C0}" - ProjectSection(ProjectDependencies) = postProject - {88F1A057-74BE-FB62-9DD7-E90A890331F1} = {88F1A057-74BE-FB62-9DD7-E90A890331F1} - {8900D8DD-F5DF-5679-FEF7-E14F6A56BDDA} = {8900D8DD-F5DF-5679-FEF7-E14F6A56BDDA} - {38BD587B-248B-4C81-0D1F-BDA7F98B28E6} = {38BD587B-248B-4C81-0D1F-BDA7F98B28E6} - {2ECAB41A-1A98-A820-032C-1947EF988485} = {2ECAB41A-1A98-A820-032C-1947EF988485} - {C0FF640D-2C14-8DBE-F595-301E616989EF} = {C0FF640D-2C14-8DBE-F595-301E616989EF} - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {BDC70008-29DE-FE9D-7255-8ABFDEAACF25}.Debug|x64.ActiveCfg = Debug|x64 - {BDC70008-29DE-FE9D-7255-8ABFDEAACF25}.Debug|x64.Build.0 = Debug|x64 - {BDC70008-29DE-FE9D-7255-8ABFDEAACF25}.Release|x64.ActiveCfg = Release|x64 - {BDC70008-29DE-FE9D-7255-8ABFDEAACF25}.Release|x64.Build.0 = Release|x64 - {C0FF640D-2C14-8DBE-F595-301E616989EF}.Debug|x64.ActiveCfg = Debug|x64 - {C0FF640D-2C14-8DBE-F595-301E616989EF}.Debug|x64.Build.0 = Debug|x64 - {C0FF640D-2C14-8DBE-F595-301E616989EF}.Release|x64.ActiveCfg = Release|x64 - {C0FF640D-2C14-8DBE-F595-301E616989EF}.Release|x64.Build.0 = Release|x64 - {89895BD8-7556-B6E3-9E6F-A48B8A9BEB71}.Debug|x64.ActiveCfg = Debug|x64 - {89895BD8-7556-B6E3-9E6F-A48B8A9BEB71}.Debug|x64.Build.0 = Debug|x64 - {89895BD8-7556-B6E3-9E6F-A48B8A9BEB71}.Release|x64.ActiveCfg = Release|x64 - {89895BD8-7556-B6E3-9E6F-A48B8A9BEB71}.Release|x64.Build.0 = Release|x64 - {38BD587B-248B-4C81-0D1F-BDA7F98B28E6}.Debug|x64.ActiveCfg = Debug|x64 - {38BD587B-248B-4C81-0D1F-BDA7F98B28E6}.Debug|x64.Build.0 = Debug|x64 - {38BD587B-248B-4C81-0D1F-BDA7F98B28E6}.Release|x64.ActiveCfg = Release|x64 - {38BD587B-248B-4C81-0D1F-BDA7F98B28E6}.Release|x64.Build.0 = Release|x64 - {8900D8DD-F5DF-5679-FEF7-E14F6A56BDDA}.Debug|x64.ActiveCfg = Debug|x64 - {8900D8DD-F5DF-5679-FEF7-E14F6A56BDDA}.Debug|x64.Build.0 = Debug|x64 - {8900D8DD-F5DF-5679-FEF7-E14F6A56BDDA}.Release|x64.ActiveCfg = Release|x64 - {8900D8DD-F5DF-5679-FEF7-E14F6A56BDDA}.Release|x64.Build.0 = Release|x64 - {2ECAB41A-1A98-A820-032C-1947EF988485}.Debug|x64.ActiveCfg = Debug|x64 - {2ECAB41A-1A98-A820-032C-1947EF988485}.Debug|x64.Build.0 = Debug|x64 - {2ECAB41A-1A98-A820-032C-1947EF988485}.Release|x64.ActiveCfg = Release|x64 - {2ECAB41A-1A98-A820-032C-1947EF988485}.Release|x64.Build.0 = Release|x64 - {88F1A057-74BE-FB62-9DD7-E90A890331F1}.Debug|x64.ActiveCfg = Debug|x64 - {88F1A057-74BE-FB62-9DD7-E90A890331F1}.Debug|x64.Build.0 = Debug|x64 - {88F1A057-74BE-FB62-9DD7-E90A890331F1}.Release|x64.ActiveCfg = Release|x64 - {88F1A057-74BE-FB62-9DD7-E90A890331F1}.Release|x64.Build.0 = Release|x64 - {3F92E998-2BF5-783D-D47A-B1F3C0BC44C0}.Debug|x64.ActiveCfg = Debug|x64 - {3F92E998-2BF5-783D-D47A-B1F3C0BC44C0}.Debug|x64.Build.0 = Debug|x64 - {3F92E998-2BF5-783D-D47A-B1F3C0BC44C0}.Release|x64.ActiveCfg = Release|x64 - {3F92E998-2BF5-783D-D47A-B1F3C0BC44C0}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {C0FF640D-2C14-8DBE-F595-301E616989EF} = {53E47842-3FC8-3998-A828-34EB942B241A} - {89895BD8-7556-B6E3-9E6F-A48B8A9BEB71} = {53E47842-3FC8-3998-A828-34EB942B241A} - {38BD587B-248B-4C81-0D1F-BDA7F98B28E6} = {53E47842-3FC8-3998-A828-34EB942B241A} - {8900D8DD-F5DF-5679-FEF7-E14F6A56BDDA} = {53E47842-3FC8-3998-A828-34EB942B241A} - {2ECAB41A-1A98-A820-032C-1947EF988485} = {53E47842-3FC8-3998-A828-34EB942B241A} - {88F1A057-74BE-FB62-9DD7-E90A890331F1} = {53E47842-3FC8-3998-A828-34EB942B241A} - EndGlobalSection -EndGlobal diff --git a/SHADE_Application/SHADE_Application.vcxproj b/SHADE_Application/SHADE_Application.vcxproj deleted file mode 100644 index b3490a70..00000000 --- a/SHADE_Application/SHADE_Application.vcxproj +++ /dev/null @@ -1,121 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - {BDC70008-29DE-FE9D-7255-8ABFDEAACF25} - true - Win32Proj - SHADE_Application - 10.0 - - - - Application - true - Unicode - v142 - - - Application - false - Unicode - v142 - - - - - - - - - - - - - true - ..\bin\Debug\ - ..\bin_int\Debug\SHADE_Application\ - SHADE_Application - .exe - - - false - ..\bin\Release\ - ..\bin_int\Release\SHADE_Application\ - SHADE_Application - .exe - - - - Use - SBpch.h - Level4 - _DEBUG;%(PreprocessorDefinitions) - ..\Dependencies\spdlog\include;..\SHADE_Engine\src;src;include;%(AdditionalIncludeDirectories) - EditAndContinue - Disabled - false - MultiThreadedDebugDLL - true - stdcpp20 - - - Windows - true - wWinMainCRTStartup - - - - - Use - SBpch.h - Level4 - _RELEASE;%(PreprocessorDefinitions) - ..\Dependencies\spdlog\include;..\SHADE_Engine\src;src;include;%(AdditionalIncludeDirectories) - Full - true - true - false - true - MultiThreadedDLL - true - stdcpp20 - - - Windows - true - true - wWinMainCRTStartup - - - - - - - - - - - Create - - - - - - - {3F92E998-2BF5-783D-D47A-B1F3C0BC44C0} - - - - - - \ No newline at end of file diff --git a/SHADE_Application/SHADE_Application.vcxproj.filters b/SHADE_Application/SHADE_Application.vcxproj.filters deleted file mode 100644 index 1234632d..00000000 --- a/SHADE_Application/SHADE_Application.vcxproj.filters +++ /dev/null @@ -1,30 +0,0 @@ - - - - - {D9DE78AF-4594-F1A4-CE88-EB7B3A3DE8A8} - - - {86EEB3D0-7290-DEA6-5B4B-F2FA478C65F7} - - - - - Application - - - - Scenes - - - - - Application - - - - Scenes - - - - \ No newline at end of file diff --git a/SHADE_Application/premake5.lua b/SHADE_Application/premake5.lua index 5c408fb5..9aa1707b 100644 --- a/SHADE_Application/premake5.lua +++ b/SHADE_Application/premake5.lua @@ -26,6 +26,7 @@ project "SHADE_Application" "../SHADE_Engine/src", "src", "%{IncludeDir.dotnet}/include", + "%{IncludeDir.SDL}/include", } flags @@ -36,6 +37,20 @@ project "SHADE_Application" links { "SHADE_Engine", + "SHADE_Managed", + "SDL2.lib", + "SDL2main.lib" + } + + libdirs + { + "%{IncludeDir.spdlog}/lib", + "%{IncludeDir.SDL}/lib", + } + + disablewarnings + { + "4251" } warnings 'Extra' diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index dc497dbe..171f4995 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -12,6 +12,9 @@ #include #include #include +#include + +#include "Scripting/SHScriptEngine.h" namespace Sandbox { @@ -27,12 +30,16 @@ namespace Sandbox // Set working directory SHADE::SHFileUtilities::SetWorkDirToExecDir(); + SDL_Init(SDL_INIT_VIDEO); window.Create(hInstance, hPrevInstance, lpCmdLine, nCmdShow); + SDL_CreateWindowFrom(window.GetHWND()); #ifdef SHEDITOR #else #endif + // Set up scripting + SHADE::SHScriptEngine::Init(); } void SBApplication::Update(void) @@ -49,6 +56,10 @@ namespace Sandbox void SBApplication::Exit(void) { + // Shutdown scripting + SHADE::SHScriptEngine::Exit(); + + SDL_DestroyWindow(sdlWindow); #ifdef SHEDITOR #else #endif diff --git a/SHADE_Application/src/Application/SBApplication.h b/SHADE_Application/src/Application/SBApplication.h index a1bf11eb..9a6e4153 100644 --- a/SHADE_Application/src/Application/SBApplication.h +++ b/SHADE_Application/src/Application/SBApplication.h @@ -1,6 +1,7 @@ #ifndef SB_APPLICATION_H #define SB_APPLICATION_H -#include +#include +#include "Graphics/Windowing/SHWindow.h" //using namespace SHADE; namespace Sandbox @@ -9,6 +10,7 @@ namespace Sandbox { private: SHADE::SHWindow window; + SDL_Window* sdlWindow; //SHAppConfig config; public: SBApplication() = default; diff --git a/SHADE_Engine/SHADE_Engine.vcxproj b/SHADE_Engine/SHADE_Engine.vcxproj deleted file mode 100644 index ed21685b..00000000 --- a/SHADE_Engine/SHADE_Engine.vcxproj +++ /dev/null @@ -1,302 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - {3F92E998-2BF5-783D-D47A-B1F3C0BC44C0} - true - Win32Proj - SHADE_Engine - 10.0 - - - - DynamicLibrary - true - Unicode - v142 - - - DynamicLibrary - false - Unicode - v142 - - - - - - - - - - - - - true - ..\bin\Debug\ - ..\bin_int\Debug\SHADE_Engine\ - SHADE_Engine - .dll - - - false - ..\bin\Release\ - ..\bin_int\Release\SHADE_Engine\ - SHADE_Engine - .dll - - - - Use - SHpch.h - Level4 - _LIB;_GLFW_INCLUDE_NONE;MSDFGEN_USE_CPP11;NOMINMAX;SH_API_EXPORT;_DEBUG;%(PreprocessorDefinitions) - src;..\Dependencies\assimp\include;..\Dependencies\imgui;..\Dependencies\imguizmo;..\Dependencies\imnodes;..\Dependencies\msdf;..\Dependencies\msdf\msdfgen;..\Dependencies\spdlog\include;..\Dependencies\tracy;..\Dependencies\VMA\include;..\Dependencies\yamlcpp\include;..\Dependencies\ktx\include;..\Dependencies\RTTR\include;..\Dependencies\reactphysics3d\include;$(VULKAN_SDK)\include;$(VULKAN_SDK)\Source\SPIRV-Reflect;%(AdditionalIncludeDirectories) - EditAndContinue - Disabled - false - MultiThreadedDebugDLL - true - stdcpp20 - - - Windows - true - vulkan-1.lib;shaderc_shared.lib;shlwapi.lib;assimp-vc142-mtd.lib;librttr_core_d.lib;spdlogd.lib;%(AdditionalDependencies) - libs;$(VULKAN_SDK)\Lib;..\Dependencies\assimp\lib\Debug;..\Dependencies\assimp\lib\Release;..\Dependencies\RTTR\lib;..\Dependencies\spdlog\lib;%(AdditionalLibraryDirectories) - ..\bin\Debug\SHADE_Engine.lib - - - xcopy /s /r /y /q "$(SolutionDir)/Dependencies/spdlog/bin" "$(OutDir)" - - - - - Use - SHpch.h - Level4 - _LIB;_GLFW_INCLUDE_NONE;MSDFGEN_USE_CPP11;NOMINMAX;SH_API_EXPORT;_RELEASE;%(PreprocessorDefinitions) - src;..\Dependencies\assimp\include;..\Dependencies\imgui;..\Dependencies\imguizmo;..\Dependencies\imnodes;..\Dependencies\msdf;..\Dependencies\msdf\msdfgen;..\Dependencies\spdlog\include;..\Dependencies\tracy;..\Dependencies\VMA\include;..\Dependencies\yamlcpp\include;..\Dependencies\ktx\include;..\Dependencies\RTTR\include;..\Dependencies\reactphysics3d\include;$(VULKAN_SDK)\include;$(VULKAN_SDK)\Source\SPIRV-Reflect;%(AdditionalIncludeDirectories) - Full - true - true - false - true - MultiThreadedDLL - true - stdcpp20 - - - Windows - true - true - vulkan-1.lib;shaderc_shared.lib;shlwapi.lib;assimp-vc142-mt.lib;librttr_core.lib;spdlog.lib;%(AdditionalDependencies) - libs;$(VULKAN_SDK)\Lib;..\Dependencies\assimp\lib\Debug;..\Dependencies\assimp\lib\Release;..\Dependencies\RTTR\lib;..\Dependencies\spdlog\lib;%(AdditionalLibraryDirectories) - ..\bin\Release\SHADE_Engine.lib - - - xcopy /s /r /y /q "$(SolutionDir)/Dependencies/spdlog/bin" "$(OutDir)" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Create - - - - - - - - - - - {88F1A057-74BE-FB62-9DD7-E90A890331F1} - - - {8900D8DD-F5DF-5679-FEF7-E14F6A56BDDA} - - - {38BD587B-248B-4C81-0D1F-BDA7F98B28E6} - - - {2ECAB41A-1A98-A820-032C-1947EF988485} - - - {C0FF640D-2C14-8DBE-F595-301E616989EF} - - - - - - \ No newline at end of file diff --git a/SHADE_Engine/SHADE_Engine.vcxproj.filters b/SHADE_Engine/SHADE_Engine.vcxproj.filters deleted file mode 100644 index 6b8bde7f..00000000 --- a/SHADE_Engine/SHADE_Engine.vcxproj.filters +++ /dev/null @@ -1,619 +0,0 @@ - - - - - {8EEA3EAC-7A8C-6982-6347-7DD64F88F0D2} - - - {1AB26817-067F-C322-2F98-B1CA1BC4F8B0} - - - {EFD23933-5B34-1741-E4A1-5DF350024E00} - - - {261D0942-92A8-7606-9BB9-F9FA07C4D206} - - - {07FEB307-F3F6-D259-1C29-B8DE0881B265} - - - {EE037863-5A8F-E527-63A0-681CCFAA4128} - - - {DBC7D3B0-C769-FE86-B024-12DB9C6585D7} - - - {3A8963B1-262B-8E87-0FE6-A1DBFB2615D8} - - - {8A8E2B37-7646-6D84-DF4D-46E0CB240875} - - - {1653CE33-0220-293F-2B39-17E717655ECD} - - - {92C817CE-7EC1-3620-A7F3-1BA5934B162C} - - - {17C745C0-83DD-4356-CC54-CF7738AA14DE} - - - {51443AC7-3D28-FB1C-A688-F56F928BE59E} - - - {573A6CF2-43C9-F5BB-ECE7-09B7D8550662} - - - {08DBDC43-F4D3-FB95-1D06-E11A095EDBA1} - - - {4AD5CA42-3664-540C-DF82-6807CBF064B2} - - - {FB5EE099-67EA-4D5E-70FB-D052DC05AA5E} - - - {BA26540B-263D-52A1-6FB4-DDC2DB092329} - - - {4B204703-3704-0859-A064-02AC8C67F2DA} - - - {EBA1D3FF-D75C-C3AB-8014-3CF66CAE0D3C} - - - {8CDBA7C9-F8E8-D5AF-81CF-D19AEDDBA166} - - - {2460C057-1070-6C28-7929-D14665585BC1} - - - {FBD334F8-67EA-328E-B061-BEAF1CB70316} - - - {1DD51CAD-8960-8A71-9271-0D66FE7BE671} - - - {57DAB30C-4369-3DD6-EC87-51D1D8F54D7C} - - - {9C0DAFD9-086F-8CE7-91DC-D299FD3CC3A6} - - - {EF2D07CC-DB26-261E-0459-0BA3F0B0052A} - - - {3AEF06DD-A6D2-151D-AFD5-43591B38DC6D} - - - {245F5AB0-1085-2417-F9CA-A9E2E58F49E3} - - - {03DB39DE-EFBE-FA33-581F-F5864422E5B5} - - - {576DF841-4392-47C2-6CDD-2C52586146E0} - - - {75F29FE5-6102-4CB6-CABB-B0D4B6EA3A4F} - - - {5BAB2A92-478F-EBE7-B0EF-E53A9CF2D569} - - - {B3B14D12-9FC1-F9E2-087B-5E01F4A9E87B} - - - {AFF4887C-9B2B-8A0D-4418-7010302E060F} - - - {F1B75745-5D6D-D03A-E661-CA115216C73E} - - - {AC05897C-983C-8A0D-4129-70102D3F060F} - - - {ED6CDF9B-D939-3AA7-0253-284FEE7E6F35} - - - {B3F7140E-1F0C-3DBF-E88D-E01E546139F0} - - - {16CF2D0E-82E3-55BF-4B65-F91EB73852F0} - - - - - Common - - - ECS_Base\Components - - - ECS_Base\Components - - - ECS_Base\Entity - - - ECS_Base\General - - - ECS_Base\General - - - ECS_Base\General - - - ECS_Base\General - - - ECS_Base\General - - - ECS_Base - - - ECS_Base\System - - - ECS_Base\System - - - ECS_Base\System - - - ECS_Base\System - - - Engine - - - Events - - - Events - - - Events - - - Events - - - Filesystem - - - Graphics\Buffers - - - Graphics\Commands - - - Graphics\Commands - - - Graphics\Commands - - - Graphics\Debugging - - - Graphics\Debugging - - - Graphics\Debugging - - - Graphics\Descriptors - - - Graphics\Descriptors - - - Graphics\Descriptors - - - Graphics\Descriptors - - - Graphics\Descriptors - - - Graphics\Devices - - - Graphics\Devices - - - Graphics\Devices - - - Graphics\Framebuffer - - - Graphics\Images - - - Graphics\Images - - - Graphics\Images - - - Graphics\Instance - - - Graphics\MiddleEnd\Interface - - - Graphics\MiddleEnd\Interface - - - Graphics\MiddleEnd\PerFrame - - - Graphics\MiddleEnd\PerFrame - - - Graphics\MiddleEnd\Shaders - - - Graphics\MiddleEnd\Shaders - - - Graphics\MiddleEnd\Shaders - - - Graphics\Pipeline - - - Graphics\Pipeline - - - Graphics\Pipeline - - - Graphics\Pipeline - - - Graphics\Pipeline - - - Graphics\Pipeline - - - Graphics\Queues - - - Graphics\RenderGraph - - - Graphics\Renderpass - - - Graphics\Renderpass - - - Graphics\Renderpass - - - Graphics\Renderpass - - - Graphics\Renderpass - - - Graphics - - - Graphics - - - Graphics - - - Graphics\Shaders\BlockInterface - - - Graphics\Shaders - - - Graphics\Shaders - - - Graphics\Shaders\spirv-reflect - - - Graphics\Swapchain - - - Graphics\Swapchain - - - Graphics\Synchronization - - - Graphics\Synchronization - - - Graphics\VertexDescriptors - - - Graphics\Windowing - - - Graphics\Windowing - - - Graphics\Windowing\Surface - - - Math - - - Math - - - Math - - - Math - - - Math\Vector - - - Math\Vector - - - Math\Vector - - - Meta - - - Resource - - - Resource - - - Resource - - - - - - Scene - - - Scene - - - Scene - - - Tools - - - Tools - - - Tools - - - Tools - - - Tools - - - - - ECS_Base\Components - - - ECS_Base\Components - - - ECS_Base\Entity - - - ECS_Base\System - - - ECS_Base\System - - - ECS_Base\System - - - Engine - - - Events - - - Filesystem - - - Graphics\Buffers - - - Graphics\Commands - - - Graphics\Commands - - - Graphics\Debugging - - - Graphics\Debugging - - - Graphics\Debugging - - - Graphics\Descriptors - - - Graphics\Descriptors - - - Graphics\Descriptors - - - Graphics\Descriptors - - - Graphics\Descriptors - - - Graphics\Devices - - - Graphics\Devices - - - Graphics\Devices - - - Graphics\Framebuffer - - - Graphics\Images - - - Graphics\Images - - - Graphics\Instance - - - Graphics\MiddleEnd\Interface - - - Graphics\MiddleEnd\Interface - - - Graphics\MiddleEnd\PerFrame - - - Graphics\MiddleEnd\PerFrame - - - Graphics\MiddleEnd\Shaders - - - Graphics\MiddleEnd\Shaders - - - Graphics\Pipeline - - - Graphics\Pipeline - - - Graphics\Pipeline - - - Graphics\Pipeline - - - Graphics\Queues - - - Graphics\RenderGraph - - - Graphics\Renderpass - - - Graphics\Renderpass - - - Graphics\Renderpass - - - Graphics - - - Graphics - - - Graphics\Shaders\BlockInterface - - - Graphics\Shaders - - - Graphics\Shaders - - - Graphics\Shaders\spirv-reflect - - - Graphics\Swapchain - - - Graphics\Synchronization - - - Graphics\Synchronization - - - Graphics\VertexDescriptors - - - Graphics\Windowing - - - Graphics\Windowing - - - Graphics\Windowing\Surface - - - Math - - - Math - - - Math - - - Math\Vector - - - Math\Vector - - - Math\Vector - - - Resource - - - - Scene - - - Scene - - - Tools - - - Tools - - - Tools - - - Tools - - - \ No newline at end of file diff --git a/SHADE_Engine/premake5.lua b/SHADE_Engine/premake5.lua index f10369a5..d56d78cf 100644 --- a/SHADE_Engine/premake5.lua +++ b/SHADE_Engine/premake5.lua @@ -12,6 +12,7 @@ project "SHADE_Engine" files { "%{prj.location}/src/**.h", + "%{prj.location}/src/**.hpp", "%{prj.location}/src/**.c", "%{prj.location}/src/**.cpp", "%{prj.location}/src/**.glsl", @@ -31,11 +32,12 @@ project "SHADE_Engine" "%{IncludeDir.tracy}", "%{IncludeDir.VMA}/include", "%{IncludeDir.yamlcpp}", - "%{IncludeDir.ktx}/include", + "%{IncludeDir.SDL}/include", "%{IncludeDir.RTTR}/include", "%{IncludeDir.reactphysics3d}/include", "%{IncludeDir.VULKAN}/include", - "%{IncludeDir.VULKAN}/Source/SPIRV-Reflect" + "%{IncludeDir.VULKAN}/Source/SPIRV-Reflect", + "%{IncludeDir.dotnet}/include", } libdirs @@ -45,6 +47,7 @@ project "SHADE_Engine" "%{IncludeDir.assimp}/lib/Debug", "%{IncludeDir.assimp}/lib/Release", "%{IncludeDir.RTTR}/lib", + "%{IncludeDir.SDL}/lib", "%{IncludeDir.spdlog}/lib" } @@ -56,8 +59,15 @@ project "SHADE_Engine" "reactphysics3d", "imgui", "vulkan-1.lib", + "SDL2.lib", + "SDL2main.lib", "shaderc_shared.lib", - "shlwapi" + "shlwapi.lib" + } + + disablewarnings + { + "4251" } defines @@ -65,7 +75,7 @@ project "SHADE_Engine" "_LIB", "_GLFW_INCLUDE_NONE", "MSDFGEN_USE_CPP11", - "NOMINMAX", + "NOMINMAX", "SH_API_EXPORT" } @@ -85,7 +95,9 @@ project "SHADE_Engine" postbuildcommands { - "xcopy /s /r /y /q \"%{IncludeDir.spdlog}/bin\" \"$(OutDir)\"" + "xcopy /s /r /y /q \"%{IncludeDir.spdlog}/bin\" \"$(OutDir)\"", + "xcopy /r /y /q \"%{IncludeDir.SDL}/lib/SDL2.dll\" \"$(OutDir)\"", + "xcopy /s /r /y /q \"%{IncludeDir.dotnet}/bin\" \"$(OutDir)\"" } warnings 'Extra' diff --git a/SHADE_Engine/src/ECS_Base/Entity/SHEntity.cpp b/SHADE_Engine/src/ECS_Base/Entity/SHEntity.cpp index 6005fb01..edf29ec7 100644 --- a/SHADE_Engine/src/ECS_Base/Entity/SHEntity.cpp +++ b/SHADE_Engine/src/ECS_Base/Entity/SHEntity.cpp @@ -28,7 +28,7 @@ namespace SHADE //SHEntityManager::RemoveEntity(this->entityID); } - EntityID SHEntity::GetEID() noexcept + EntityID SHEntity::GetEID() const noexcept { return this->entityID; } diff --git a/SHADE_Engine/src/ECS_Base/Entity/SHEntity.h b/SHADE_Engine/src/ECS_Base/Entity/SHEntity.h index 685ba99a..9077b0b9 100644 --- a/SHADE_Engine/src/ECS_Base/Entity/SHEntity.h +++ b/SHADE_Engine/src/ECS_Base/Entity/SHEntity.h @@ -78,7 +78,7 @@ namespace SHADE * \return uint32_t * The entityID of this Entity object. ***************************************************************************/ - EntityID GetEID() noexcept; + EntityID GetEID() const noexcept; /*!************************************************************************* * \brief Set the Active object diff --git a/SHADE_Engine/src/FRC/SHFramerateController.cpp b/SHADE_Engine/src/FRC/SHFramerateController.cpp new file mode 100644 index 00000000..d64c6336 --- /dev/null +++ b/SHADE_Engine/src/FRC/SHFramerateController.cpp @@ -0,0 +1,134 @@ +/********************************************************************* + * \file SHFramerateController.cpp + * \author Ryan Wang Nian Jing + * \brief Definition for functions of the framerate controller + * Handles changing of scenes and manages loop (timestep, etc.) + * + * \copyright Copyright (c) 2022 DigiPen Institute of Technology. Reproduction + or disclosure of this file or its contents without the prior written + consent of DigiPen Institute of Technology is prohibited. + *********************************************************************/ + +#include +#include +#include +#include "SHFramerateController.h" +#include "../Tools/SHLogger.h" + +namespace SHADE +{ + //Init statics + double SHFramerateController::fixedTimestep = 0.01; + SHScene* SHFramerateController::previousScene = nullptr; + SHScene* SHFramerateController::currentScene = nullptr; + SHScene* SHFramerateController::nextScene = nullptr; + bool SHFramerateController::toRestart = false; + bool SHFramerateController::toQuit = false; + + //Scene manager loop + void SHFramerateController::Run(SHScene* firstScene) + { + if (firstScene == nullptr) + { + SHLOG_ERROR("Do not pass a nullptr as the firstScene"); + return; + } + + //Set quit and restart flags to false + toQuit = false; + toRestart = false; + + //Set the first scene to run + previousScene = firstScene; + currentScene = firstScene; + nextScene = firstScene; + + while (!toQuit) + { + if (toRestart) + { + //Restart current scene + currentScene = previousScene; + nextScene = previousScene; + toRestart = false; + } + else + { + //Move to a new scene + currentScene->Load(); + } + + //Call init function of current scene + currentScene->Init(); + + //Have an initial value + //This frame time will fluctuate + //SHOULD be larger than the fixed timestep + //TODO this might need to be changed + double variableLastFrameTime = fixedTimestep; + + //Time accumulator for meshing between fixed and variable timesteps + double accumulator = 0.0; + + //Start state loop + while (currentScene == nextScene && !toQuit && !toRestart) + { + //Use of new STL timing functions + //https://en.cppreference.com/w/cpp/chrono + std::chrono::duration deltaTime; + + auto startTime = std::chrono::high_resolution_clock::now(); + + //Whittle down the accumulator by continuously simulating + for (; accumulator > fixedTimestep; accumulator -= fixedTimestep) + { + MSG msg; + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + //TODO change to double + currentScene->Update((float)fixedTimestep); + } + + //Interpolation + //Manage the alpha value well + //https://randomascii.wordpress.com/2012/02/13/dont-store-that-in-a-float/ + //Key points: + //1) Any time you add or subtract floats of widely varying + // magnitudes, you need to watch for loss of precision + //2) Sometimes using double instead of float is the correct + // solution, but often a more stable algorithm is more important + //3) calcT() should probably use double (to give sufficient + // precision after many hours of gameplay) + + //TODO awaiting approval to use this + //double alpha = accumulator / fixedTimestep; + + //assert alpha does not go out of range + + currentScene->Render(); + + auto endTime = std::chrono::high_resolution_clock::now(); + deltaTime = endTime - startTime; + variableLastFrameTime = deltaTime.count(); + + //Increase accumulator + accumulator += variableLastFrameTime; + } + + //Free once out of scene loop + currentScene->Free(); + + //Check if not to restart state + //If so, unload + if (!toRestart) currentScene->Unload(); + + //Shift forward scenes + previousScene = currentScene; + currentScene = nextScene; + } + } +} \ No newline at end of file diff --git a/SHADE_Engine/src/FRC/SHFramerateController.h b/SHADE_Engine/src/FRC/SHFramerateController.h new file mode 100644 index 00000000..26f276d8 --- /dev/null +++ b/SHADE_Engine/src/FRC/SHFramerateController.h @@ -0,0 +1,62 @@ +/********************************************************************* + * \file SHFramerateController.h + * \author Ryan Wang Nian Jing + * \brief Declaration for the framerate controller + * Handles changing of scenes and manages loop (timestep, etc.) + * + * \copyright Copyright (c) 2022 DigiPen Institute of Technology. Reproduction + or disclosure of this file or its contents without the prior written + consent of DigiPen Institute of Technology is prohibited. + *********************************************************************/ + +#ifndef SH_FRAMERATECONTROLLER_H +#define SH_FRAMERATECONTROLLER_H +#pragma once + +#include "../Scene/SHScene.h" + +namespace SHADE +{ + class SHFramerateController + { + private: + //scene pointers + static SHScene* previousScene; + static SHScene* currentScene; + static SHScene* nextScene; + + //Flags + //Whether the flag has been raised for the game to be quit + static bool toQuit; + + //Whether the flag has been raised for the current scene to restart + static bool toRestart; + + public: + //Fixed timestep value for physics. Default at 1/100th of a second. + //Should be lower than the variable refresh rate + static double fixedTimestep; + + //Scene Manager Loop + //This loop is vital to the game because it runs for as long as the game + //runs. Before entering, initialise vital systems for game. After exiting, + //free these vital systems before finishing the main() function and + //terminating the game + //Parameter of firstScene is what scene the game should start with + static void Run(SHScene* firstScene); + + //Set the flag to restart the current game scene + static inline void RestartScene() { toRestart = true; } + + //Set the flag to halt running of the scene manager and quit the game + static inline void QuitGame() { toQuit = true; } + + //Set the next scene to be excuted + //This will tell the scene manager to + //halt execution of the current scene and prepare + //execution of the next + static inline void SetNextScene(SHScene* const next) { nextScene = next; } + }; +} + +#endif \ No newline at end of file diff --git a/SHADE_Engine/src/Scripting/SHDotNetRuntime.cpp b/SHADE_Engine/src/Scripting/SHDotNetRuntime.cpp new file mode 100644 index 00000000..89603524 --- /dev/null +++ b/SHADE_Engine/src/Scripting/SHDotNetRuntime.cpp @@ -0,0 +1,198 @@ +/*************************************************************************************//*! +\file SHDotNetRuntime.cpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 2, 2021 +\brief Contains the definition of the SHDotNetRuntime class. + Implementation of code to set up code for SHDotNetRuntime is based on the + following repository: + https://github.com/mjrousos/SampleCoreCLRHost + +Copyright (C) 2021 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. +*//**************************************************************************************/ +// Precompiled Header +#include +// Primary Header +#include "SHDotNetRuntime.h" +// Standard Library +#include +// External Dependencies +#include // PathRemoveFileSpecA +#include "Tools/SHLogger.h" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Constructors/Destructor */ + /*---------------------------------------------------------------------------------*/ + SHDotNetRuntime::SHDotNetRuntime(bool autoInit) + { + if (autoInit) + { + Init(); + } + } + + SHDotNetRuntime::~SHDotNetRuntime() + { + if (IsLoaded()) + { + try + { + Exit(); + } + catch (std::runtime_error& e) + { + SHLOG_ERROR(e.what()); + } + } + } + + /*---------------------------------------------------------------------------------*/ + /* Lifecycle Functions */ + /*---------------------------------------------------------------------------------*/ + void SHDotNetRuntime::Init() + { + // State checking, in case there was an unload before, we must ensure that the state is valid + if (initialised) + throw std::runtime_error("[DotNetRuntime] Failed to initialise as it was already initialised or was deinitialised into an invalid state."); + + // Get the current executable directory + std::string runtimePath(MAX_PATH, '\0'); + GetModuleFileNameA(nullptr, runtimePath.data(), MAX_PATH); + PathRemoveFileSpecA(runtimePath.data()); + // Since PathRemoveFileSpecA() removes from data(), the size is not updated, so we must manually update it + runtimePath.resize(std::strlen(runtimePath.data())); + + // Do not need to load the library if it was previously loaded + if (coreClr == nullptr) + { + // Construct the CoreCLR path + std::string coreClrPath(runtimePath); // Works + coreClrPath += "\\coreclr.dll"; + + // Load the CoreCLR DLL + coreClr = LoadLibraryExA(coreClrPath.c_str(), nullptr, 0); + if (!coreClr) + { + std::ostringstream oss; + oss << "[DotNetRuntime] Error #" << GetLastError() << " Failed to load CoreCLR from \"" << coreClrPath << "\"\n"; + throw std::runtime_error(oss.str()); + } + + // Step 2: Get CoreCLR hosting functions + initializeCoreClr = getCoreClrFunctionPtr("coreclr_initialize"); + createManagedDelegate = getCoreClrFunctionPtr("coreclr_create_delegate"); + shutdownCoreClr = getCoreClrFunctionPtr("coreclr_shutdown"); + } + + // Step 3: Construct AppDomain properties used when starting the runtime + // Construct the trusted platform assemblies (TPA) list + // This is the list of assemblies that .NET Core can load as + // trusted system assemblies (similar to the .NET Framework GAC). + // For this host (as with most), assemblies next to CoreCLR will + // be included in the TPA list + std::string tpaList = buildTpaList(runtimePath); + + // Define CoreCLR properties + std::array propertyKeys = + { + "TRUSTED_PLATFORM_ASSEMBLIES", // Trusted assemblies (like the GAC) + "APP_PATHS", // Directories to probe for application assemblies + // "APP_NI_PATHS", // Directories to probe for application native images (not used in this sample) + // "NATIVE_DLL_SEARCH_DIRECTORIES", // Directories to probe for native dlls (not used in this sample) + }; + std::array propertyValues = + { + tpaList.c_str(), + runtimePath.c_str() + }; + + // Step 4: Start the CoreCLR runtime + int result = initializeCoreClr + ( + runtimePath.c_str(), // AppDomain base path + "SHADEHost", // AppDomain friendly name + static_cast(propertyKeys.size()), // Property count + propertyKeys.data(), // Property names + propertyValues.data(), // Property values + &hostHandle, // Host handle + &domainId // AppDomain ID + ); + + // Check if intiialization of CoreCLR failed + throwIfFailed("[DotNetRuntime] Failed to initialize CoreCLR.", result); + + initialised = true; + SHLOG_INFO("[DotNetRuntime] Successfully loaded the .NET 6.0 Runtime."); + } + + void SHDotNetRuntime::Exit() + { + // State checking, in case there was an unload before, we must ensure that the state is valid + if (!initialised) + throw std::runtime_error("[DotNetRuntime] Failed to deinitialise as it was not initialised before."); + + // Shutdown CoreCLR + int result = shutdownCoreClr(hostHandle, domainId); + throwIfFailed("[DotNetRuntime] Failed to shut down CoreCLR.", result); + + // Unset pointers + hostHandle = nullptr; + domainId = 0; + initialised = false; + + SHLOG_INFO("[DotNetRuntime] Successfully shut down the .NET 6.0 Runtime."); + } + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + std::string SHDotNetRuntime::buildTpaList(const std::string& directory) + { + // Constants + static const std::string SEARCH_PATH = directory + "\\*.dll"; + static constexpr char PATH_DELIMITER = ';'; + + // Create a osstream object to compile the string + std::ostringstream tpaList; + + // Search the current directory for the TPAs (.DLLs) + WIN32_FIND_DATAA findData; + HANDLE fileHandle = FindFirstFileA(SEARCH_PATH.c_str(), &findData); + if (fileHandle != INVALID_HANDLE_VALUE) + { + do + { + // Append the assembly to the list + tpaList << directory << '\\' << findData.cFileName << PATH_DELIMITER; + + // Note that the CLR does not guarantee which assembly will be loaded if an assembly + // is in the TPA list multiple times (perhaps from different paths or perhaps with different NI/NI.dll + // extensions. Therefore, a real host should probably add items to the list in priority order and only + // add a file if it's not already present on the list. + // + // For this simple sample, though, and because we're only loading TPA assemblies from a single path, + // and have no native images, we can ignore that complication. + } + while (FindNextFileA(fileHandle, &findData)); + FindClose(fileHandle); + } + + return tpaList.str(); + } + + void SHDotNetRuntime::throwIfFailed(const std::string& errMsg, int resultCode) + { + if (resultCode < 0) + { + std::ostringstream oss; + oss << std::hex << std::setfill('0') << std::setw(8) + << errMsg + << " Error 0x" << resultCode << "\n"; + throw std::runtime_error(oss.str()); + } + } +} diff --git a/SHADE_Engine/src/Scripting/SHDotNetRuntime.h b/SHADE_Engine/src/Scripting/SHDotNetRuntime.h new file mode 100644 index 00000000..efb9e54b --- /dev/null +++ b/SHADE_Engine/src/Scripting/SHDotNetRuntime.h @@ -0,0 +1,207 @@ +/*************************************************************************************//*! +\file SHDotNetRuntime.h +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 2, 2021 +\brief Contains the interface of a wrapper class for interfacing with the + .NET 5 Runtime. + +Copyright (C) 2021 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 + +// Standard Libraries +#include // std::setfill, std::setw +#include // std::runtime_error +#include // std::string +#include // std::ostringstream +// External Dependencies +#include // HMODULE +#include // coreclr_* + +namespace SHADE +{ + /*************************************************************************************/ + /*! + + class SHDotNetRuntime + + \brief + Class that encapsulates the state of the .NET Core Runtime lifecycle. + + */ + /*************************************************************************************/ + + class SHDotNetRuntime + { + public: + /*---------------------------------------------------------------------------------*/ + /* Constructors/Destructor */ + /*---------------------------------------------------------------------------------*/ + /***********************************************************************************/ + /*! + + \brief + Default constructor that immediately initializes the CoreCLR. + + \param autoInit + If true, loads the CoreCLR by calling Init(). + + */ + /***********************************************************************************/ + SHDotNetRuntime(bool autoInit = true); + /***********************************************************************************/ + /*! + + \brief + Destructor that unloads the CoreCLR if it has not been unloaded yet. + + */ + /***********************************************************************************/ + ~SHDotNetRuntime(); + + // Disallow copy and moving + SHDotNetRuntime(const SHDotNetRuntime&) = delete; + SHDotNetRuntime(SHDotNetRuntime&&) = delete; + + /*----------------------------------------------------------------------------------*/ + /* Lifecycle Functions */ + /*----------------------------------------------------------------------------------*/ + /***********************************************************************************/ + /*! + + \brief + Loads the CoreCLR and grabs pointers to bootstrapping functions and kickstarts the + CoreCLR. + + \throws std::runtime_error + Thrown if there is a failure in loading the CLR and related functions. + + */ + /***********************************************************************************/ + void Init(); + /***********************************************************************************/ + /*! + + \brief + Unloads the CoreCLR. + + \throws std::runtime_error + Thrown if there is a failure in unloading the CLR. + + */ + /***********************************************************************************/ + void Exit(); + + /*----------------------------------------------------------------------------------*/ + /* Usage Functions */ + /*----------------------------------------------------------------------------------*/ + /***********************************************************************************/ + /*! + + \brief + Checks if the DotNetRuntime has successfully been initialised. + + \return + True if this DotNetRuntime has been initialised. + + */ + /***********************************************************************************/ + inline bool IsLoaded() const noexcept { return initialised; } + + /***********************************************************************************/ + /*! + + \brief + Retrieves a function pointer from the a CLR assembly based on the specified + assembly, type and function names. + + \tparam FunctionType + Type of the function pointer that the specified function name will provide. + \param assemblyName + Name of the CoreCLR assembly that contains the function. + \param typeName + Name of the CoreCLR type in the assembly that contains the function. Nested types + are separated by a period(.). + \param functionName + Name of the CoreCLR function to get a pointer to. + \return + Pointer to the function in the assembly that was specified. + + */ + /***********************************************************************************/ + template + FunctionType GetFunctionPtr(const std::string_view& assemblyName, + const std::string_view& typeName, + const std::string_view& functionName) const; + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + bool initialised = false; + // References to CoreCLR key components + HMODULE coreClr = nullptr; + void* hostHandle = nullptr; + unsigned int domainId = 0; + // Function Pointers to CoreCLR functions + coreclr_initialize_ptr initializeCoreClr = nullptr; + coreclr_create_delegate_ptr createManagedDelegate = nullptr; + coreclr_shutdown_ptr shutdownCoreClr = nullptr; + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + /***********************************************************************************/ + /*! + + \brief + Retrieves a function pointer from the CoreCLR based on the specified + function name. + + \tparam FunctionType + Type of the function pointer that the specified function name will provide. + \param functionName + Name of the CoreCLR function to get a pointer to. + \return + Pointer to the function in the CoreCLR that was specified. + + */ + /***********************************************************************************/ + template + FunctionType getCoreClrFunctionPtr(const std::string& functionName); + /***********************************************************************************/ + /*! + + \brief + Compiles a semicolon separated string of trusted platform assemblies by + searching the specified directory. + + \param directory + Path to the directory where the trusted platform assemblies reside. + \return + Semicolon separated string of trusted platform assemblies. + + */ + /***********************************************************************************/ + static std::string buildTpaList(const std::string& directory); + /***********************************************************************************/ + /*! + + \brief + Takes in a Win32 result code and throws an exception it if there is an error. + + \param errMsg + Error message to display if the resultCode is a failure code. + \param resultCode + Result code of the function to check. + + */ + /***********************************************************************************/ + static void throwIfFailed(const std::string& errMsg, int resultCode); + }; +} + +#include "SHDotNetRuntime.hpp" diff --git a/SHADE_Engine/src/Scripting/SHDotNetRuntime.hpp b/SHADE_Engine/src/Scripting/SHDotNetRuntime.hpp new file mode 100644 index 00000000..ae8f28e5 --- /dev/null +++ b/SHADE_Engine/src/Scripting/SHDotNetRuntime.hpp @@ -0,0 +1,61 @@ +/*************************************************************************************//*! +\file SHDotNetRuntime.hpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 2, 2021 +\brief Contains the implementation of the template functions of the + DotNetRuntime class. + +Copyright (C) 2021 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 + +// Primary Include +#include "SHDotNetRuntime.h" + +namespace SHADE +{ + template + FunctionType SHDotNetRuntime::GetFunctionPtr(const std::string_view & assemblyName, + const std::string_view & typeName, + const std::string_view & functionName) const + { + FunctionType managedDelegate = nullptr; + int result = createManagedDelegate + ( + hostHandle, + domainId, + assemblyName.data(), + typeName.data(), + functionName.data(), + reinterpret_cast(&managedDelegate) + ); + + // Check if it failed + if (result < 0) + { + std::ostringstream oss; + oss << std::hex << std::setfill('0') << std::setw(8) + << "[DotNetRuntime] Failed to get pointer to function \"" + << typeName << "." << functionName << "\" in assembly (" << assemblyName << "). " + << "Error 0x" << result << "\n"; + throw std::runtime_error(oss.str()); + } + + return managedDelegate; + } + template + FunctionType SHDotNetRuntime::getCoreClrFunctionPtr(const std::string& functionName) + { + FunctionType fPtr = reinterpret_cast(GetProcAddress(coreClr, functionName.c_str())); + if (!fPtr) + { + std::ostringstream oss; + oss << "[DotNetRuntime] Unable to get pointer to function: \"" << functionName << "\""; + throw std::runtime_error(oss.str()); + } + return fPtr; + } +} diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp new file mode 100644 index 00000000..47c722dd --- /dev/null +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp @@ -0,0 +1,506 @@ +/************************************************************************************//*! +\file SHScriptEngine.cpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Sep 17, 2021 +\brief Contains the implementation for ScriptEngine class. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Headers +#include +// Primary Header +#include "SHScriptEngine.h" +// Standard Library +#include // std::fstream +#include // std::filesystem::canonical, std::filesystem::remove +// Project Headers +#include "Tools/SHLogger.h" +#include "Tools/SHStringUtils.h" + +namespace SHADE +{ + /*--------------------------------------------------------------------------------*/ + /* Static Definitions */ + /*--------------------------------------------------------------------------------*/ + const std::string SHScriptEngine::DEFAULT_CSHARP_NAMESPACE = std::string("SHADE"); + SHDotNetRuntime SHScriptEngine::dotNet { false }; + SHScriptEngine::CsFuncPtr SHScriptEngine::csEngineInit = nullptr; + SHScriptEngine::CsFuncPtr SHScriptEngine::csEngineLoadScripts = nullptr; + SHScriptEngine::CsFuncPtr SHScriptEngine::csEngineUnloadScripts = nullptr; + SHScriptEngine::CsFuncPtr SHScriptEngine::csEngineReloadScripts = nullptr; + SHScriptEngine::CsFuncPtr SHScriptEngine::csEngineExit = nullptr; + SHScriptEngine::CsFuncPtr SHScriptEngine::csScriptsFrameSetUp = nullptr; + SHScriptEngine::CsFuncPtr SHScriptEngine::csScriptsExecuteFixedUpdate = nullptr; + SHScriptEngine::CsFuncPtr SHScriptEngine::csScriptsExecuteUpdate = nullptr; + SHScriptEngine::CsFuncPtr SHScriptEngine::csScriptsExecuteLateUpdate = nullptr; + SHScriptEngine::CsFuncPtr SHScriptEngine::csScriptsFrameCleanUp = nullptr; + SHScriptEngine::CsScriptManipFuncPtr SHScriptEngine::csScriptsAdd = nullptr; + SHScriptEngine::CsScriptBasicFuncPtr SHScriptEngine::csScriptsRemoveAll = nullptr; + SHScriptEngine::CsScriptOptionalFuncPtr SHScriptEngine::csScriptsRemoveAllImmediately = nullptr; + SHScriptEngine::CsScriptSerialiseFuncPtr SHScriptEngine::csScriptsSerialise = nullptr; + SHScriptEngine::CsScriptDeserialiseFuncPtr SHScriptEngine::csScriptDeserialise = nullptr; + SHScriptEngine::CsScriptSerialiseYamlFuncPtr SHScriptEngine::csScriptsSerialiseYaml = nullptr; + SHScriptEngine::CsScriptSerialiseYamlFuncPtr SHScriptEngine::csScriptDeserialiseYaml = nullptr; + SHScriptEngine::CsScriptEditorFuncPtr SHScriptEngine::csEditorRenderScripts = nullptr; + + /*---------------------------------------------------------------------------------*/ + /* Lifecycle Functions */ + /*---------------------------------------------------------------------------------*/ + void SHScriptEngine::Init() + { + // Do not allow initialization if already initialised + if (dotNet.IsLoaded()) + { + SHLOG_ERROR("[ScriptEngine] Attempted to initialise an already loaded DotNetRuntime."); + return; + } + + dotNet.Init(); + + // Load all the helpers + loadFunctions(); + + // Generate script assembly if it hasn't been before + if (!fileExists(std::string(MANAGED_SCRIPT_LIB_NAME) + ".dll")) + { + BuildScriptAssembly(); + } + + // Initialise the CSharp Engine + csEngineInit(); + + // Link events + // - Entity Destruction + /*onEntityDestroy = [this](const SHEntity& e) + { + csScriptsRemoveAll(e.GetEID()); + csGOLibNotifyDestroyEntity(e.GetEID()); + }; + ECS::OnEntityDestroy += onEntityDestroy;*/ + } + void SHScriptEngine::UnloadScriptAssembly() + { + csEngineUnloadScripts(); + } + void SHScriptEngine::LoadScriptAssembly() + { + csEngineLoadScripts(); + } + void SHScriptEngine::ReloadScriptAssembly() + { + csEngineReloadScripts(); + } + void SHScriptEngine::ExecuteFixedUpdates() + { + csScriptsExecuteFixedUpdate(); + } + + void SHScriptEngine::Exit() + { + // Do not allow deinitialization if not initialised + if (!dotNet.IsLoaded()) + { + SHLOG_ERROR("[ScriptEngine] Attempted to clean up an unloaded DotNetRuntime."); + return; + } + + // Unlink events + /*ECS::OnEntityCreated -= onEntityCreate; + ECS::OnEntityDestroy -= onEntityDestroy;*/ + + // Clean up the CSharp Engine + csEngineExit(); + + // Shut down the CLR + dotNet.Exit(); + } + + /*---------------------------------------------------------------------------------*/ + /* Script Manipulation Functions */ + /*---------------------------------------------------------------------------------*/ + bool SHScriptEngine::AddScript(const SHEntity& entity, const std::string_view& scriptName) + { + return csScriptsAdd(entity.GetEID(), scriptName.data()); + } + void SHScriptEngine::RemoveAllScripts(const SHEntity& entity) + { + csScriptsRemoveAll(entity.GetEID()); + } + void SHScriptEngine::RemoveAllScriptsImmediately(const SHEntity& entity, bool callOnDestroy) + { + csScriptsRemoveAllImmediately(entity.GetEID(), callOnDestroy); + } + + /*---------------------------------------------------------------------------------*/ + /* Script Serialisation Functions */ + /*---------------------------------------------------------------------------------*/ + std::string SHScriptEngine::SerialiseScripts(const SHEntity& entity) + { + // Create buffer needed to store serialised script data + constexpr int BUFFER_SIZE = 10240; + std::unique_ptr buffer { new char[BUFFER_SIZE] }; + std::memset(buffer.get(), 0, BUFFER_SIZE); + + // Attempt to serialise the script + std::string result; + if (csScriptsSerialise(entity.GetEID(), buffer.get(), BUFFER_SIZE)) + { + result = std::string(buffer.get()); + } + else + { + SHLOG_ERROR("[ScriptEngine] Failed to serialise scripts as string buffer is too small!"); + } + + // Return an empty string since we failed to serialise + return result; + } + + /*---------------------------------------------------------------------------------*/ + /* Script Serialisation Functions */ + /*---------------------------------------------------------------------------------*/ + void SHScriptEngine::DeserialiseScript(const SHEntity& entity, const std::string& yaml) + { + csScriptDeserialise(entity.GetEID(), yaml.c_str()); + } + + /*---------------------------------------------------------------------------------*/ + /* Script Editor Functions */ + /*---------------------------------------------------------------------------------*/ + void SHScriptEngine::RenderScriptsInInspector(const SHEntity& entity) + { + csEditorRenderScripts(entity.GetEID()); + } + + /*---------------------------------------------------------------------------------*/ + /* Static Utility Functions */ + /*---------------------------------------------------------------------------------*/ + bool SHScriptEngine::BuildScriptAssembly(bool debug) + { + constexpr std::string_view BUILD_LOG_PATH = "../Build.log"; + + // Generate csproj file if it doesn't exist + static const std::filesystem::path CSPROJ_PATH = "../SHADE_Scripting.csproj"; + if (!std::filesystem::exists(CSPROJ_PATH)) + { + GenerateScriptsCsProjFile(CSPROJ_PATH); + } + + // Prepare directory (delete useless files) + deleteFolder("net6.0"); + deleteFolder("ref"); + deleteFolder("../SHADE_Scripting"); + deleteFolder("../obj"); + + // Attempt to build the assembly + std::ostringstream oss; + oss << "[ScriptEngine] Building " << (debug ? " debug " : "") << "Managed Script Assembly (" << MANAGED_SCRIPT_LIB_NAME << ")!"; + SHLOG_INFO(oss.str()); + oss.str(""); + const bool BUILD_SUCCESS = execProcess + ( + L"C:\\Windows\\system32\\cmd.exe", + L"/K \"dotnet build \"../SHADE_Scripting.csproj\" -c Debug -o \"./tmp/\" -fl -flp:LogFile=build.log;Verbosity=quiet & exit\"" + ) == 0; + if (BUILD_SUCCESS) + { + // Copy to built dll to the working directory and replace + std::filesystem::copy_file("./tmp/SHADE_Scripting.dll", "SHADE_Scripting.dll", std::filesystem::copy_options::overwrite_existing); + + oss << "[ScriptEngine] Successfully built Managed Script Assembly (" << MANAGED_SCRIPT_LIB_NAME << ")!"; + SHLOG_INFO(oss.str()); + } + else + { + oss << "[ScriptEngine] Failed to build Managed Script Assembly (" << MANAGED_SCRIPT_LIB_NAME << ")!"; + SHLOG_ERROR(oss.str()); + } + + // Clean up built files + deleteFolder("./tmp"); + + // Read the build log and output to the console + dumpBuildLog(BUILD_LOG_PATH); + // Delete the build log file since we no longer need it + deleteFile(BUILD_LOG_PATH); + + return BUILD_SUCCESS; + } + + void SHScriptEngine::GenerateScriptsCsProjFile(const std::filesystem::path& path) + { + // Sample + static std::string_view FILE_CONTENTS = +"\n\ + \n\ + net6.0\n\ + x64\n\ + Release;Debug\n\ + \n\ + \n\ + .\\bin_Release-x64\n\ + x64\n\ + \n\ + \n\ + .\\bin_Debug-x64\n\ + x64\n\ + DEBUG;TRACE\n\ + false\n\ + full\n\ + true\n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + .\\bin\\SHADE_Managed.dll\n\ + \n\ + \n\ +"; + + // Attempt to create the file + std::ofstream file(path); + if (!file.is_open()) + throw std::runtime_error("Unable to create CsProj file!"); + + // Fill the file + file << FILE_CONTENTS; + + // Close + file.close(); + } + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + void SHScriptEngine::loadFunctions() + { + std::ostringstream oss; + oss << "[ScriptEngine] Loading \"" << DEFAULT_CSHARP_LIB_NAME << "\" CLR library."; + SHLOG_INFO(oss.str()); + + // Load functions + csEngineInit = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".EngineInterface", + "Init" + ); + csEngineLoadScripts = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".EngineInterface", + "LoadScriptAssembly" + ); + csEngineUnloadScripts = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".EngineInterface", + "UnloadScriptAssembly" + ); + csEngineReloadScripts = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".EngineInterface", + "ReloadScriptAssembly" + ); + csEngineExit = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".EngineInterface", + "Exit" + ); + csScriptsFrameSetUp = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", + "FrameSetUp" + ); + csScriptsExecuteFixedUpdate = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", + "ExecuteFixedUpdate" + ); + csScriptsExecuteUpdate = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", + "ExecuteUpdate" + ); + csScriptsExecuteLateUpdate = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", + "ExecuteLateUpdate" + ); + csScriptsFrameCleanUp = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", + "FrameCleanUp" + ); + csScriptsAdd = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", + "AddScriptViaName" + ); + csScriptsRemoveAll = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", + "RemoveAllScripts" + ); + csScriptsRemoveAllImmediately = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", + "RemoveAllScriptsImmediately" + ); + /*csScriptsSerialise = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", + "SerialiseScripts" + ); + csScriptsSerialiseYaml = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", + "SerialiseScriptsYaml" + ); + csScriptDeserialise = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", + "DeserialiseScript" + ); + csScriptDeserialiseYaml = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", + "SerialiseScriptsYaml" + ); + csEditorRenderScripts = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".Editor", + "RenderScriptsInInspector" + );*/ + } + + void SHScriptEngine::dumpBuildLog(const std::string_view& buildLogPath) + { + std::ifstream buildLog(buildLogPath); + + // Fail to open + if (!buildLog.is_open()) + return; + + // Process line by line + std::string line; + while (std::getline(buildLog, line)) + { + if (line.find("error") != line.npos) + { + SHLOG_ERROR(line); + } + else + { + SHLOG_WARNING(line); + } + } + } + void SHScriptEngine::deleteFile(const std::string_view& filePath) + { + try + { + std::filesystem::remove(std::filesystem::canonical(filePath)); + } + catch (...) {} // Ignore deletion failures + } + + void SHScriptEngine::deleteFolder(const std::string_view& filePath) + { + try + { + std::filesystem::remove_all(std::filesystem::canonical(filePath)); + } + catch (...) {} // Ignore deletion failures + } + + bool SHScriptEngine::fileExists(const std::string_view& filePath) + { + std::error_code error; + if (std::filesystem::exists(filePath, error)) + { + return true; + } + return false; + } + + DWORD SHScriptEngine::execProcess(const std::wstring& path, const std::wstring& args) + { + STARTUPINFOW startInfo; + PROCESS_INFORMATION procInfo; + ZeroMemory(&startInfo, sizeof(startInfo)); + ZeroMemory(&procInfo, sizeof(procInfo)); + startInfo.cb = sizeof(startInfo); + + std::wstring argsWstr = args; + + // Start Process + const auto SUCCESS = CreateProcess + ( + path.data(), argsWstr.data(), + nullptr, nullptr, false, NULL, nullptr, nullptr, + &startInfo, &procInfo + ); + + // Error Check + if (!SUCCESS) + { + auto err = GetLastError(); + std::ostringstream oss; + oss << "[ScriptEngine] Failed to launch process. Error code: " << std::hex << err + << " (" << SHStringUtils::GetWin32ErrorMessage(err) << ")"; + throw std::runtime_error(oss.str()); + } + + // Wait for execution to end + DWORD status; + while (true) + { + const auto SUCCESS = GetExitCodeProcess(procInfo.hProcess, &status); + if (!SUCCESS) + { + auto err = GetLastError(); + std::ostringstream oss; + oss << "[ScriptEngine] Failed to query process. Error code: " << std::hex << err + << " (" << SHStringUtils::GetWin32ErrorMessage(err) << ")"; + throw std::runtime_error(oss.str()); + } + + // Break only if process ends + if (status != STILL_ACTIVE) + { + CloseHandle(procInfo.hProcess); + CloseHandle(procInfo.hThread); + return status; + } + } + } + +} diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.h b/SHADE_Engine/src/Scripting/SHScriptEngine.h new file mode 100644 index 00000000..442c0053 --- /dev/null +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.h @@ -0,0 +1,247 @@ +/************************************************************************************//*! +\file ScriptEngine.h +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Sep 17, 2021 +\brief Contains the interface for ScriptEngine class. + +Copyright (C) 2021 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 + +// STL Includes +#include + +// Project Headers +#include "SHDotNetRuntime.h" +#include "ECS_Base/SHECSMacros.h" +#include "ECS_Base/Entity/SHEntity.h" +#include "SH_API.h" + +namespace SHADE +{ + /// + /// Manages initialisation of the DotNetRuntime and interfacing with CLR code written + /// and executed on .NET. + /// + class SH_API SHScriptEngine + { + public: + /*-----------------------------------------------------------------------------*/ + /* Constructor */ + /*-----------------------------------------------------------------------------*/ + SHScriptEngine() = delete; + + /*-----------------------------------------------------------------------------*/ + /* Lifecycle Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Initialises the DotNetRuntime and retrieves function pointers to all + /// functions on the CLR used to interface with the engine. + /// + static void Init(); + /// + /// Loads the managed script assembly. Ensure this is only called after + /// UnloadScriptAssembly() has been called. + /// + static void UnloadScriptAssembly(); + /// + /// Unloads the managed script assembly. + /// Take note that this will clear all existing scripts, ensure that the scene + /// is saved before doing so. + /// + static void LoadScriptAssembly(); + /// + /// Reloads the managed script assembly. + /// Take note that this will clear all existing scripts, ensure that the scene + /// is saved before doing so. + /// + static void ReloadScriptAssembly(); + /// + /// Executes the FixedUpdate()s of the Scripts that are attached to + /// Entities. + /// + static void ExecuteFixedUpdates(); + /// + /// Shuts down the DotNetRuntime. + /// + static void Exit(); + + /*-----------------------------------------------------------------------------*/ + /* Script Manipulation Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Adds a Script to a specified Entity. Note that while you can call this + /// multiple times on a specified Entity, it will work for all intents and + /// purposes but GetScript<T>() (C# only) currently only + /// gives you the first Script added of the specified type. + /// + /// The entity to add a script to. + /// Type name of the script to add. + /// + /// True if successfully added. False otherwise with the error logged to the + /// console. + /// + static bool AddScript(const SHEntity& entity, const std::string_view& scriptName); + /// + /// Removes all Scripts attached to the specified Entity. Does not do anything + /// if the specified Entity is invalid or does not have any Scripts + /// attached. + /// + /// The entity to remove the scripts from. + static void RemoveAllScripts(const SHEntity& entity); + /// + /// Removes all Scripts attached to the specified Entity. Unlike + /// RemoveAllScripts(), this removes all the scripts immediately. + /// Does not do anything if the specified Entity is invalid or does not have any + /// Scripts attached. + /// + /// The entity to remove the scripts from. + /// + /// Whether or not to call OnDestroy on the scripts. This is ignored if not in + /// play mode. + /// + static void RemoveAllScriptsImmediately(const SHEntity& entity, bool callOnDestroy); + + /*-----------------------------------------------------------------------------*/ + /* Script Serialisation Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Generates a JSON string that represents the set of Scripts attached to the + /// specified Entity. + /// + /// The Entity to Serialise. + /// + /// String that represents the set of scripts attached to the specified Entity. + /// + static std::string SerialiseScripts(const SHEntity& entity); + /// + /// Loads the specified JSON string and creates a Script for the specified Entity + /// based on the specified JSON string. + /// + /// The Entity to deserialise a Script on to. + /// + /// The YAML string that represents the Script to load into the Entity. + /// + static void DeserialiseScript(const SHEntity& entity, const std::string& yaml); + + /*-----------------------------------------------------------------------------*/ + /* Script Editor Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Renders the set of attached Scripts for the specified Entity into the + /// inspector. + ///
+ /// This function is meant for consumption from native code in the inspector + /// rendering code. + ///
+ /// The Entity to render the Scripts of. + static void RenderScriptsInInspector(const SHEntity& entity); + + /*-----------------------------------------------------------------------------*/ + /* Static Utility Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Utilises execution of a external batch file for invoking the dotnet build + /// tool to compile C# scripts in the Assets folder into the SHADE_Scripting + /// C# assembly DLL. + /// + /// + /// Whether or not a debug build will be built. Only debug built C# assemblies + /// can be debugged. + /// + /// Whether or not the build succeeded. + static bool BuildScriptAssembly(bool debug = false); + /// + /// Generates a .csproj file for editing and compiling the C# scripts. + /// + /// File path to the generated file. + static void GenerateScriptsCsProjFile(const std::filesystem::path& path); + + private: + /*-----------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------*/ + using CsFuncPtr = void(*)(void); + using CsScriptManipFuncPtr = bool(*)(EntityID, const char*); + using CsScriptBasicFuncPtr = void(*)(EntityID); + using CsScriptOptionalFuncPtr = void(*)(EntityID, bool); + using CsScriptSerialiseFuncPtr = bool(*)(EntityID, char*, int); + using CsScriptDeserialiseFuncPtr = bool(*)(EntityID, const char*); + using CsScriptSerialiseYamlFuncPtr = bool(*)(EntityID, void*); + using CsScriptEditorFuncPtr = void(*)(EntityID); + + /*-----------------------------------------------------------------------------*/ + /* Constants */ + /*-----------------------------------------------------------------------------*/ + static constexpr std::string_view DEFAULT_CSHARP_LIB_NAME = "SHADE_Managed"; + static constexpr std::string_view MANAGED_SCRIPT_LIB_NAME = "SHADE_Scripting"; + static const std::string DEFAULT_CSHARP_NAMESPACE; + + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + static SHDotNetRuntime dotNet; + // Function Pointers to CLR Code + // - Engine Lifecycle + static CsFuncPtr csEngineInit; + static CsFuncPtr csEngineLoadScripts; + static CsFuncPtr csEngineUnloadScripts; + static CsFuncPtr csEngineReloadScripts; + static CsFuncPtr csEngineExit; + // - Scripts Store + static CsFuncPtr csScriptsFrameSetUp; + static CsFuncPtr csScriptsExecuteFixedUpdate; + static CsFuncPtr csScriptsExecuteUpdate; + static CsFuncPtr csScriptsExecuteLateUpdate; + static CsFuncPtr csScriptsFrameCleanUp; + static CsScriptManipFuncPtr csScriptsAdd; + static CsScriptBasicFuncPtr csScriptsRemoveAll; + static CsScriptOptionalFuncPtr csScriptsRemoveAllImmediately; + static CsScriptSerialiseFuncPtr csScriptsSerialise; + static CsScriptDeserialiseFuncPtr csScriptDeserialise; + static CsScriptSerialiseYamlFuncPtr csScriptsSerialiseYaml; + static CsScriptSerialiseYamlFuncPtr csScriptDeserialiseYaml; + // - Editor + static CsScriptEditorFuncPtr csEditorRenderScripts; + // Delegates + /*ECS::EntityEvent::Delegate onEntityCreate; + ECS::EntityEvent::Delegate onEntityDestroy;*/ + + /*-----------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Loads all the function pointers to CLR code that we need to execute. + /// + static void loadFunctions(); + /// + /// Reads the file via the specified path that represents a build log of error + /// and warning messages. + /// + /// + /// File path to the build log of script builds done by BuildScriptAssembly() to + /// dump and process. + /// + static void dumpBuildLog(const std::string_view& buildLogPath); + /// + /// Deletes the file as specified by the file path. + /// + /// File path to the file to delete. + static void deleteFile(const std::string_view& filePath); + /// + /// Deletes the folder and all files in it as specified by the file path. + /// + /// File path to the file to delete. + static void deleteFolder(const std::string_view& filePath); + /// + /// Checks if a specified file exists. + /// + /// File path to the file to check. + /// True if the file exists + static bool fileExists(const std::string_view& filePath); + static DWORD execProcess(const std::wstring& path, const std::wstring& args); + }; +} diff --git a/SHADE_Engine/src/Tools/SHStringUtils.cpp b/SHADE_Engine/src/Tools/SHStringUtils.cpp new file mode 100644 index 00000000..a2594888 --- /dev/null +++ b/SHADE_Engine/src/Tools/SHStringUtils.cpp @@ -0,0 +1,52 @@ +/************************************************************************************//*! +\file StringUtilities.cpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 29, 2021 +\brief Contains the definition of functions for working with strings. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Header +#include +// Primary Header +#include "SHStringUtils.h" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Utility Functions */ + /*---------------------------------------------------------------------------------*/ + std::vector SHStringUtils::Split(const std::string& str, const char& delim) + { + return Split(str, delim); + } + std::vector SHStringUtils::Split(const std::wstring& str, const wchar_t& delim) + { + return Split(str, delim); + } + std::string SHStringUtils::WstrToStr(const std::wstring& wstr) + { + static std::vector buffer; + const int STR_SIZE = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.size()), nullptr, 0, nullptr, nullptr) + 1 /* Null Terminator */; + buffer.resize(STR_SIZE); + WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.size()), buffer.data(), MAX_PATH, nullptr, nullptr); + return std::string(buffer.data()); + } + std::wstring SHStringUtils::StrToWstr(const std::string& str) + { + static std::vector buffer; + const int WSTR_SIZE = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.size()), nullptr, 0) + 1 /* Null Terminator */; + buffer.resize(WSTR_SIZE); + MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.size()), buffer.data(), WSTR_SIZE); + return std::wstring(buffer.data()); + } + + std::string SHStringUtils::GetWin32ErrorMessage(unsigned long errorCode) + { + return std::system_category().message(errorCode); + } + +} \ No newline at end of file diff --git a/SHADE_Engine/src/Tools/SHStringUtils.h b/SHADE_Engine/src/Tools/SHStringUtils.h new file mode 100644 index 00000000..1c895b99 --- /dev/null +++ b/SHADE_Engine/src/Tools/SHStringUtils.h @@ -0,0 +1,81 @@ +/************************************************************************************//*! +\file StringUtilities.h +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 29, 2021 +\brief Contains the declaration of functions for working with files and folders. + +Copyright (C) 2021 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 +// Standard Libraries +#include // std::basic_string +#include // std::vector + +namespace SHADE +{ + /// + /// Contains useful functions for operating on strings. + /// + class SHStringUtils + { + public: + /*-----------------------------------------------------------------------------*/ + /* Utility Functions */ + /*-----------------------------------------------------------------------------*/ + + /// + /// Splits a string separated by a specified delimiter into a vector of strings. + /// + /// Internal type of each element in the string. + /// Read only reference to the string to split. + /// Read only reference to the delimiter. + /// Vector of strings that have been split. + template + static std::vector> Split(const std::basic_string& str, const T& delim); + /// + /// Splits a string separated by a specified delimiter into a vector of strings. + /// Overload of Split() to allow for string literals to be accepted. + /// + /// Read only reference to the string to split. + /// Read only reference to the delimiter. + /// Vector of strings that have been split. + static std::vector Split(const std::string& str, const char& delim); + /// + /// Splits a string separated by a specified delimiter into a vector of strings. + /// Overload of Split() to allow for wide string literals to be accepted. + /// + /// Read only reference to the string to split. + /// Read only reference to the delimiter. + /// Vector of strings that have been split. + static std::vector Split(const std::wstring& str, const wchar_t& delim); + /// + /// Converts a wstring to a string. + /// + /// wstring to convert. + /// The converted wstring in string form. + static std::string WstrToStr(const std::wstring& wstr); + /// + /// Converts a string to a wstring. + /// + /// string to convert. + /// The converted string in wstring form. + static std::wstring StrToWstr(const std::string& str); + /// + /// Retrieves the error message associated with a Win32 error code. + /// + /// Win32 error code to decode. + /// String that represents the Win32 error. + static std::string GetWin32ErrorMessage(unsigned long errorCode); + + private: + /*-------------------------------------------------------------------------------*/ + /* Constructors/Destructors */ + /*-------------------------------------------------------------------------------*/ + SHStringUtils() = delete; + }; +} + +#include "SHStringUtils.hpp" diff --git a/SHADE_Engine/src/Tools/SHStringUtils.hpp b/SHADE_Engine/src/Tools/SHStringUtils.hpp new file mode 100644 index 00000000..8b83187a --- /dev/null +++ b/SHADE_Engine/src/Tools/SHStringUtils.hpp @@ -0,0 +1,46 @@ +/************************************************************************************//*! +\file StringUtilities.hpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 29, 2021 +\brief Contains the implementation of template functions for working with files + and folders. + +Copyright (C) 2021 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 +// Primary Header +#include "SHStringUtils.h" + +namespace SHADE +{ + /*-------------------------------------------------------------------------------*/ + /* Template Function Definitions */ + /*-------------------------------------------------------------------------------*/ + template + inline std::vector> SHStringUtils::Split(const std::basic_string& str, const T& delim) + { + std::vector> results; + std::basic_string remaining = str; + + // Go through looking for delimiters + while (true) + { + const size_t DELIM_POS = remaining.find_first_of(delim); + results.emplace_back(remaining.substr(0, DELIM_POS)); + + // Check if we hit the end of the string + if (DELIM_POS == remaining.npos) + { + break; + } + + // Otherwise, cut the remainder + remaining = remaining.substr(DELIM_POS + 1); + } + + return results; + } +} \ No newline at end of file diff --git a/SHADE_Managed/premake5.lua b/SHADE_Managed/premake5.lua new file mode 100644 index 00000000..092e92af --- /dev/null +++ b/SHADE_Managed/premake5.lua @@ -0,0 +1,74 @@ +project "SHADE_Managed" + kind "SharedLib" + language "C++" + clr "NetCore" + dotnetframework "net6.0" + cppdialect "C++17" + targetdir (outputdir) + objdir (interdir) + systemversion "latest" + pchheader "SHpch.h" + pchsource "%{prj.location}/src/SHpch.cpp" + staticruntime "off" + + files + { + "%{prj.location}/src/**.hxx", + "%{prj.location}/src/**.h++", + "%{prj.location}/src/**.cxx", + "%{prj.location}/src/**.h", + "%{prj.location}/src/**.hpp", + "%{prj.location}/src/**.c", + "%{prj.location}/src/**.cpp", + } + + includedirs + { + "%{prj.location}/src", + "%{IncludeDir.spdlog}/include", + "%{IncludeDir.imgui}", + "%{IncludeDir.imguizmo}", + "%{IncludeDir.imnodes}", + "%{IncludeDir.yamlcpp}", + "%{IncludeDir.RTTR}/include", + "%{wks.location}/SHADE_Engine/src" + } + + links + { + "yaml-cpp", + "imgui", + "SHADE_Engine" + } + + disablewarnings + { + "4251" + } + + defines + { + "NOMINMAX" + } + + flags + { + "MultiProcessorCompile" + } + + dependson + { + "yaml-cpp", + "imgui", + "SHADE_Engine" + } + + warnings 'Extra' + + filter "configurations:Debug" + symbols "On" + defines {"_DEBUG"} + + filter "configurations:Release" + optimize "On" + defines{"_RELEASE"} diff --git a/SHADE_Managed/src/AssemblyInfo.cxx b/SHADE_Managed/src/AssemblyInfo.cxx new file mode 100644 index 00000000..234bda73 --- /dev/null +++ b/SHADE_Managed/src/AssemblyInfo.cxx @@ -0,0 +1,39 @@ +/************************************************************************************//*! +\file AssemblyInfo.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 24, 2021 +\brief Defines the properties of this managed .NET Assembly. + + Note: This file is written in C++17/CLI. + +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" + +/*-------------------------------------------------------------------------------------*/ +/* Using Declarations */ +/*-------------------------------------------------------------------------------------*/ +using namespace System; +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; +using namespace System::Security::Permissions; + +/*-------------------------------------------------------------------------------------*/ +/* Assembly Properties */ +/*-------------------------------------------------------------------------------------*/ +[assembly:AssemblyTitleAttribute(L"SHADE_Managed")]; +[assembly:AssemblyDescriptionAttribute(L"")]; +[assembly:AssemblyConfigurationAttribute(L"")]; +[assembly:AssemblyCompanyAttribute(L"")]; +[assembly:AssemblyProductAttribute(L"SHADE_Managed")]; +[assembly:AssemblyCopyrightAttribute(L"Copyright (C) 2022 DigiPen Institute of Technology")]; +[assembly:AssemblyTrademarkAttribute(L"")]; +[assembly:AssemblyCultureAttribute(L"")]; + +[assembly:AssemblyVersionAttribute("1.0.*")]; + +[assembly:ComVisible(false)]; diff --git a/SHADE_Managed/src/Components/Component.cxx b/SHADE_Managed/src/Components/Component.cxx new file mode 100644 index 00000000..a6afc5cc --- /dev/null +++ b/SHADE_Managed/src/Components/Component.cxx @@ -0,0 +1,107 @@ +/************************************************************************************//*! +\file Component.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 27, 2021 +\brief Contains the definition of the functions for the Component class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "Components/Component.hxx" +// External Dependencies +#include "Engine/ECS.hxx" +// Project Headers +#include "Scripts/ScriptStore.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Component Access Functions */ + /*---------------------------------------------------------------------------------*/ + generic + T BaseComponent::AddComponent() + { + return ECS::AddComponent(owner.GetEntity()); + } + generic + T BaseComponent::GetComponent() + { + return ECS::GetComponent(owner.GetEntity()); + } + generic + void BaseComponent::RemoveComponent() + { + ECS::RemoveComponent(owner.GetEntity()); + } + + /*---------------------------------------------------------------------------------*/ + /* Script Access Functions */ + /*---------------------------------------------------------------------------------*/ + generic + T BaseComponent::AddScript() + { + return ScriptStore::AddScript(owner.GetEntity()); + } + generic + T BaseComponent::GetScript() + { + return ScriptStore::GetScript(owner.GetEntity()); + } + + generic + System::Collections::Generic::IEnumerable^ BaseComponent::GetScripts() + { + return ScriptStore::GetScripts(owner.GetEntity()); + } + + generic + void BaseComponent::RemoveScript() + { + ScriptStore::RemoveScript(owner.GetEntity()); + } + + /*---------------------------------------------------------------------------------*/ + /* Constructors */ + /*---------------------------------------------------------------------------------*/ + BaseComponent::BaseComponent(Entity entity) + : owner { entity } + {} + + /*---------------------------------------------------------------------------------*/ + /* IEquatable */ + /*---------------------------------------------------------------------------------*/ + bool BaseComponent::Equals(BaseComponent^ other) + { + if (other == nullptr) + return false; + return owner == other->owner; + } + + /*---------------------------------------------------------------------------------*/ + /* Object */ + /*---------------------------------------------------------------------------------*/ + bool BaseComponent::Equals(Object^ o) + { + try + { + BaseComponent^ cmp = safe_cast(o); + return Equals(cmp); + } + catch (System::InvalidCastException^) + { + return false; + } + } + + int BaseComponent::GetHashCode() + { + return owner.GetHashCode(); + } +} diff --git a/SHADE_Managed/src/Components/Component.h++ b/SHADE_Managed/src/Components/Component.h++ new file mode 100644 index 00000000..e2a20998 --- /dev/null +++ b/SHADE_Managed/src/Components/Component.h++ @@ -0,0 +1,41 @@ +/************************************************************************************//*! +\file Component.h++ +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 27, 2021 +\brief Contains the definition of templated functions for the managed Component + classes. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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 + +// Primary Include +#include "Component.hxx" +// Project includes +#include "Utility/Convert.hxx" +#include "Engine/ECS.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Constructors */ + /*---------------------------------------------------------------------------------*/ + template + Component::Component(Entity entity) + : BaseComponent { entity } + {} + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + template + typename Component::NativeComponent* Component::GetNativeComponent() + { + return ECS::GetNativeComponent(owner.GetEntity()); + } +} diff --git a/SHADE_Managed/src/Components/Component.hxx b/SHADE_Managed/src/Components/Component.hxx new file mode 100644 index 00000000..670e4e21 --- /dev/null +++ b/SHADE_Managed/src/Components/Component.hxx @@ -0,0 +1,200 @@ +/************************************************************************************//*! +\file Component.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 27, 2021 +\brief Contains the definition of the managed Component classes with the + declaration of functions for working with it. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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 + +// External Dependencies +#include "ECS_Base/Components/SHComponent.h" +// Project Includes +#include "Engine/Entity.hxx" +#include "Scripts/Script.hxx" + +namespace SHADE +{ + /// + /// Class that serves as the base for a wrapper class to Components in native code. + /// + public ref class BaseComponent : public System::IEquatable + { + public: + /*-----------------------------------------------------------------------------*/ + /* Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// Retrieves the GameObject that this Component belongs to. + /// + property GameObject Owner + { + GameObject get() { return owner; } + } + + /*-----------------------------------------------------------------------------*/ + /* Component Access Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Adds a Component to this GameObject. + /// + /// Type of the Component to add. + /// Reference to the Component that was added. + generic where T : BaseComponent + T AddComponent(); + /// + /// Gets a Component from this GameObject. + /// + /// Type of the Component to get. + /// + /// Reference to the Component or null if this GameObject does not have the + /// specified Component. + /// + generic where T : BaseComponent + T GetComponent(); + /// + /// Removes a Component from this GameObject. If no Component exists to begin + /// with, nothing happens. + /// + /// Type of the Component to get. + generic where T : BaseComponent + void RemoveComponent(); + + /*-----------------------------------------------------------------------------*/ + /* Script Access Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Adds a Script of the specified type to this GameObject. + /// + /// Type of Script to add. + /// Reference to the created Script. + generic where T : ref class, Script + T AddScript(); + /// + /// Retrieves a Script of the specified type from this GameObject. + /// If multiple Scripts of the same specified type are added on the same + /// GameObject, this will retrieve the first one added. + /// + /// Type of Script to add. + /// Reference to the Script to retrieve. + generic where T : ref class, Script + T GetScript(); + /// + /// Retrieves a immutable list of Scripts of the specified type from this + /// GameObject. + /// + /// Type of Scripts to Get. + /// Immutable list of Scripts of the specified type. + generic where T : ref class, Script + System::Collections::Generic::IEnumerable^ GetScripts(); + /// + /// Removes all Scripts of the specified type from this GameObject. + /// + /// Type of PLushieScripts to remove. + generic where T : ref class, Script + void RemoveScript(); + + protected: + /*-----------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------*/ + /// + /// Constructor for BaseComponent to tie it to a specific Entity. + /// Constructors of derived Components should call this Constructor. + /// + /// Entity that this Component will be tied to. + BaseComponent(Entity entity); + + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + /// + /// Entity that this Component belongs to. + /// + GameObject owner; + + public: + /*-----------------------------------------------------------------------------*/ + /* IEquatable */ + /*-----------------------------------------------------------------------------*/ + /// + /// Compares equality with an object of the same type. + /// + /// The object to compare with. + /// True if both objects are the same. + virtual bool Equals(BaseComponent^ other); + + /*-----------------------------------------------------------------------------*/ + /* Object */ + /*-----------------------------------------------------------------------------*/ + /// + /// Compares equality with another unboxed object. + /// + /// The unboxed object to compare with. + /// True if both objects are the same. + bool Equals(Object^ o) override; + /// + /// Gets a unique hash for this object. + /// + /// Unique hash for this object. + int GetHashCode() override; + }; + + /// + /// C++ template for the BaseComponent class used to generate common template-able + /// functions and types. + /// + /// + /// Type of the native component that this Component wraps. + /// + template + public ref class Component : public BaseComponent + { + internal: + /*-----------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Type of the native component that this Component wraps. + /// + using NativeComponent = NativeType; + + /*-----------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Retrieves a pointer to the native unmanaged component that is tied to the + /// Entity described by the owner value. + /// + /// + /// Pointer to the native component. Will be nullptr if it does not exist. + /// + /// + /// Thrown if the internal ID stored by this native component is invalid. + /// + /// + /// Thrown if an attempt to retrieve the native component fails. + /// + NativeComponent* GetNativeComponent(); + + protected: + /*-----------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------*/ + /// + /// Constructor for Component to tie it to a specific Entity. + /// Constructors of derived Components should call this Constructor. + /// + /// Entity that this Component will be tied to. + Component(Entity entity); + }; +} + +#include "Component.h++" diff --git a/SHADE_Managed/src/Engine/ECS.cxx b/SHADE_Managed/src/Engine/ECS.cxx new file mode 100644 index 00000000..5aceceee --- /dev/null +++ b/SHADE_Managed/src/Engine/ECS.cxx @@ -0,0 +1,255 @@ +/************************************************************************************//*! +\file ECS.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the definition of the functions for the ECS managed static + class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "ECS.hxx" +// Standard Library +#include +#include +// External Dependencies +#include "ECS_Base/System/SHEntityManager.h" +// Project Headers +#include "Utility/Convert.hxx" +#include "Utility/Debug.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Component Manipulation Functions */ + /*---------------------------------------------------------------------------------*/ + generic + T ECS::AddComponent(EntityID entity) + { + System::Type^ componentType = T::typeid; + + // Check if entity is correct + if (!SHEntityManager::IsValidEID(entity)) + { + std::ostringstream oss; + oss << "[ECS] Attempted to add Component \"" + << msclr::interop::marshal_as(componentType->Name) + << "\" to invalid Entity."; + Debug::LogError(oss.str()); + return T(); + } + + // Add based on the correct component + for each(ComponentSet^ type in componentMap) + { + if (componentType == type->Type) + { + // Attempt to add + type->AddFunction(entity); + + // Return the managed component + return createManagedComponent(entity); + } + } + + std::ostringstream oss; + oss << "[ECS] Failed to add unsupported Component \"" + << Convert::ToNative(componentType->Name) + << "\" to Entity #" + << entity; + Debug::LogError(oss.str()); + return T(); + } + generic + T ECS::GetComponent(EntityID entity) + { + System::Type^ componentType = T::typeid; + + // Check if entity is correct + if (!SHEntityManager::IsValidEID(entity)) + { + std::ostringstream oss; + oss << "[ECS] Attempted to retrieve Component \"" + << Convert::ToNative(componentType->Name) + << "\" from invalid Entity."; + Debug::LogError(oss.str()); + return T(); + } + + // Get based on the correct component + for each(ComponentSet^ type in componentMap) + { + if (componentType == type->Type) + { + if (type->HasFunction(entity)) + { + return createManagedComponent(entity); + } + else + { + return T(); + } + } + } + + std::ostringstream oss; + oss << "[ECS] Failed to retrieve unsupported Component \"" + << Convert::ToNative(componentType->Name) + << "\" to Entity #" + << entity; + Debug::LogError(oss.str()); + return T(); + } + + generic + T ECS::GetComponentInChildren(EntityID entity) + { + System::Type^ componentType = T::typeid; + + // Check if entity is correct + if (!SHEntityManager::IsValidEID(entity)) + { + std::ostringstream oss; + oss << "[ECS] Attempted to retrieve Component \"" + << Convert::ToNative(componentType->Name) + << "\" from invalid Entity."; + Debug::LogError(oss.str()); + return T(); + } + + // Get Transform component and get the children list + throw gcnew System::NotImplementedException; + //Pls::Transform* tf = Pls::ECS::GetComponent(entity); + //if (tf == nullptr) + // return T(); + + //// Search direct children first + //for (const auto& child : tf->GetChildren()) + //{ + // T component = GetComponent(child); + // if (component != nullptr) + // return component; + //} + + //// Search their children + //for (const auto& child : tf->GetChildren()) + //{ + // T script = GetComponentInChildren(child); + // if (script != nullptr) + // return script; + //} + + // None here + return T(); + } + + generic + T ECS::EnsureComponent(EntityID entity) + { + if (HasComponent(entity)) + { + AddComponent(entity); + } + + return GetComponent(entity); + } + generic + bool ECS::HasComponent(EntityID entity) + { + System::Type^ componentType = T::typeid; + + // Check if entity is correct + if (!SHEntityManager::IsValidEID(entity)) + { + std::ostringstream oss; + oss << "[ECS] Attempted to check existence of Component \"" + << Convert::ToNative(componentType->Name) + << "\" from invalid Entity."; + Debug::LogError(oss.str()); + return false; + } + + // Add based on the correct component + for each(ComponentSet^ type in componentMap) + { + if (componentType == type->Type) + { + return type->HasFunction(entity); + } + } + + std::ostringstream oss; + oss << "[ECS] Attempted to check existence of unsupported Component \"" + << msclr::interop::marshal_as(componentType->Name) + << "\" from Entity #" + << entity; + Debug::LogError(oss.str()); + + return false; + } + generic + void ECS::RemoveComponent(EntityID entity) + { + System::Type^ componentType = T::typeid; + + // Check if entity is correct + if (!SHEntityManager::IsValidEID(entity)) + { + std::ostringstream oss; + oss << "[ECS] Attempted to remove Component \"" + << Convert::ToNative(componentType->Name) + << "\" from invalid Entity."; + Debug::LogError(oss.str()); + } + + // Add based on the correct component + for each(ComponentSet^ type in componentMap) + { + if (componentType == type->Type) + { + type->RemoveFunction(entity); + return; + } + } + + std::ostringstream oss; + oss << "[ECS] Attempted to remove unsupported Component \"" + << msclr::interop::marshal_as(componentType->Name) + << "\" from Entity #" + << entity; + Debug::LogError(oss.str()); + } + + /*---------------------------------------------------------------------------------*/ + /* Constructors */ + /*---------------------------------------------------------------------------------*/ + static ECS::ECS() + { + // TODO + // componentMap.Add(createComponentSet()); + } + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + generic + T ECS::createManagedComponent(EntityID entity) + { + using namespace System::Reflection; + + array^ params = gcnew array{ static_cast(entity) }; + return safe_cast(System::Activator::CreateInstance + ( + T::typeid, + BindingFlags::Instance | BindingFlags::NonPublic | BindingFlags::CreateInstance, + nullptr, params, nullptr) + ); + } +} diff --git a/SHADE_Managed/src/Engine/ECS.h++ b/SHADE_Managed/src/Engine/ECS.h++ new file mode 100644 index 00000000..e5ede5f2 --- /dev/null +++ b/SHADE_Managed/src/Engine/ECS.h++ @@ -0,0 +1,60 @@ +/************************************************************************************//*! +\file ECS.h++ +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 27, 2021 +\brief Contains the definition of templated functions for the managed Component + classes. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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 + +// Primary Include +#include "ECS.hxx" +// External Dependencies +#include "ECS_Base/System/SHComponentManager.h" +#include "ECS_Base/System/SHEntityManager.h" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Static Functions */ + /*---------------------------------------------------------------------------------*/ + template + NativeComponent* ECS::GetNativeComponent(Entity entity) + { + // Get native Entity + SHEntity* nativeEntity = SHEntityManager::GetEntityByID(entity); + + // Entity Validity Check + if (nativeEntity == nullptr) + throw gcnew System::InvalidOperationException("Attempted to get native Component to an invalid Entity."); + + // Null Check + NativeComponent* component = SHComponentManager::GetComponent_s(nativeEntity); + if (component == nullptr) + throw gcnew System::NullReferenceException("Attempted to get a native Component that does not exist."); + + return component; + } + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + template + ECS::ComponentSet ECS::createComponentSet() + { + return ComponentSet + { + ManagedType::typeid, + SHComponentManager::AddComponent, + SHComponentManager::EnsureComponent, + SHComponentManager::HasComponent, + SHComponentManager::RemoveComponent + }; + } +} diff --git a/SHADE_Managed/src/Engine/ECS.hxx b/SHADE_Managed/src/Engine/ECS.hxx new file mode 100644 index 00000000..72c88e11 --- /dev/null +++ b/SHADE_Managed/src/Engine/ECS.hxx @@ -0,0 +1,174 @@ +/************************************************************************************//*! +\file ECS.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the definitions of the GameObject managed class which define an + abstraction for working with Entities in managed code. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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 + +// External Dependencies +#include "ECS_Base/System/SHComponentManager.h" +// Project Includes +#include "Components/Component.hxx" + +namespace SHADE +{ + /// + /// Static class which contains functions that map Pls::ECS's Component manipulation + /// functions to managed generic functions. + /// + private ref class ECS abstract sealed + { + public: + /*-----------------------------------------------------------------------------*/ + /* Component Manipulation Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Adds a Component to the specified Entity. + /// + /// Type of the Component to add. + /// + /// Entity object that should have the specified Component added to. + /// + /// Reference to the Component that was added. + generic where T : BaseComponent + static T AddComponent(EntityID entity); + /// + /// Gets a Component from the specified Entity. + /// + /// Type of the Component to get. + /// Entity object to get the Component from. + /// + /// Reference to the Component or null if the Entity does not have the + /// specified Component. + /// + generic where T : BaseComponent + static T GetComponent(EntityID entity); + /// + /// Retrieves the first Component from the specified GameObjectt's children that + /// matches the specified type. + /// + /// Type of the Component to get. + /// Entity object to get the Component from. + /// + /// Reference to the Component or null if the Entity does not have the + /// specified Component. + /// + generic where T : BaseComponent + static T GetComponentInChildren(EntityID entity); + /// + /// Ensures a Component on the specified Entity. + /// + /// Type of the Component to ensure. + /// Entity object to ensure the Component on. + /// Reference to the Component. + generic where T : BaseComponent + static T EnsureComponent(EntityID entity); + /// + /// Checks if the specified Entity has the specified Component. + /// + /// Type of the Component to check for. + /// Entity object to check for the Component. + /// + /// True if the specified Entity has the specified Component. False otherwise. + /// + generic where T : BaseComponent + static bool HasComponent(EntityID entity); + /// + /// Removes a Component from the specified Entity. + /// + /// Type of the Component to remove. + /// + /// Entity object that should have the specified Component removed from/ + /// + generic where T : BaseComponent + static void RemoveComponent(EntityID entity); + + internal: + /*-----------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Pointer to a function for Component manipulation operations. + /// + using ComponentFunc = void(*)(const EntityID&); + using ComponentHasFunc = bool(*)(const EntityID&); + /// + /// Contains a set of Component related data used for resolving operations for + /// each Component. + /// + value struct ComponentSet + { + public: + System::Type^ Type; + ComponentFunc AddFunction; + ComponentHasFunc HasFunction; + ComponentFunc RemoveFunction; + + }; + + /*-----------------------------------------------------------------------------*/ + /* Static Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Retrieves a pointer to the native unmanaged component of the specified + /// Entity. + /// + /// + /// Pointer to the native component. Will be nullptr if it does not exist. + /// + /// + /// Thrown if the Entity specified is invalid. + /// + /// + /// Thrown if an attempt to retrieve the native component fails. + /// + template + static NativeComponent* GetNativeComponent(Entity entity); + + private: + /*-----------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------*/ + /// + /// Static constructor to initialize static data + /// + static ECS(); + + /*-----------------------------------------------------------------------------*/ + /* Static Data Members */ + /*-----------------------------------------------------------------------------*/ + static System::Collections::Generic::List componentMap; + + /*-----------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Creates a ComponentSet for a pair of Native and Managed Components. + /// + /// Type of the Native Component. + /// Type of the Managed Component. + /// ComponentSet for the parameters specified. + template + static ComponentSet createComponentSet(); + /// + /// Creates an instance of the Managed representation of a Component with a + /// native Entity. + /// + /// Type of Component to create. + /// Native Entity that this Component is tied to. + /// The created Managed representation of the Component. + generic where T : BaseComponent + static T createManagedComponent(EntityID entity); + }; +} + +#include "ECS.h++" diff --git a/SHADE_Managed/src/Engine/EngineInterface.cxx b/SHADE_Managed/src/Engine/EngineInterface.cxx new file mode 100644 index 00000000..2009b2e5 --- /dev/null +++ b/SHADE_Managed/src/Engine/EngineInterface.cxx @@ -0,0 +1,138 @@ +/************************************************************************************//*! +\file EngineInterface.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the implementation of the managed EngineInterface static class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "EngineInterface.hxx" +// Standard Libraries +#include +// Project Headers +#include "Utility/Convert.hxx" +#include "Utility/Debug.hxx" +#include "Scripts/ScriptStore.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Interop Static Functions */ + /*---------------------------------------------------------------------------------*/ + void EngineInterface::Init() + { + SAFE_NATIVE_CALL_BEGIN + // Set up exception handler + System::AppDomain::CurrentDomain->UnhandledException += exceptionHandler; + LoadScriptAssembly(); + Debug::Log("[EngineInterface] Successfully initialized managed runtime."); + SAFE_NATIVE_CALL_END_N("SHADE_Managed.EngineInterface") + } + void EngineInterface::UnloadScriptAssembly() + { + SAFE_NATIVE_CALL_BEGIN + std::ostringstream oss; + oss << "[EngineInterface] Unloading " << Convert::ToNative(ManagedLibraryName) << ".dll"; + ScriptStore::Exit(); + + // Unload the script + scriptContext->Unload(); + scriptContext = nullptr; + System::GC::Collect(); + System::GC::WaitForPendingFinalizers(); + + // Unload the assembly File + if (managedLibFile != nullptr) + { + managedLibFile->Close(); + managedLibFile = nullptr; + } + + oss.str(""); + oss << "[EngineInterface] Successfully unloaded " << Convert::ToNative(ManagedLibraryName) << ".dll"; + Debug::Log(oss.str()); + SAFE_NATIVE_CALL_END_N("SHADE_Managed.EngineInterface") + } + void EngineInterface::LoadScriptAssembly() + { + SAFE_NATIVE_CALL_BEGIN + scriptContext = gcnew DisposableAssemblyLoadContext(); + loadManagedLibrary(); + ScriptStore::Init(); + SAFE_NATIVE_CALL_END_N("SHADE_Managed.EngineInterface") + } + void EngineInterface::ReloadScriptAssembly() + { + SAFE_NATIVE_CALL_BEGIN + // Stop scripts + UnloadScriptAssembly(); + // Reload assembly and restart scripts runtime + LoadScriptAssembly(); + SAFE_NATIVE_CALL_END_N("SHADE_Managed.EngineInterface") + } + void EngineInterface::Exit() + { + SAFE_NATIVE_CALL_BEGIN + // Clean up ScriptStore + ScriptStore::Exit(); + scriptContext->Unload(); + + // Release exception handler + System::AppDomain::CurrentDomain->UnhandledException -= exceptionHandler; + SAFE_NATIVE_CALL_END_N("SHADE_Managed.EngineInterface") + } + + /*---------------------------------------------------------------------------------*/ + /* Constructor */ + /*---------------------------------------------------------------------------------*/ + static EngineInterface::EngineInterface() + { + exceptionHandler = gcnew System::UnhandledExceptionEventHandler(unhandledExceptionHandler); + managedLibPath = System::Reflection::Assembly::GetExecutingAssembly()->Location->Replace("SHADE_Managed.dll", ManagedLibraryName + ".dll"); + } + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + void EngineInterface::loadManagedLibrary() + { + using namespace System::IO; + + std::ostringstream oss; + try + { + oss << "[EngineInterface] Loading " << Convert::ToNative(ManagedLibraryName) << ".dll"; + managedLibFile = File::Open(managedLibPath, FileMode::Open, FileAccess::Read); + scriptContext->LoadFromStream(managedLibFile); + oss.str(""); + oss << "[EngineInterface] Successfully loaded " << Convert::ToNative(ManagedLibraryName) << ".dll"; + Debug::Log(oss.str()); + } + catch (System::Exception^ e) + { + oss << "[EngineInterface] Unable to load " << Convert::ToNative(ManagedLibraryName) << ".dll!" + << "(" << Convert::ToNative(e->ToString()) << ")"; + Debug::LogError(oss.str()); + } + } + + /*---------------------------------------------------------------------------------*/ + /* Exception Handler Functions */ + /*---------------------------------------------------------------------------------*/ + void EngineInterface::unhandledExceptionHandler(System::Object^, System::UnhandledExceptionEventArgs^ e) + { + std::ostringstream oss; + oss << "[EngineInterface] Unhandled managed exception: " + << Convert::ToNative(e->ExceptionObject->GetType()->ToString()) << ": " + << Convert::ToNative(e->ExceptionObject->ToString()); + Debug::LogError(oss.str()); + } +} diff --git a/SHADE_Managed/src/Engine/EngineInterface.hxx b/SHADE_Managed/src/Engine/EngineInterface.hxx new file mode 100644 index 00000000..4fd8f7b3 --- /dev/null +++ b/SHADE_Managed/src/Engine/EngineInterface.hxx @@ -0,0 +1,90 @@ +/************************************************************************************//*! +\file EngineInterface.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the definitions of the managed EngineInterface static class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +// Project Includes +#include "Utility/DisposableAssemblyLoadContext.hxx" + +namespace SHADE +{ + /// + /// Static class that contains the functions for interfacing with the core + /// PlushieEngine written in C++ for managing the lifecycle of managed code. + /// + private ref class EngineInterface abstract sealed + { + public: + /*-----------------------------------------------------------------------------*/ + /* Constants */ + /*-----------------------------------------------------------------------------*/ + /// + /// Name of the Managed Library that contains the C# scripts written externally. + /// + literal System::String^ ManagedLibraryName = "SHADE_Scripting"; + + /*-----------------------------------------------------------------------------*/ + /* Interop Static Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Initialises all required components for managed code. + /// + static void Init(); + /// + /// Unloads the managed script assembly. + /// Take note that this will clear all existing scripts, ensure that the scene + /// is saved before doing so. + /// + static void UnloadScriptAssembly(); + /// + /// Loads the managed script assembly. Ensure this is only called after + /// UnloadScriptAssembly() has been called. + /// + static void LoadScriptAssembly(); + /// + /// Reloads the managed script assembly. + /// Take note that this will clear all existing scripts, ensure that the scene + /// is saved before doing so. + /// Equivalent to calling UnloadScriptAssembly() and then LoadScriptAssembly(). + /// + static void ReloadScriptAssembly(); + /// + /// Cleans up all required components for managed code. + /// + static void Exit(); + + private: + /*-----------------------------------------------------------------------------*/ + /* Constructor */ + /*-----------------------------------------------------------------------------*/ + static EngineInterface(); + + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + static DisposableAssemblyLoadContext^ scriptContext; + static System::UnhandledExceptionEventHandler^ exceptionHandler; + static System::String^ managedLibPath; + static System::IO::FileStream^ managedLibFile; + + /*-----------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------*/ + static void loadManagedLibrary(); + + /*-----------------------------------------------------------------------------*/ + /* Exception Handler Functions */ + /*-----------------------------------------------------------------------------*/ + static void unhandledExceptionHandler(System::Object^ sender, System::UnhandledExceptionEventArgs^ e); + }; +} \ No newline at end of file diff --git a/SHADE_Managed/src/Engine/Entity.cxx b/SHADE_Managed/src/Engine/Entity.cxx new file mode 100644 index 00000000..ba1a31c6 --- /dev/null +++ b/SHADE_Managed/src/Engine/Entity.cxx @@ -0,0 +1,28 @@ +/************************************************************************************//*! +\file Entity.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the definition of the functions for the EntityUtils managed + static class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "Entity.hxx" +// External Dependencies +#include "ECS_Base/System/SHEntityManager.h" + +namespace SHADE +{ + bool EntityUtils::IsValid(Entity^ entity) + { + return SHEntityManager::IsValidEID(static_cast(entity)); + } +} diff --git a/SHADE_Managed/src/Engine/Entity.hxx b/SHADE_Managed/src/Engine/Entity.hxx new file mode 100644 index 00000000..7be9340b --- /dev/null +++ b/SHADE_Managed/src/Engine/Entity.hxx @@ -0,0 +1,41 @@ +/************************************************************************************//*! +\file Entity.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the definitions of a managed Entity identifier and declarations + of useful utility functions for working with Entity identifiers. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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 + +// External Dependencies +#include "ECS_Base/Entity/SHEntity.h" + +namespace SHADE +{ + /// + /// Managed representation of a native ECS Entity. + /// + using Entity = System::UInt32; + + /// + /// Static class that contains useful utility functions for working with Entity. + /// + private ref class EntityUtils abstract sealed + { + public: + /// + /// Checks if the specified entity is valid. This is done by checking if it + /// matches Pls::Entity::INVALID. + /// + /// The Entity to check. + /// True if the specified Entity is valid. + static bool IsValid(Entity^ entity); + }; +} diff --git a/SHADE_Managed/src/Engine/GameObject.cxx b/SHADE_Managed/src/Engine/GameObject.cxx new file mode 100644 index 00000000..3896fac5 --- /dev/null +++ b/SHADE_Managed/src/Engine/GameObject.cxx @@ -0,0 +1,201 @@ +/************************************************************************************//*! +\file GameObject.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the definition of the functions for the GameObject managed class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "GameObject.hxx" +// External Dependencies +#include "ECS_Base/System/SHEntityManager.h" +// Project Headers +#include "ECS.hxx" +#include "Scripts/ScriptStore.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Static Functions */ + /*---------------------------------------------------------------------------------*/ + GameObject GameObject::Create() + { + return GameObject(SHEntityManager::CreateEntity()); + } + + void GameObject::Destroy(GameObject obj) + { + SHEntityManager::DestroyEntity(static_cast(obj.GetEntity())); + } + + System::Nullable GameObject::Find(System::String ^ name) + { + // Search the GameObjectLibrary for an Entity with the specified name + throw gcnew System::NotImplementedException(); + } + + /*---------------------------------------------------------------------------------*/ + /* Properties */ + /*---------------------------------------------------------------------------------*/ + System::String^ GameObject::Name::get() + { + return Convert::ToCLI(GetNativeEntity().name); + + } + bool GameObject::IsActiveSelf::get() + { + return GetNativeEntity().isActive; + } + bool GameObject::IsActiveInHierarchy::get() + { + throw gcnew System::NotImplementedException(); + } + + /*---------------------------------------------------------------------------------*/ + /* GameObject Property Functions */ + /*---------------------------------------------------------------------------------*/ + void GameObject::SetName(System::String^ name) + { + GetNativeEntity().name = Convert::ToNative(name); + } + void GameObject::SetActive(bool active) + { + GetNativeEntity().isActive = active; + } + + /*---------------------------------------------------------------------------------*/ + /* Component Functions */ + /*---------------------------------------------------------------------------------*/ + generic + T GameObject::AddComponent() + { + return ECS::AddComponent(entity); + } + + generic + T GameObject::GetComponent() + { + return ECS::GetComponent(entity); + } + + generic + T GameObject::GetComponentInChildren() + { + return ECS::GetComponentInChildren(entity); + } + + generic + T GameObject::EnsureComponent() + { + return ECS::EnsureComponent(entity); + } + + generic + void GameObject::RemoveComponent() + { + ECS::RemoveComponent(entity); + } + + /*---------------------------------------------------------------------------------*/ + /* Script Access Functions */ + /*---------------------------------------------------------------------------------*/ + generic + T GameObject::AddScript() + { + return ScriptStore::AddScript(entity); + } + + generic + T GameObject::GetScript() + { + return ScriptStore::GetScript(entity); + } + + generic + T GameObject::GetScriptInChildren() + { + return ScriptStore::GetScriptInChildren(entity); + } + + generic + System::Collections::Generic::IEnumerable^ GameObject::GetScripts() + { + return ScriptStore::GetScripts(entity); + } + + generic + void GameObject::RemoveScript() + { + ScriptStore::RemoveScript(entity); + } + + /*---------------------------------------------------------------------------------*/ + /* Constructors */ + /*---------------------------------------------------------------------------------*/ + GameObject::GameObject(const SHEntity& entity) + : entity { entity.GetEID() } + {} + + GameObject::GameObject(Entity entity) + : entity { entity } + {} + + /*---------------------------------------------------------------------------------*/ + /* Getters */ + /*---------------------------------------------------------------------------------*/ + SHEntity& GameObject::GetNativeEntity() + { + SHEntity* nativeEntity = SHEntityManager::GetEntityByID(entity); + if (nativeEntity == nullptr) + throw gcnew System::InvalidOperationException("[GameObject] Unable to obtain native Entity for GameObject."); + + return *nativeEntity; + } + + /*---------------------------------------------------------------------------------*/ + /* IEquatable */ + /*---------------------------------------------------------------------------------*/ + bool GameObject::Equals(GameObject other) + { + return entity == other.entity; + } + + /*---------------------------------------------------------------------------------*/ + /* Object */ + /*---------------------------------------------------------------------------------*/ + bool GameObject::Equals(Object^ o) + { + try + { + GameObject^ cmp = safe_cast(o); + return Equals(cmp); + } + catch (System::InvalidCastException^) + { + return false; + } + } + + int GameObject::GetHashCode() + { + return entity.GetHashCode(); + } + + bool GameObject::operator==(GameObject lhs, GameObject rhs) + { + return lhs.Equals(rhs); + } + + bool GameObject::operator!=(GameObject lhs, GameObject rhs) + { + return !(lhs == rhs); + } +} diff --git a/SHADE_Managed/src/Engine/GameObject.hxx b/SHADE_Managed/src/Engine/GameObject.hxx new file mode 100644 index 00000000..723d9cec --- /dev/null +++ b/SHADE_Managed/src/Engine/GameObject.hxx @@ -0,0 +1,282 @@ +/************************************************************************************//*! +\file GameObject.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the definitions of the GameObject managed class which define an + abstraction for working with Entities in managed code. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +// Project Includes +#include "Entity.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*---------------------------------------------------------------------------------*/ + ref class Script; + ref class BaseComponent; + + /*---------------------------------------------------------------------------------*/ + /* Class Definitions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Lightweight object for an PlushieEngine Entity that allows for easy access + /// to Component and Script operations. + /// + public value class GameObject : public System::IEquatable + { + public: + /*-----------------------------------------------------------------------------*/ + /* Static Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Creates a new GameObject in the current Scene. If multiple Scenes are loaded, + /// and you would like to create an object in a specific Scene, call the Scene's + /// CreateGameObject(). + /// + /// GameObject that represents the newly created GameObject. + static GameObject Create(); + /// + /// Destroys the specified GameObject. Note that the specified GameObject will no + /// longer be a valid GameObject after this function is called. + /// + /// The GameObject to be destroyed. + static void Destroy(GameObject obj); + /// + /// Retrieves a GameObject with the specified name. If there are multiple + /// GameObjects with the same name, the first found GameObject will be retrieved. + /// There is no guaranteed order of which GameObject is considered "first". + /// + /// Name of the GameObject to find. + /// GameObject that has the specified name. Null if not found. + static System::Nullable Find(System::String^ name); + + /*-----------------------------------------------------------------------------*/ + /* Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// Name of the object that this Entity represents. + /// + property System::String^ Name + { + System::String^ get(); + } + /// + /// Whether or not this Entity alone, is active. This does not mean that this + /// object is active in the scene. For example, if this Entity's parent is not + /// active, then this Entity would also be not active. + /// + property bool IsActiveSelf + { + bool get(); + } + /// + /// Whether or not this Entity is active in the Scene hierarchy. + /// + property bool IsActiveInHierarchy + { + bool get(); + } + + /*-----------------------------------------------------------------------------*/ + /* GameObject Property Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Sets the name of this GameObject. + /// + /// The name to set. + void SetName(System::String^ name); + /// + /// Sets the active state of this GameObject. + ///
+ /// The actual "activeness" of this GameObject is still dependent on the parents' + /// active states. + ///
+ /// + /// Whether to activate or deactivate this GameObject. + /// + void SetActive(bool active); + + /*-----------------------------------------------------------------------------*/ + /* Component Access Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Adds a Component to this GameObject. + /// + /// Type of the Component to add. + /// Reference to the Component that was added. + generic where T : BaseComponent + T AddComponent(); + /// + /// Gets a Component from this GameObject. + /// + /// Type of the Component to get. + /// + /// Reference to the Component or null if this GameObject does not have the + /// specified Component. + /// + generic where T : BaseComponent + T GetComponent(); + /// + /// Retrieves the first Component from this GameObject's children that matches + /// the specified type. + /// + /// Type of the Component to get. + /// + /// Reference to the Component or null if neither of this GameObject's children + /// does not have the specified Component. + /// + generic where T : BaseComponent + T GetComponentInChildren(); + /// + /// Ensures a Component on this GameObject. + /// + /// Type of the Component to ensure. + /// + /// Reference to the Component. + /// + generic where T : BaseComponent + T EnsureComponent(); + /// + /// Removes a Component from this GameObject. If no Component exists to begin + /// with, nothing happens. + /// + /// Type of the Component to get. + generic where T : BaseComponent + void RemoveComponent(); + + /*-----------------------------------------------------------------------------*/ + /* Script Access Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Adds a Script of the specified type to this GameObject. + /// + /// Type of Script to add. + /// Reference to the created Script. + generic where T : ref class, Script + T AddScript(); + /// + /// Retrieves a Script of the specified type from this GameObject. + /// If multiple Scripts of the same specified type are added on the same + /// GameObject, this will retrieve the first one added. + /// + /// Type of Script to retrieve. + /// Reference to the Script to retrieve. + generic where T : ref class, Script + T GetScript(); + /// + /// Retrieves a Script of the specified type from child GameObjects. + /// If multiple Scripts of the same specified type are added on the same + /// child GameObject, this will retrieve the first one added. + /// + /// Type of Script to retrieve. + /// Reference to the Script to retrieve. + generic where T : ref class, Script + T GetScriptInChildren(); + /// + /// Retrieves a immutable list of Scripts of the specified type from this + /// GameObject. + /// + /// Type of Scripts to retrieve. + /// Immutable list of Scripts of the specified type. + generic where T : ref class, Script + System::Collections::Generic::IEnumerable^ GetScripts(); + /// + /// Removes all Scripts of the specified type from this GameObject. + /// + /// Type of PLushieScripts to remove. + generic where T : ref class, Script + void RemoveScript(); + + internal: + /*-----------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------*/ + /// + /// Constructor for the GameObject. + /// + /// + /// The ECS Entity that this GameObject should represent. + /// + GameObject(const SHEntity& entity); + /// + /// Constructor for the GameObject. + /// + /// + /// Managed numerical representation of the ECS Entity that this GameObject + /// should represent. + /// + GameObject(Entity entity); + + /*-----------------------------------------------------------------------------*/ + /* Getters */ + /*-----------------------------------------------------------------------------*/ + /// + /// Retrieves the CLR Entity object that this GameObject represents. + /// + /// Entity object that this GameObject represents. + inline Entity GetEntity() { return entity; } + /// + /// Retrieves the native Entity object that this GameObject represents. + /// + /// Native Entity object that this GameObject represents. + SHEntity& GetNativeEntity(); + + private: + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + Entity entity; + + public: + /*-----------------------------------------------------------------------------*/ + /* IEquatable */ + /*-----------------------------------------------------------------------------*/ + /// + /// Compares equality with an object of the same type. + /// + /// The object to compare with. + /// True if both objects are the same. + virtual bool Equals(GameObject other); + + /*-----------------------------------------------------------------------------*/ + /* Object */ + /*-----------------------------------------------------------------------------*/ + /// + /// Compares equality with another unboxed object. + /// + /// The unboxed object to compare with. + /// True if both objects are the same. + bool Equals(Object^ o) override; + /// + /// Gets a unique hash for this object. + /// + /// Unique hash for this object. + int GetHashCode() override; + /// + /// Checks if two GameObject references are the same. + /// + /// GameObject to check. + /// Another GameObject to check with. + /// True if both Components are the same. + static bool operator==(GameObject lhs, GameObject rhs); + /// + /// Checks if two GameObject references are different. + /// + /// GameObject to check. + /// Another GameObject to check with. + /// True if both Components are different. + static bool operator!=(GameObject lhs, GameObject rhs); + }; + +} + diff --git a/SHADE_Managed/src/Math/Math.cxx b/SHADE_Managed/src/Math/Math.cxx new file mode 100644 index 00000000..5ec850a1 --- /dev/null +++ b/SHADE_Managed/src/Math/Math.cxx @@ -0,0 +1,57 @@ +/************************************************************************************//*! +\file Math.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 11, 2021 +\brief Contains the implementation of the functions of the managed Math struct. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "Math/Math.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Utility Functions */ + /*---------------------------------------------------------------------------------*/ + double Math::Wrap(double value, double min, double max) + { + while (value < min) + { + value = max - (min - value); + } + while (value > max) + { + value = min + (value - max); + } + return value; + } + double Math::DegreesToRadians(double degrees) + { + return degrees * Deg2Rad; + } + double Math::RadiansToDegrees(double radians) + { + return radians * Rad2Deg; + } + double Math::Lerp(double a, double b, double t) + { + return LerpUnclamped(a, b, System::Math::Clamp(t, 0.0, 1.0)); + } + double Math::LerpUnclamped(double a, double b, double t) + { + return a + t * (b - a); + } + + double Math::InverseLerp(double a, double b, double value) + { + return (value - a) / (b - a); + } +} diff --git a/SHADE_Managed/src/Math/Math.hxx b/SHADE_Managed/src/Math/Math.hxx new file mode 100644 index 00000000..3ddc5149 --- /dev/null +++ b/SHADE_Managed/src/Math/Math.hxx @@ -0,0 +1,92 @@ +/************************************************************************************//*! +\file Math.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 11, 2021 +\brief Contains the definition of the managed Math static class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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 + +namespace SHADE +{ + /// + /// Contains utility Math functions. + /// + public ref class Math abstract sealed + { + public: + /*-----------------------------------------------------------------------------*/ + /* Static Constants */ + /*-----------------------------------------------------------------------------*/ + /// + /// Degrees-to-radians conversion constant + /// + static constexpr double Deg2Rad = System::Math::PI / 180.0; + /// + /// Radians-to-degrees conversion constant + /// + static constexpr double Rad2Deg = 180.0 / System::Math::PI; + /// + /// Small value used for single precision floating point comparisons. + /// + static constexpr float Epsilon = 0.001f; + + /*-----------------------------------------------------------------------------*/ + /* Utility Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Wraps a value if they get to low or too high. + /// + /// Value to wrap. + /// Minimum value to wrap at. + /// Maximum value to wrap at. + /// Wrapped value. + static double Wrap(double value, double min, double max); + /// + /// Converts an angle from degree representation to radian representation. + /// + /// Degree-based angle to convert. + /// The specified angle in radians. + static double DegreesToRadians(double degrees); + /// + /// Converts an angle from radian representation to degree representation. + /// + /// Radian-based angle to convert. + /// The specified angle in degrees. + static double RadiansToDegrees(double radians); + /// + /// Linearly interpolates between a and b by t. + /// The parameter t is clamped to the range [0, 1]. + /// + /// The start value. + /// The end value. + /// The interpolation value between the two double. + /// The interpolated double result between the two double values. + static double Lerp(double a, double b, double t); + /// + /// Linearly interpolates between a and b by t. + /// The parameter t is not clamped and a value based on a and b is supported. + /// If t is less than zero, or greater than one, then LerpUnclamped will result + /// in a return value outside the range a to b. + /// + /// The start value. + /// The end value. + /// The interpolation value between the two double. + /// The interpolated double result between the two double values. + static double LerpUnclamped(double a, double b, double t); + /// + /// Calculates the linear parameter t that produces the interpolant value within the range [a, b]. + /// + /// Start value. + /// End value. + /// Value between start and end. + /// Percentage of value between start and end. + static double InverseLerp(double a, double b, double value); + }; +} diff --git a/SHADE_Managed/src/Math/Vector2.cxx b/SHADE_Managed/src/Math/Vector2.cxx new file mode 100644 index 00000000..d40e2323 --- /dev/null +++ b/SHADE_Managed/src/Math/Vector2.cxx @@ -0,0 +1,263 @@ +/************************************************************************************//*! +\file Vector2.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 2, 2021 +\brief Contains the definitions of functions of the Vector2 struct. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "Math/Vector2.hxx" +// Standard Libraries +#include +#include +// Project Headers +#include "Math.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Constructors */ + /*---------------------------------------------------------------------------------*/ + Vector2::Vector2(double _x) + : Vector2 { _x, 0.0 } + {} + Vector2::Vector2(double _x, double _y) + : x { _x } + , y { _y } + {} + + /*---------------------------------------------------------------------------------*/ + /* Usage Functions */ + /*---------------------------------------------------------------------------------*/ + void Vector2::Normalise() + { + *this = GetNormalised(); + } + + Vector2 Vector2::GetNormalised() + { + return *this / GetMagnitude(); + } + + double Vector2::GetMagnitude() + { + return sqrt(x * x + y * y); + } + + double Vector2::GetSqrMagnitude() + { + return x * x + y * y; + } + + double Vector2::AngleFromRightRadians() + { + return atan2(y, x); + } + + double Vector2::AngleFromRightDegrees() + { + return Math::RadiansToDegrees(AngleFromRightRadians()); + } + + bool Vector2::IsNearPoint(Vector2 point) + { + return IsNearPoint(point, Math::Epsilon); + } + + bool Vector2::IsNearPoint(Vector2 point, double tolerance) + { + return (*this - point).GetSqrMagnitude() < (tolerance * tolerance); + } + + /*---------------------------------------------------------------------------------*/ + /* IEquatable */ + /*---------------------------------------------------------------------------------*/ + bool Vector2::Equals(Object^ o) + { + try + { + Vector2 vec = safe_cast(o); + return Equals(vec); + } + catch (System::InvalidCastException^) + { + return false; + } + } + + /*---------------------------------------------------------------------------------*/ + /* Object Overrides */ + /*---------------------------------------------------------------------------------*/ + bool Vector2::Equals(Vector2 other) + { + return IsNear(*this, other); + } + int Vector2::GetHashCode() + { + const int HASH = 19; + return x.GetHashCode() * HASH + y.GetHashCode(); + } + + /*---------------------------------------------------------------------------------*/ + /* Static Functions */ + /*---------------------------------------------------------------------------------*/ + bool Vector2::IsNear(Vector2 lhs, Vector2 rhs) + { + return IsNear(lhs, rhs, Math::Epsilon); + } + bool Vector2::IsNear(Vector2 lhs, Vector2 rhs, double tolerance) + { + return (std::abs(lhs.x) - std::abs(rhs.x)) < tolerance + && + (std::abs(lhs.y) - std::abs(rhs.y)) < tolerance; + } + double Vector2::Dot(Vector2 lhs, Vector2 rhs) + { + return lhs.x * rhs.x + lhs.y * rhs.y; + } + + Vector2 Vector2::Perpendicular(Vector2 lhs) + { + return Perpendicular(lhs, true); + } + + Vector2 Vector2::Perpendicular(Vector2 lhs, bool inward) + { + if (inward) + { + return Vector2 + ( + -lhs.y, lhs.x + ); + } + else + { + return Vector2 + ( + lhs.y, -lhs.x + ); + } + } + + Vector2 Vector2::Project(Vector2 vec, Vector2 direction) + { + return direction.GetNormalised() * vec.GetMagnitude(); + } + Vector2 Vector2::Reflect(Vector2 vec, Vector2 normal) + { + return vec - (Project(vec, normal.GetNormalised()) * 2.0); + } + Vector2 Vector2::RotateRadians(Vector2 vec, double radians) + { + const double SINE = sin(radians); + const double COSINE = cos(radians); + + return Vector2 + ( + vec.x * COSINE - vec.y * SINE, + vec.x * SINE + vec.y * COSINE + ); + } + Vector2 Vector2::RotateDegrees(Vector2 vec, double degrees) + { + return RotateRadians(vec, Math::DegreesToRadians(degrees)); + } + Vector2 Vector2::Min(Vector2 lhs, Vector2 rhs) + { + double lx = lhs.x, rx = rhs.x; + double ly = lhs.y, ry = rhs.y; + + return Vector2(std::min(lx, rx), + std::min(ly, ry)); + } + Vector2 Vector2::Max(Vector2 lhs, Vector2 rhs) + { + double lx = lhs.x, rx = rhs.x; + double ly = lhs.y, ry = rhs.y; + + return Vector2(std::max(lx, rx), + std::max(ly, ry)); + } + Vector2 Vector2::Lerp(Vector2 a, Vector2 b, double t) + { + return LerpUnclamped(a, b, std::clamp(t, 0.0, 1.0)); + } + Vector2 Vector2::LerpUnclamped(Vector2 a, Vector2 b, double t) + { + return a + ((b - a) * t); + } + Vector2 Vector2::MoveTowards(Vector2 current, Vector2 target, double maxDistanceDelta) + { + // Ignore if it is exactly on the same point + if (current == target) + return target; + + // Calculate new position + Vector2 DELTA = (target - current).GetNormalised() * maxDistanceDelta; + Vector2 newPos = current + DELTA; + + // Check if check if is behind or ahead of target + Vector2 DIFF = target - newPos; + if (Dot(DELTA, DIFF) < 0.0) + { + newPos = target; + } + return newPos; + } + Vector2 Vector2::operator+(Vector2 lhs, Vector2 rhs) + { + return Vector2 + ( + lhs.x + rhs.x, + lhs.y + rhs.y + ); + } + Vector2 Vector2::operator-(Vector2 lhs, Vector2 rhs) + { + return Vector2 + ( + lhs.x - rhs.x, + lhs.y - rhs.y + ); + } + Vector2 Vector2::operator*(Vector2 lhs, Vector2 rhs) + { + return Vector2 + ( + lhs.x * rhs.x, + lhs.y * rhs.y + ); + } + Vector2 Vector2::operator*(Vector2 lhs, double rhs) + { + return Vector2 + ( + lhs.x * rhs, + lhs.y * rhs + ); + } + Vector2 Vector2::operator/(Vector2 lhs, double rhs) + { + return Vector2 + ( + lhs.x / rhs, + lhs.y / rhs + ); + } + bool Vector2::operator==(Vector2 lhs, Vector2 rhs) + { + return lhs.Equals(rhs); + } + bool Vector2::operator!=(Vector2 lhs, Vector2 rhs) + { + return !(lhs == rhs); + } +} // namespace PlushieAPI::Mathematics \ No newline at end of file diff --git a/SHADE_Managed/src/Math/Vector2.hxx b/SHADE_Managed/src/Math/Vector2.hxx new file mode 100644 index 00000000..69a6110f --- /dev/null +++ b/SHADE_Managed/src/Math/Vector2.hxx @@ -0,0 +1,396 @@ +/************************************************************************************//*! +\file Vector2.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 2, 2021 +\brief Contains the definitions of Vector2 struct. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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 + +// Standard Libraries +#include + +namespace SHADE +{ + /// + /// CLR version of the the PlushieEngine's Vector2 class that represents a + /// 2-Dimensional Vector. Designed to closely match Unity's Vector2 struct. + /// + [System::Runtime::InteropServices::StructLayout(System::Runtime::InteropServices::LayoutKind::Sequential)] + public value struct Vector2 : public System::IEquatable + { + public: + /*-----------------------------------------------------------------------------*/ + /* Constants */ + /*-----------------------------------------------------------------------------*/ + #pragma region Constants + /// + /// Shorthand for writing Vector2(0, -1). + /// + static initonly Vector2 Down = Vector2(0.0, -1.0); + /// + /// Shorthand for writing Vector2(-1, 0). + /// + static initonly Vector2 Left = Vector2(-1.0, 0.0); + /// + /// Shorthand for writing Vector2(double.NegativeInfinity, + /// double.NegativeInfinity). + /// + static initonly Vector2 NegativeInfinity = Vector2(std::numeric_limits::lowest(), std::numeric_limits::lowest()); + /// + /// Shorthand for writing Vector2(1, 1). + /// + static initonly Vector2 One = Vector2(1.0, 1.0); + /// + /// Shorthand for writing Vector2(double.PositiveInfinity, + /// double.PositiveInfinity). + /// + static initonly Vector2 PositiveInfinity = Vector2(std::numeric_limits::max(), std::numeric_limits::max()); + /// + /// Shorthand for writing Vector2(1, 0). + /// + static initonly Vector2 Right = Vector2(1.0, 0.0); + /// + /// Shorthand for writing Vector2(0, 1). + /// + static initonly Vector2 Up = Vector2(0.0, 1.0); + /// + /// Shorthand for writing Vector2(0, 0). + /// + static initonly Vector2 Zero = Vector2(0.0, 0.0); + #pragma endregion + + /*-----------------------------------------------------------------------------*/ + /* Public Members */ + /*-----------------------------------------------------------------------------*/ + /// + /// X-component of the Vector2. + /// + double x; + /// + /// Y-component of the Vector2. + /// + double y; + + /*-----------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------*/ + /// + /// Constructor to construct a Vector2 with the specified components with the + /// Y-component set to 0.0. + /// + /// X-coordinate to set. + Vector2(double _x); + /// + /// Constructor to construct a Vector2 with the specified components.. + /// + /// X-coordinate to set. + /// Y-coordinate to set. + Vector2(double _x, double _y); + + /*-----------------------------------------------------------------------------*/ + /* Usage Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Normalises this current Vector2. This changes the data of this Vector2. + /// If you would like to get a copy, use GetNormalised() instead. + /// This function does nothing to a zero vector. + /// + void Normalise(); + /// + /// Creates a copy of this Vector2 and returns a normalized version. + /// + /// + /// Returns a normalised copy of this Vector2. + /// If this Vector2 is a zero vector, a zero vector will be returned. + /// + Vector2 GetNormalised(); + /// + /// Calculates and returns the magnitude of this Vector2. Note that this function + /// incurs a performance cost from the square root calculation. If you do not + /// need the precise magnitude, consider using GetSqrMagnitude() instead. + /// + /// Returns the length of this Vector2. + double GetMagnitude(); + /// + /// Calculates and returns the squared magnitude of this Vector2. + /// + /// Returns the squared length of this Vector2. + double GetSqrMagnitude(); + /// + /// Calculates and returns the angle of this vector from the right vector. This + /// function returns values between -Math.PI and Math.PI. + /// + /// Returns the angle of this vector from the right vector in radians. + double AngleFromRightRadians(); + /// + /// Calculates and returns the angle of this vector from the right vector. This + /// function returns values between -180.0 and 180.0. + /// + /// Returns the angle of this vector from the right vector in degrees. + double AngleFromRightDegrees(); + /// + /// Checks if a specified point is near this Vector2 that represents a point with + /// a tolerance value of PLS_EPSILON. + /// + /// The other point to check if we are near. + /// + /// True if this Vector2 representing a point and the specified point are within + /// the range of the specified tolerance. False otherwise. + /// + bool IsNearPoint(Vector2 point); + /// + /// Checks if a specified point is near this Vector2 that represents a point. + /// + /// The other point to check if we are near. + /// + /// The amount of tolerance before we consider these points as "near". + /// + /// + /// True if this Vector2 representing a point and the specified point are within + /// the range of the specified tolerance. False otherwise. + /// + bool IsNearPoint(Vector2 point, double tolerance); + + /*-----------------------------------------------------------------------------*/ + /* IEquatable */ + /*-----------------------------------------------------------------------------*/ + /// + /// Compares equality with an object of the same type. + /// + /// The object to compare with. + /// True if both objects are the same. + virtual bool Equals(Vector2 other); + + /*-----------------------------------------------------------------------------*/ + /* Object */ + /*-----------------------------------------------------------------------------*/ + /// + /// Compares equality with another unboxed object. + /// + /// The unboxed object to compare with. + /// True if both objects are the same. + bool Equals(Object^ o) override; + /// + /// Gets a unique hash for this object. + /// + /// Unique hash for this object. + int GetHashCode() override; + + /*-----------------------------------------------------------------------------*/ + /* Static Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Checks if two specified Vector2s are near in value. + /// + /// Vector2 to check if is near in value. + /// Another Vector2 to check if is near in value. + /// + /// True if the two Vector2s are within the tolerance value specified + /// + static bool IsNear(Vector2 lhs, Vector2 rhs); + /// + /// Checks if two specified Vector2s are near in value. + /// + /// Vector2 to check if is near in value. + /// Another Vector2 to check if is near in value. + /// + /// Amount of tolerance to do the comparison with. + /// + /// + /// True if the two Vector2s are within the tolerance value specified + /// + static bool IsNear(Vector2 lhs, Vector2 rhs, double tolerance); + /// + /// Computes and returns the dot product of 2 specified Vector2s. + /// + /// Vector2 to calculate dot product with. + /// Another Vector2 to calculate dot product with. + /// + /// Scalar value representing the dot product of the two Vector2s. + /// + static double Dot(Vector2 lhs, Vector2 rhs); + /// + /// Computes the inward perpendicular Vector2 to the specified Vector2. + /// Equivalent to calling Perpendicular(lhs, true). This means, the + /// resultant Vector2 is rotated 90-degrees in a counter-clockwise. + /// + /// Vector2 to find a perpendicular of. + /// + /// The perpendicular Vector2 relative to the specified Vector2. + /// + static Vector2 Perpendicular(Vector2 lhs); + /// + /// Computes a perpendicular Vector2 to the specified Vector2. + /// + /// Vector2 to find a perpendicular of. + /// + /// Whether the inward perpendicular Vector is retrieved. If true, the + /// resultant vector is rotated 90-degrees in a counter-clockwise. + /// + /// The perpendicular Vector2 relative to the specified Vector2. + /// + static Vector2 Perpendicular(Vector2 lhs, bool inward); + /// + /// Computes and returns a Vector2 projection. + /// + /// Vector2 to project. + /// Vector2 to project onto. + /// The Vector2 that represents the projected vec onto direction. + static Vector2 Project(Vector2 vec, Vector2 direction); + /// + /// Reflects a Vector2 across another Vector2. + /// + /// A Vector2 to reflect. + /// A normal to reflect the Vector2 across. + /// The Vector2 that represents vec reflected across normal. + static Vector2 Reflect(Vector2 vec, Vector2 normal); + /// + /// Rotates a Vector2 on the Z-axis by a specified angle in an anti-clockwise + /// direction. + /// + /// A Vector2 to rotate. + /// + /// Angle to rotate the vector by in an anti-clockwise direction in radians. + /// + /// The Vector2 that represents the rotated vector. + static Vector2 RotateRadians(Vector2 vec, double radians); + /// + /// Rotates a Vector2 on the Z-axis by a specified angle in an anti-clockwise + /// direction. + /// + /// A Vector2 to rotate. + /// + /// Angle to rotate the vector by in an anti-clockwise direction in degrees. + /// + /// The Vector2 that represents the rotated vector. + static Vector2 RotateDegrees(Vector2 vec, double degrees); + /// + /// Computes and returns a Vector2 that is made from the smallest components of + /// the two specified Vector2s. + /// + /// Vector2 to calculate minimum Vector2 with. + /// Another Vector2 to calculate minimum Vector2 with. + /// + /// The Vector2 that contains the smallest components of the two specified + /// Vector2s. + /// + static Vector2 Min(Vector2 lhs, Vector2 rhs); + /// + /// Computes and returns a Vector2 that is made from the largest components of + /// the two specified Vector2s. + /// + /// Vector2 to calculate maximum Vector2 with. + /// Another Vector2 to calculate maximum Vector2 with. + /// + /// The Vector2 that contains the largest components of the two specified + /// Vector2s. + /// + static Vector2 Max(Vector2 lhs, Vector2 rhs); + /// + /// Linearly interpolates between two specified points. + /// This is most commonly used to find a point some fraction of the way along a + /// line between two endpoints. + /// + /// The start Vector2, returned when t = 0.0. + /// The end Vector2, returned when t = 1.0. + /// + /// Value used to interpolate between a and b which is clamped to + /// the range[0, 1]. + /// + /// The interpolated Vector2. + static Vector2 Lerp(Vector2 a, Vector2 b, double t); + /// + /// Linearly interpolates between two specified points. + /// This is most commonly used to find a point some fraction of the way along a + /// line between two endpoints. + /// Unlike Lerp(), t is not clamped to a range at all. + /// + /// The start Vector2, returned when t = 0.0. + /// The end Vector2, returned when t = 1.0. + /// Value used to interpolate between a and b. + /// The interpolated Vector2. + static Vector2 LerpUnclamped(Vector2 a, Vector2 b, double t); + /// + /// Moves a point current towards target. + /// Similar to Lerp(), however, the function will ensure that the distance never + /// exceeds maxDistanceDelta. Negative values of maxDistanceDelta pushes the + /// vector away from target + /// + /// The current position of the point. + /// The target position to move to. + /// Maximum distance moved per call. + /// Vector representing the moved point. + static Vector2 MoveTowards(Vector2 current, Vector2 target, double maxDistanceDelta); + + /*-----------------------------------------------------------------------------*/ + /* Overloaded Operators */ + /*-----------------------------------------------------------------------------*/ + /// + /// Adds two Vector2s together and returns the result. + /// + /// Vector2 to add. + /// Another Vector2 to add. + /// The result of lhs added to rhs + static Vector2 operator+(Vector2 lhs, Vector2 rhs); + /// + /// Subtracts a Vector2 from another Vector2 and returns the result. + /// + /// Vector2 to subtract from. + /// Another Vector2 to subtract. + /// The result of rhs subtracted from lhs. + static Vector2 operator-(Vector2 lhs, Vector2 rhs); + /// + /// Calculates the component-wise multiplication of two Vector2s and returns the + /// result. + /// + /// Vector2 to multiply with. + /// Another Vector2 to multiply with. + /// The result of rhs subtracted from lhs. + static Vector2 operator*(Vector2 lhs, Vector2 rhs); + /// + /// Calculates the multiplication of a Vector2 with a scalar value and returns + /// the result. + /// + /// Vector2 to multiply with. + /// Scalar to multiply with. + /// The result of the scalar multiplication. + static Vector2 operator*(Vector2 lhs, double rhs); + /// + /// Calculates the division of a Vector2 with a scalar value and returns + /// the result. + /// + /// Scalar to divide with. + /// Vector2 to divide with. + /// The result of the scalar division. + static Vector2 operator/(Vector2 lhs, double rhs); + /// + /// Checks if two Vector2s are approximately equal. This is equivalent to + /// calling Vector2.IsNear() with default tolerance values. + /// + /// Vector2 to compare. + /// Another Vector2 to compare. + /// + /// True if all components are approximately equal within the default + /// tolerance value. + /// + static bool operator==(Vector2 lhs, Vector2 rhs); + /// + /// Checks if two Vector2s are not approximately equal. This is equivalent to + /// calling !Vector2.IsNear() with default tolerance values. + /// + /// Vector2 to compare. + /// Another Vector2 to compare. + /// + /// True if all components are not approximately equal within the default + /// tolerance value. + /// + static bool operator!=(Vector2 lhs, Vector2 rhs); + }; +} diff --git a/SHADE_Managed/src/Math/Vector3.cxx b/SHADE_Managed/src/Math/Vector3.cxx new file mode 100644 index 00000000..26ff5a72 --- /dev/null +++ b/SHADE_Managed/src/Math/Vector3.cxx @@ -0,0 +1,278 @@ +/************************************************************************************//*! +\file Vector3.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 24, 2021 +\brief Contains the definitions of functions of the Vector3 struct. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "Vector3.hxx" +// Standard Libraries +#include +#include +// Project Headers +#include "Math.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Constructors */ + /*---------------------------------------------------------------------------------*/ + Vector3::Vector3(double _x) + : Vector3 {_x, 0.0, 0.0} + {} + Vector3::Vector3(double _x, double _y) + : Vector3 {_x, _y, 0.0} + {} + Vector3::Vector3(double _x, double _y, double _z) + : x { _x } + , y { _y } + , z { _z } + {} + Vector3::Vector3(Vector2 vec) + : Vector3(vec.x, vec.y) + {} + + /*---------------------------------------------------------------------------------*/ + /* Usage Functions */ + /*---------------------------------------------------------------------------------*/ + void Vector3::Normalise() + { + *this = GetNormalised(); + } + + Vector3 Vector3::GetNormalised() + { + return *this / GetSqrMagnitude(); + } + + double Vector3::GetMagnitude() + { + return sqrt(x * x + y * y + z * z); + } + + double Vector3::GetSqrMagnitude() + { + return x * x + y * y + z * z; + } + + double Vector3::Angle2DFromRightRadians() + { + return atan2(y, x); + } + + double Vector3::Angle2DFromRightDegrees() + { + return Math::RadiansToDegrees(Angle2DFromRightRadians()); + } + + bool Vector3::IsNearPoint(Vector3 point) + { + return IsNearPoint(point, Math::Epsilon); + } + + bool Vector3::IsNearPoint(Vector3 point, double tolerance) + { + return (*this - point).GetSqrMagnitude() < (tolerance * tolerance); + } + + /*---------------------------------------------------------------------------------*/ + /* IEquatable */ + /*---------------------------------------------------------------------------------*/ + bool Vector3::Equals(Object^ o) + { + try + { + Vector3 vec = safe_cast(o); + return Equals(vec); + } + catch (System::InvalidCastException^) + { + return false; + } + } + + /*---------------------------------------------------------------------------------*/ + /* Object Overrides */ + /*---------------------------------------------------------------------------------*/ + bool Vector3::Equals(Vector3 other) + { + return IsNear(*this, other); + } + int Vector3::GetHashCode() + { + const int HASH = 19; + const int HASH2 = 23; + return x.GetHashCode() * HASH + y.GetHashCode() * HASH2 + z.GetHashCode(); + } + + /*---------------------------------------------------------------------------------*/ + /* Static Functions */ + /*---------------------------------------------------------------------------------*/ + bool Vector3::IsNear(Vector3 lhs, Vector3 rhs) + { + return IsNear(lhs, rhs, Math::Epsilon); + } + bool Vector3::IsNear(Vector3 lhs, Vector3 rhs, double tolerance) + { + return (std::abs(lhs.x) - std::abs(rhs.x)) < tolerance + && + (std::abs(lhs.y) - std::abs(rhs.y)) < tolerance + && + (std::abs(lhs.z) - std::abs(rhs.z)) < tolerance; + } + double Vector3::Dot(Vector3 lhs, Vector3 rhs) + { + return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z; + } + Vector3 Vector3::Cross(Vector3 lhs, Vector3 rhs) + { + return Vector3(lhs.y * rhs.z - lhs.z * rhs.y, + lhs.z * rhs.x - lhs.x * rhs.z, + lhs.x * rhs.y - lhs.y * rhs.x); + } + Vector3 Vector3::Project(Vector3 vec, Vector3 direction) + { + return direction.GetNormalised() * vec.GetMagnitude(); + } + Vector3 Vector3::Reflect(Vector3 vec, Vector3 normal) + { + return vec - (Project(vec, normal.GetNormalised()) * 2.0); + } + Vector3 Vector3::RotateRadians(Vector3 vec, double radians) + { + const double SINE = sin(radians); + const double COSINE = cos(radians); + + return Vector3 + ( + vec.x * COSINE - vec.y * SINE, + vec.x * SINE + vec.y * COSINE, + vec.z + ); + } + Vector3 Vector3::RotateDegrees(Vector3 vec, double degrees) + { + return RotateRadians(vec, Math::DegreesToRadians(degrees)); + } + Vector3 Vector3::Min(Vector3 lhs, Vector3 rhs) + { + double lx = lhs.x, rx = rhs.x; + double ly = lhs.y, ry = rhs.y; + double lz = lhs.z, rz = rhs.z; + + return Vector3(std::min(lx, rx), + std::min(ly, ry), + std::min(lz, rz)); + } + Vector3 Vector3::Max(Vector3 lhs, Vector3 rhs) + { + double lx = lhs.x, rx = rhs.x; + double ly = lhs.y, ry = rhs.y; + double lz = lhs.z, rz = rhs.z; + + return Vector3(std::max(lx, rx), + std::max(ly, ry), + std::max(lz, rz)); + } + Vector3 Vector3::Lerp(Vector3 a, Vector3 b, double t) + { + return LerpUnclamped(a, b, std::clamp(t, 0.0, 1.0)); + } + Vector3 Vector3::LerpUnclamped(Vector3 a, Vector3 b, double t) + { + return a + ((b - a) * t); + } + Vector3 Vector3::MoveTowards(Vector3 current, Vector3 target, double maxDistanceDelta) + { + // Ignore if it is exactly on the same point + if (current == target) + return target; + + // Calculate new position + Vector3 DELTA = (target - current).GetNormalised() * maxDistanceDelta; + Vector3 newPos = current + DELTA; + + // Check if check if is behind or ahead of target + Vector3 DIFF = target - newPos; + if (Dot(DELTA, DIFF) < 0.0) + { + newPos = target; + } + return newPos; + } + Vector3 Vector3::operator+(Vector3 lhs, Vector3 rhs) + { + return Vector3 + ( + lhs.x + rhs.x, + lhs.y + rhs.y, + lhs.z + rhs.z + ); + } + Vector3 Vector3::operator-(Vector3 lhs, Vector3 rhs) + { + return Vector3 + ( + lhs.x - rhs.x, + lhs.y - rhs.y, + lhs.z - rhs.z + ); + } + Vector3 Vector3::operator*(Vector3 lhs, Vector3 rhs) + { + return Vector3 + ( + lhs.x * rhs.x, + lhs.y * rhs.y, + lhs.z * rhs.z + ); + } + Vector3 Vector3::operator*(Vector3 lhs, double rhs) + { + return Vector3 + ( + lhs.x * rhs, + lhs.y * rhs, + lhs.z * rhs + ); + } + Vector3 Vector3::operator/(Vector3 lhs, double rhs) + { + return Vector3 + ( + lhs.x / rhs, + lhs.y / rhs, + lhs.z / rhs + ); + } + bool Vector3::operator==(Vector3 lhs, Vector3 rhs) + { + return lhs.Equals(rhs); + } + bool Vector3::operator!=(Vector3 lhs, Vector3 rhs) + { + return !(lhs == rhs); + } + + /*---------------------------------------------------------------------------------*/ + /* Conversion Operators */ + /*---------------------------------------------------------------------------------*/ + Vector3::operator Vector2(Vector3 vec) + { + return Vector2(vec.x, vec.y); + } + + Vector3::operator Vector3(Vector2 vec) + { + return Vector3(vec); + } +} // namespace PlushieAPI::Mathematics \ No newline at end of file diff --git a/SHADE_Managed/src/Math/Vector3.hxx b/SHADE_Managed/src/Math/Vector3.hxx new file mode 100644 index 00000000..e6cdc7d4 --- /dev/null +++ b/SHADE_Managed/src/Math/Vector3.hxx @@ -0,0 +1,425 @@ +/************************************************************************************//*! +\file Vector3.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 24, 2021 +\brief Contains the definitions of Vector3 struct. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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 + +// Standard Libraries +#include +// Project Includes +#include "Vector2.hxx" + +namespace SHADE +{ + /// + /// CLR version of the the PlushieEngine's Vector3 class that represents a + /// 3-Dimensional Vector. Designed to closely match Unity's Vector3 struct. + /// + [System::Runtime::InteropServices::StructLayout(System::Runtime::InteropServices::LayoutKind::Sequential)] + public value struct Vector3 : public System::IEquatable + { + public: + /*-----------------------------------------------------------------------------*/ + /* Constants */ + /*-----------------------------------------------------------------------------*/ + #pragma region Constants + /// + /// Shorthand for writing Vector3(0, 0, -1). + /// + static initonly Vector3 Back = Vector3(0.0, 0.0, -1.0); + /// + /// Shorthand for writing Vector3(0, -1, 0). + /// + static initonly Vector3 Down = Vector3(0.0, -1.0, 0.0); + /// + /// Shorthand for writing Vector3(0, 0, 1). + /// + static initonly Vector3 Forward = Vector3(0.0, 0.0, 1.0); + /// + /// Shorthand for writing Vector3(-1, 0, 0). + /// + static initonly Vector3 Left = Vector3(-1.0, 0.0, 0.0); + /// + /// Shorthand for writing Vector3(double.NegativeInfinity, + /// double.NegativeInfinity, double.NegativeInfinity). + /// + static initonly Vector3 NegativeInfinity = Vector3(std::numeric_limits::lowest(), + std::numeric_limits::lowest(), + std::numeric_limits::lowest()); + /// + /// Shorthand for writing Vector3(1, 1, 1). + /// + static initonly Vector3 One = Vector3(1.0, 1.0, 1.0); + /// + /// Shorthand for writing Vector3(double.PositiveInfinity, + /// double.PositiveInfinity, double.PositiveInfinity). + /// + static initonly Vector3 PositiveInfinity = Vector3(std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()); + /// + /// Shorthand for writing Vector3(1, 0, 0). + /// + static initonly Vector3 Right = Vector3(1.0, 0.0, 0.0); + /// + /// Shorthand for writing Vector3(0, 1, 0). + /// + static initonly Vector3 Up = Vector3(0.0, 1.0, 0.0); + /// + /// Shorthand for writing Vector3(0, 0, 0). + /// + static initonly Vector3 Zero = Vector3(0.0, 0.0, 0.0); + #pragma endregion + + /*-----------------------------------------------------------------------------*/ + /* Public Members */ + /*-----------------------------------------------------------------------------*/ + /// + /// X-component of the Vector3. + /// + double x; + /// + /// Y-component of the Vector3. + /// + double y; + /// + /// Z-component of the Vector3. + /// + double z; + + /*-----------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------*/ + /// + /// Constructor to construct a Vector3 with the specified components with the + /// Y and Z-component set to 0.0. + /// + /// X-coordinate to set. + Vector3(double _x); + /// + /// Constructor to construct a Vector3 with the specified components with the + /// Z-component set to 0.0. + /// + /// X-coordinate to set. + /// Y-coordinate to set. + Vector3(double _x, double _y); + /// + /// Constructor to construct a Vector3 with the specified components. + /// + /// X-coordinate to set. + /// Y-coordinate to set. + /// Z-coordinate to set. + Vector3(double _x, double _y, double _z); + /// + /// Conversion constructor to construct a Vector3 using a Vector2. + /// + /// + Vector3(Vector2 vec); + + /*-----------------------------------------------------------------------------*/ + /* Usage Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Normalises this current Vector3. This changes the data of this Vector3. + /// If you would like to get a copy, use GetNormalised() instead. + /// This function does nothing to a zero vector. + /// + void Normalise(); + /// + /// Creates a copy of this Vector3 and returns a normalized version. + /// + /// + /// Returns a normalised copy of this Vector3. + /// If this Vector3 is a zero vector, a zero vector will be returned. + /// + Vector3 GetNormalised(); + /// + /// Calculates and returns the magnitude of this Vector3. Note that this function + /// incurs a performance cost from the square root calculation. If you do not + /// need the precise magnitude, consider using GetSqrMagnitude() instead. + /// + /// Returns the length of this Vector3. + double GetMagnitude(); + /// + /// Calculates and returns the squared magnitude of this Vector3. + /// + /// Returns the squared length of this Vector3. + double GetSqrMagnitude(); + /// + /// Calculates and returns the angle of this vector from the right vector. This + /// function returns values between -Math.PI and Math.PI. + /// + /// Returns the angle of this vector from the right vector in radians. + double Angle2DFromRightRadians(); + /// + /// Calculates and returns the angle of this vector from the right vector. This + /// function returns values between -180.0 and 180.0. + /// + /// Returns the angle of this vector from the right vector in degrees. + double Angle2DFromRightDegrees(); + /// + /// Checks if a specified point is near this Vector3 that represents a point with + /// a tolerance value of PLS_EPSILON. + /// + /// The other point to check if we are near. + /// + /// True if this Vector3 representing a point and the specified point are within + /// the range of the specified tolerance. False otherwise. + /// + bool IsNearPoint(Vector3 point); + /// + /// Checks if a specified point is near this Vector3 that represents a point. + /// + /// The other point to check if we are near. + /// + /// The amount of tolerance before we consider these points as "near". + /// + /// + /// True if this Vector3 representing a point and the specified point are within + /// the range of the specified tolerance. False otherwise. + /// + bool IsNearPoint(Vector3 point, double tolerance); + + /*-----------------------------------------------------------------------------*/ + /* IEquatable */ + /*-----------------------------------------------------------------------------*/ + /// + /// Compares equality with an object of the same type. + /// + /// The object to compare with. + /// True if both objects are the same. + virtual bool Equals(Vector3 other); + + /*-----------------------------------------------------------------------------*/ + /* Object */ + /*-----------------------------------------------------------------------------*/ + /// + /// Compares equality with another unboxed object. + /// + /// The unboxed object to compare with. + /// True if both objects are the same. + bool Equals(Object^ o) override; + /// + /// Gets a unique hash for this object. + /// + /// Unique hash for this object. + int GetHashCode() override; + + /*-----------------------------------------------------------------------------*/ + /* Static Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Checks if two specified Vector3s are near in value. + /// + /// Vector3 to check if is near in value. + /// Another Vector3 to check if is near in value. + /// + /// True if the two Vector3s are within the tolerance value specified + /// + static bool IsNear(Vector3 lhs, Vector3 rhs); + /// + /// Checks if two specified Vector3s are near in value. + /// + /// Vector3 to check if is near in value. + /// Another Vector3 to check if is near in value. + /// Amount of tolerance to do the comparison with. + /// + /// True if the two Vector3s are within the tolerance value specified + /// + static bool IsNear(Vector3 lhs, Vector3 rhs, double tolerance); + /// + /// Computes and returns the dot product of 2 specified Vector3s. + /// + /// Vector3 to calculate dot product with. + /// Another Vector3 to calculate dot product with. + /// Scalar value representing the dot product of the two Vector3s. + static double Dot(Vector3 lhs, Vector3 rhs); + /// + /// Computes and returns the cross product of 2 specified Vector3s. + /// + /// Vector3 to calculate cross product with. + /// Another Vector3 to calculate cross product with. + /// The cross product of the two Vector3s. + static Vector3 Cross(Vector3 lhs, Vector3 rhs); + /// + /// Computes and returns a Vector3 projection. + /// + /// Vector3 to project. + /// Vector3 to project onto. + /// The Vector3 that represents the projected vec onto direction. + static Vector3 Project(Vector3 vec, Vector3 direction); + /// + /// Reflects a Vector3 across another Vector3. + /// + /// A Vector3 to reflect. + /// A normal to reflect the Vector3 across. + /// The Vector3 that represents vec reflected across normal. + static Vector3 Reflect(Vector3 vec, Vector3 normal); + /// + /// Rotates a Vector3 on the Z-axis by a specified angle in an anti-clockwise + /// direction. + /// + /// A Vector3 to rotate. + /// + /// Angle to rotate the vector by in an anti-clockwise direction in radians. + /// + /// The Vector3 that represents the rotated vector. + static Vector3 RotateRadians(Vector3 vec, double radians); + /// + /// Rotates a Vector3 on the Z-axis by a specified angle in an anti-clockwise + /// direction. + /// + /// A Vector3 to rotate. + /// + /// Angle to rotate the vector by in an anti-clockwise direction in degrees. + /// + /// The Vector3 that represents the rotated vector. + static Vector3 RotateDegrees(Vector3 vec, double degrees); + /// + /// Computes and returns a Vector3 that is made from the smallest components of + /// the two specified Vector3s. + /// + /// Vector3 to calculate minimum Vector3 with. + /// Another Vector3 to calculate minimum Vector3 with. + /// + /// The Vector3 that contains the smallest components of the two specified + /// Vector3s. + /// + static Vector3 Min(Vector3 lhs, Vector3 rhs); + /// + /// Computes and returns a Vector3 that is made from the largest components of + /// the two specified Vector3s. + /// + /// Vector3 to calculate maximum Vector3 with. + /// Another Vector3 to calculate maximum Vector3 with. + /// + /// The Vector3 that contains the largest components of the two specified + /// Vector3s. + /// + static Vector3 Max(Vector3 lhs, Vector3 rhs); + /// + /// Linearly interpolates between two specified points. + /// This is most commonly used to find a point some fraction of the way along a + /// line between two endpoints. + /// + /// The start Vector3, returned when t = 0.0. + /// The end Vector3, returned when t = 1.0. + /// + /// Value used to interpolate between a and b which is clamped to + /// the range[0, 1]. + /// + /// The interpolated Vector3. + static Vector3 Lerp(Vector3 a, Vector3 b, double t); + /// + /// Linearly interpolates between two specified points. + /// This is most commonly used to find a point some fraction of the way along a + /// line between two endpoints. + /// Unlike Lerp(), t is not clamped to a range at all. + /// + /// The start Vector3, returned when t = 0.0. + /// The end Vector3, returned when t = 1.0. + /// Value used to interpolate between a and b. + /// The interpolated Vector3. + static Vector3 LerpUnclamped(Vector3 a, Vector3 b, double t); + /// + /// Moves a point current towards target. + /// Similar to Lerp(), however, the function will ensure that the distance never + /// exceeds maxDistanceDelta. Negative values of maxDistanceDelta pushes the + /// vector away from target + /// + /// The current position of the point. + /// The target position to move to. + /// Maximum distance moved per call. + /// Vector representing the moved point. + static Vector3 MoveTowards(Vector3 current, Vector3 target, double maxDistanceDelta); + + /*-----------------------------------------------------------------------------*/ + /* Overloaded Operators */ + /*-----------------------------------------------------------------------------*/ + /// + /// Adds two Vector3s together and returns the result. + /// + /// Vector3 to add. + /// Another Vector3 to add. + /// The result of lhs added to rhs + static Vector3 operator+(Vector3 lhs, Vector3 rhs); + /// + /// Subtracts a Vector3 from another Vector3 and returns the result. + /// + /// Vector3 to subtract from. + /// Another Vector3 to subtract. + /// The result of rhs subtracted from lhs. + static Vector3 operator-(Vector3 lhs, Vector3 rhs); + /// + /// Calculates the component-wise multiplication of two Vector3s and returns the + /// result. + /// + /// Vector3 to multiply with. + /// Another Vector3 to multiply with. + /// The result of rhs subtracted from lhs. + static Vector3 operator*(Vector3 lhs, Vector3 rhs); + /// + /// Calculates the multiplication of a Vector3 with a scalar value and returns + /// the result. + /// + /// Vector3 to multiply with. + /// Scalar to multiply with. + /// The result of the scalar multiplication. + static Vector3 operator*(Vector3 lhs, double rhs); + /// + /// Calculates the division of a Vector3 with a scalar value and returns + /// the result. + /// + /// Scalar to divide with. + /// Vector3 to divide with. + /// The result of the scalar division. + static Vector3 operator/(Vector3 lhs, double rhs); + /// + /// Checks if two Vector3s are approximately equal. This is equivalent to + /// calling Vector3.IsNear() with default tolerance values. + /// + /// Vector3 to compare. + /// Another Vector3 to compare. + /// + /// True if all components are approximately equal within the default + /// tolerance value. + /// + static bool operator==(Vector3 lhs, Vector3 rhs); + /// + /// Checks if two Vector3s are not approximately equal. This is equivalent to + /// calling !Vector3.IsNear() with default tolerance values. + /// + /// Vector3 to compare. + /// Another Vector3 to compare. + /// + /// True if all components are not approximately equal within the default + /// tolerance value. + /// + static bool operator!=(Vector3 lhs, Vector3 rhs); + + /*-----------------------------------------------------------------------------*/ + /* Conversion Operators */ + /*-----------------------------------------------------------------------------*/ + /// + /// Explicit conversion operator to enable explicit casting from a Vector3 to a + /// Vector2. + /// + /// Vector3 to convert from. + static explicit operator Vector2(Vector3 vec); + /// + /// Explicit conversion operator to enable explicit casting from a Vector2 to a + /// Vector3. + /// + /// Vector2 to convert from. + static explicit operator Vector3(Vector2 vec); + }; +} // namespace PlushieAPI::Mathematics diff --git a/SHADE_Managed/src/SHpch.cpp b/SHADE_Managed/src/SHpch.cpp new file mode 100644 index 00000000..2a36c693 --- /dev/null +++ b/SHADE_Managed/src/SHpch.cpp @@ -0,0 +1,10 @@ +/**************************************************************************************** + * \file SHpch.h + * \brief Empty source file for generating SHADE Engine's precompiled header. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include "SHpch.h" \ No newline at end of file diff --git a/SHADE_Managed/src/SHpch.h b/SHADE_Managed/src/SHpch.h new file mode 100644 index 00000000..b54a8a5b --- /dev/null +++ b/SHADE_Managed/src/SHpch.h @@ -0,0 +1,31 @@ +/**************************************************************************************** + * \file SHpch.h + * \brief Precompiled header file for SHADE Engine. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include +// C RunTime Header Files +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/SHADE_Managed/src/Scripts/Script.cxx b/SHADE_Managed/src/Scripts/Script.cxx new file mode 100644 index 00000000..ecd27325 --- /dev/null +++ b/SHADE_Managed/src/Scripts/Script.cxx @@ -0,0 +1,171 @@ +/************************************************************************************//*! +\file Script.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the definition of the functions for the PlushieScript managed + class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "Script.hxx" +// Project Headers +#include "Utility/Debug.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Component Access Functions */ + /*---------------------------------------------------------------------------------*/ + generic + T Script::AddComponent() + { + return owner.AddComponent(); + } + generic + T Script::GetComponent() + { + return owner.GetComponent(); + } + + generic + T Script::GetComponentInChildren() + { + return owner.GetComponentInChildren(); + } + + generic + T Script::EnsureComponent() + { + return owner.EnsureComponent(); + } + generic + void Script::RemoveComponent() + { + throw gcnew System::NotImplementedException; + //ECS::RemoveComponent(owner.GetNativeEntity()); + } + + /*---------------------------------------------------------------------------------*/ + /* Script Access Functions */ + /*---------------------------------------------------------------------------------*/ + generic + T Script::AddScript() + { + throw gcnew System::NotImplementedException; + //return ScriptStore::AddScript(owner.GetEntity()); + } + generic + T Script::GetScript() + { + throw gcnew System::NotImplementedException; + //return ScriptStore::GetScript(owner.GetEntity()); + } + + generic + T Script::GetScriptInChildren() + { + throw gcnew System::NotImplementedException; + //return ScriptStore::GetScriptInChildren(owner.GetEntity()); + } + + generic + System::Collections::Generic::IEnumerable^ Script::GetScripts() + { + throw gcnew System::NotImplementedException; + //return ScriptStore::GetScripts(owner.GetEntity()); + } + + generic + void Script::RemoveScript() + { + throw gcnew System::NotImplementedException; + //ScriptStore::RemoveScript(owner.GetEntity()); + } + + /*---------------------------------------------------------------------------------*/ + /* "All-time" Lifecycle Functions */ + /*---------------------------------------------------------------------------------*/ + void Script::OnAttached() + { + SAFE_NATIVE_CALL_BEGIN + onAttached(); + SAFE_NATIVE_CALL_END(this) + } + void Script::OnDetached() + { + SAFE_NATIVE_CALL_BEGIN + onDetatched(); + SAFE_NATIVE_CALL_END(this) + } + + /*---------------------------------------------------------------------------------*/ + /* Lifecycle Functions */ + /*---------------------------------------------------------------------------------*/ + void Script::Awake() + { + SAFE_NATIVE_CALL_BEGIN + awake(); + SAFE_NATIVE_CALL_END(this) + } + void Script::Start() + { + SAFE_NATIVE_CALL_BEGIN + start(); + SAFE_NATIVE_CALL_END(this) + } + void Script::FixedUpdate() + { + SAFE_NATIVE_CALL_BEGIN + fixedUpdate(); + SAFE_NATIVE_CALL_END(this) + } + void Script::Update() + { + SAFE_NATIVE_CALL_BEGIN + update(); + SAFE_NATIVE_CALL_END(this) + } + void Script::LateUpdate() + { + SAFE_NATIVE_CALL_BEGIN + lateUpdate(); + SAFE_NATIVE_CALL_END(this) + } + void Script::OnDestroy() + { + SAFE_NATIVE_CALL_BEGIN + onDestroy(); + SAFE_NATIVE_CALL_END(this) + } + + /*---------------------------------------------------------------------------------*/ + /* Constructors */ + /*---------------------------------------------------------------------------------*/ + Script::Script(GameObject gameObj) + : owner { gameObj } + {} + + /*---------------------------------------------------------------------------------*/ + /* Virtual "All-Time" Lifecycle Functions */ + /*---------------------------------------------------------------------------------*/ + void Script::onAttached() {} + void Script::onDetatched() {} + + /*---------------------------------------------------------------------------------*/ + /* Virtual Lifecycle Functions */ + /*---------------------------------------------------------------------------------*/ + void Script::awake() {} + void Script::start() {} + void Script::fixedUpdate() {} + void Script::update() {} + void Script::lateUpdate() {} + void Script::onDestroy() {} +}// namespace PlushieAPI diff --git a/SHADE_Managed/src/Scripts/Script.hxx b/SHADE_Managed/src/Scripts/Script.hxx new file mode 100644 index 00000000..cef9f4cd --- /dev/null +++ b/SHADE_Managed/src/Scripts/Script.hxx @@ -0,0 +1,274 @@ +/************************************************************************************//*! +\file Script.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the definition of the Script class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +// Project Includes +#include "Engine/GameObject.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*---------------------------------------------------------------------------------*/ + ref class BaseComponent; + + /*---------------------------------------------------------------------------------*/ + /* Class Definitions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Class that forms the basis of all "script"-objects that can be attached to + /// Entities to update each Entity's Components via C# code. + /// + public ref class Script + { + public: + /*-----------------------------------------------------------------------------*/ + /* Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// GameObject that this Script belongs to. + /// + property GameObject Owner + { + GameObject get() { return owner; } + } + + /*-----------------------------------------------------------------------------*/ + /* Component Access Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Adds a Component to the GameObject that this Script belongs to. + /// + /// + /// Type of the Component to add. Must be derived from BaseComponent. + /// + /// Reference to the Component that was added. + generic where T : BaseComponent + T AddComponent(); + /// + /// Gets a Component from the GameObject that this Script belongs to. + /// + /// + /// Type of the Component to get. Must be derived from BaseComponent. + /// + /// Reference to the Component that was retrieved. + generic where T : BaseComponent + T GetComponent(); + /// + /// Retrieves the first Component from this GameObject's children that matches + /// the specified type. + /// + /// + /// Type of the Component to get. Must be derived from BaseComponent. + /// + /// Reference to the Component that was retrieved. + generic where T : BaseComponent + T GetComponentInChildren(); + /// + /// Ensures a Component on the GameObject that this Script belongs to. + /// + /// + /// Type of the Component to ensure. Must be derived from BaseComponent. + /// + /// Reference to the Component. + generic where T : BaseComponent + T EnsureComponent(); + /// + /// Removes a Component from the GameObject that this Script belongs to. + /// + /// + /// Type of the Component to remove. Must be derived from BaseComponent. + /// + generic where T : BaseComponent + void RemoveComponent(); + + /*-----------------------------------------------------------------------------*/ + /* Script Access Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Adds a Script to this GameObject. + /// + /// + /// Type of script to add. + /// This needs to be a default constructable Script. + /// + /// Reference to the script added + generic where T : ref class, Script + T AddScript(); + /// + /// Retrieves the first Script from this GameObject that matches the specified + /// type. + /// + /// + /// Type of script to get. + /// This needs to be a default constructable Script. + /// + /// Reference to the script added + generic where T : ref class, Script + T GetScript(); + /// + /// Retrieves the first Script from this GameObject's children that matches the + /// specified type. + /// + /// + /// Type of script to get. + /// This needs to be a default constructable Script. + /// + /// Reference to the script added + generic where T : ref class, Script + T GetScriptInChildren(); + /// + /// Retrieves a immutable list of scripts from the specified Entity that + /// matches the specified type. + ///
+ /// Note that this function allocates. It should be used sparingly. + ///
+ /// + /// Type of scripts to get. + /// This needs to be a default constructable Script. + /// + /// + /// Immutable list of references to scripts of the specified type. + /// + generic where T : ref class, Script + System::Collections::Generic::IEnumerable^ GetScripts(); + /// + /// Removes all Scripts of the specified type from this GameObject. + /// + /// + /// Type of script to remove. + /// This needs to be a default constructable Script. + /// + generic where T : ref class, Script + void RemoveScript(); + + internal: + /*-----------------------------------------------------------------------------*/ + /* "All-Time" Lifecycle Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Used to call onAttached(). This is called immediately when this script is + /// attached to a GameObject. + /// + void OnAttached(); + /// + /// Used to call onDetached(). This is called immediately when this script is + /// detached from a GameObject. + /// + void OnDetached(); + + /*-----------------------------------------------------------------------------*/ + /* Lifecycle Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Used to call awake(). This should be called on the first frame that the + /// attached GameObject is active if they are a part of the scene. + /// + void Awake(); + /// + /// Used to call start(). This should be called on the first frame that the + /// attached GameObject is active but always after Awake(). + /// + void Start(); + /// + /// Used to call fixedUpdate(). This should be called in sync with Physics + /// update steps and thus in most cases will execute more than Update() will. + /// This will be called immediately before a Physics update step. + /// + void FixedUpdate(); + /// + /// Used to call update(). This should be called every frame before physics and + /// collision resolution. + /// + void Update(); + /// + /// Used to call lateUpdate(). This should be called every frame after physics + /// and collision resolution but before rendering. + /// + void LateUpdate(); + /// + /// Used to call onDestroy(). This should be called at the end of the frame + /// where the attached GameObject or this script is destroyed directly or + /// indirectly due to destruction of the owner. + /// + void OnDestroy(); + + protected: + /*-----------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------*/ + /// + /// Constructor for Script to tie it to a specific GameObject. + /// Constructors of derived Scripts should call this Constructor. + /// + /// + /// GameObject that this Script will be tied to. + /// + Script(GameObject gameObj); + + /*-----------------------------------------------------------------------------*/ + /* Virtual "All-Time" Lifecycle Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Called immediately once this script is attached to a GameObject. + /// + virtual void onAttached(); + /// + /// Called immediately once this script is detached from a GameObject. + /// + virtual void onDetatched(); + + /*-----------------------------------------------------------------------------*/ + /* Virtual Lifecycle Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Called on the first frame that the attached GameObject is active if they are + /// a part of the scene. + /// + virtual void awake(); + /// + /// Called on the first frame that the attached GameObject is active but always + /// after Awake(). + /// + virtual void start(); + /// + /// Called every frame in sync with Physics update steps and thus in most cases + /// will execute more than update() will. This will be called immediately before + /// a Physics update step. + /// + virtual void fixedUpdate(); + /// + /// Called every frame before physics and collision resolution. + /// + virtual void update(); + /// + /// Called every frame after physics and collision resolution but before + /// rendering. + /// + virtual void lateUpdate(); + /// + /// Called just before the end of the frame where the attached GameObject or + /// this script is destroyed directly or indirectly due to destruction of the + /// owner. + /// + virtual void onDestroy(); + + private: + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + GameObject owner; + }; + +} // namespace PlushieAPI diff --git a/SHADE_Managed/src/Scripts/ScriptStore.cxx b/SHADE_Managed/src/Scripts/ScriptStore.cxx new file mode 100644 index 00000000..9a9eff54 --- /dev/null +++ b/SHADE_Managed/src/Scripts/ScriptStore.cxx @@ -0,0 +1,673 @@ +/************************************************************************************//*! +\file ScriptStore.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the definition of the functions for the ScriptStore managed + static class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "ScriptStore.hxx" +// Standard Libraries +#include +// External Dependencies +#include "ECS_Base/System/SHEntityManager.h" +// Project Headers +#include "Utility/Debug.hxx" +#include "Utility/Convert.hxx" +#include "Script.hxx" +#include "Engine/Entity.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Scripts Manipulation Functions */ + /*---------------------------------------------------------------------------------*/ + generic + T ScriptStore::AddScript(Entity entity) + { + // Check if entity exists + if (!EntityUtils::IsValid(entity)) + throw gcnew System::ArgumentException("Invalid Entity provided to add a Script to."); + + System::Collections::Generic::List ^ entityScriptList; + + // Check if storage for scripts of this entity exists + if (!scripts.ContainsKey(entity)) + { + // Create a new list for this set of scripts + entityScriptList = gcnew System::Collections::Generic::List(); + scripts.Add(entity, entityScriptList); + } + else + { + entityScriptList = scripts[entity]; + } + + // Create the script and add it in + array^ params = gcnew array{GameObject(entity)}; + Script^ script = safe_cast(System::Activator::CreateInstance(T::typeid, params)); + entityScriptList->Add(script); + awakeList.Add(script); + startList.Add(script); + script->OnAttached(); + + return safe_cast(script); + } + + bool ScriptStore::AddScriptViaName(Entity entity, System::String^ scriptName) + { + SAFE_NATIVE_CALL_BEGIN + Script^ script; + return AddScriptViaNameWithRef(entity, scriptName, script); + SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + return false; + } + + bool ScriptStore::AddScriptViaNameWithRef(Entity entity, System::String^ scriptName, Script^% createdScript) + { + // Check if we are set up to get scripts + if (addScriptMethod == nullptr) + { + Debug::LogError("[ScriptStore] Native AddScript() was not loaded. Unable to add scripts."); + return false; + } + + // Get the script if it exists + System::Type^ scriptType = getScriptType(scriptName); + if (scriptType == nullptr) + { + std::ostringstream oss; + oss << "[ScriptStore] No Script named " + << Convert::ToNative(scriptName) + << " found!"; + Debug::LogError(oss.str()); + return false; + } + + // Otherwise, add the script + System::Reflection::MethodInfo^ method = addScriptMethod->MakeGenericMethod(scriptType); + try + { + array^ params = gcnew array{entity}; + createdScript = safe_cast(method->Invoke(nullptr, params)); + } + catch (System::Exception^ e) + { + std::ostringstream oss; + oss << "[ScriptStore] Failed to add Script named \"" << Convert::ToNative(scriptName) + << "\" to Entity #" << entity << "! (" << Convert::ToNative(e->GetType()->Name) << ")"; + Debug::LogError(oss.str()); + return false; + } + return true; + } + + generic + T ScriptStore::GetScript(Entity entity) + { + // Check if entity exists + if (!EntityUtils::IsValid(entity)) + throw gcnew System::ArgumentException("Invalid Entity provided to get a Script from."); + + // Check if entity exists in the script storage + if (!scripts.ContainsKey(entity)) + { + return T(); + } + + // Search for and obtain + for each (Script^ script in scripts[entity]) + { + try + { + T actualScript = safe_cast(script); + return actualScript; + } + catch (System::InvalidCastException^) + { + continue; + } + } + + return T(); + } + + generic + T ScriptStore::GetScriptInChildren(Entity entity) + { + // Check if entity exists and is a valid GameObject + if (!EntityUtils::IsValid(entity)) + throw gcnew System::ArgumentException("Invalid Entity provided to get a Script from."); + + + // Check if entity exists in the script storage + if (!scripts.ContainsKey(entity)) + { + return T(); + } + + // Get Transform component and get the children list + throw gcnew System::NotImplementedException; + //Pls::Transform* tf = Pls::ECS::GetComponent(Convert::ToNative(entity)); + //if (tf == nullptr) + // return T(); + + //// Search direct children first + //for (const auto& child : tf->GetChildren()) + //{ + // T script = GetScript(Convert::ToCLI(child)); + // if (script != nullptr) + // return script; + //} + + //// Search their children + //for (const auto& child : tf->GetChildren()) + //{ + // T script = GetScriptInChildren(Convert::ToCLI(child)); + // if (script != nullptr) + // return script; + //} + + // None here + return T(); + } + + generic + System::Collections::Generic::IEnumerable^ ScriptStore::GetScripts(Entity entity) + { + // Check if entity exists and is a valid GameObject + if (!EntityUtils::IsValid(entity)) + throw gcnew System::ArgumentException("Invalid Entity provided to get a Script from."); + + // Create a list to store entries + System::Collections::Generic::List^ foundScripts = gcnew System::Collections::Generic::List(); + + // Check if entity exists in the script storage + if (!scripts.ContainsKey(entity)) + { + return foundScripts; + } + + // Search for and obtain + for each (Script^ script in scripts[entity]) + { + try + { + T actualScript = safe_cast(script); + foundScripts->Add(actualScript); + } + catch (System::InvalidCastException^) + { + continue; + } + } + + return foundScripts; + } + System::Collections::Generic::IEnumerable^ ScriptStore::GetAllScripts(Entity entity) + { + // Check if entity exists + if (!EntityUtils::IsValid(entity)) + return nullptr; + + // Check if entity exists in the script storage + if (scripts.ContainsKey(entity)) + { + return scripts[entity]; + } + return nullptr; + } + generic + void ScriptStore::RemoveScript(Entity entity) + { + // Check if entity exists + if (!EntityUtils::IsValid(entity)) + throw gcnew System::ArgumentException("Invalid Entity provided to remove a Script from."); + + + // Check if entity exists in the script storage + if (!scripts.ContainsKey(entity)) + { + Debug::LogError("[ScriptStore] Attempted to remove a Script that does not belong to the specified Entity!"); + return; + } + + // Search for and obtain + for each (Script^ script in scripts[entity]) + { + try + { + safe_cast(script); + removeScript(script); + } + catch (System::InvalidCastException^) + { + continue; + } + } + } + bool ScriptStore::RemoveScript(Entity entity, Script^ script) + { + // Check if entity exists + if (!EntityUtils::IsValid(entity)) + { + Debug::LogError("[ScriptStore] Attempted to remove a Script from an invalid Entity!"); + return false; + } + + + // Check if entity exists in the script storage + if (!scripts.ContainsKey(entity)) + { + Debug::LogError("[ScriptStore] Attempted to remove a Script that does not belong to the specified Entity!"); + return false; + } + + // Check if the script exists to begin with + if (!scripts[entity]->Contains(script)) + { + Debug::LogError("[ScriptStore] Attempted to remove a Script that does not belong to the specified Entity!"); + return false; + } + + // Script found, queue it for deletion + removeScript(script); + return true; + } + void ScriptStore::RemoveAllScripts(Entity entity) + { + SAFE_NATIVE_CALL_BEGIN + // Check if entity exists + if (!EntityUtils::IsValid(entity)) + { + Debug::LogError("[ScriptStore] Attempted to remove Scripts from an invalid Entity!"); + return; + } + + // Check if entity exists in the script storage + if (!scripts.ContainsKey(entity)) + return; + + // Search for and clear + System::Collections::Generic::List^ scriptList = scripts[entity]; + for each (Script^ script in scriptList) + { + removeScript(script); + } + scriptList->Clear(); + SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + } + void ScriptStore::RemoveAllScriptsImmediately(Entity entity, bool callOnDestroy) + { + SAFE_NATIVE_CALL_BEGIN + // Check if entity exists + if (!EntityUtils::IsValid(entity)) + { + Debug::LogError("[ScriptStore] Attempted to remove Scripts from an invalid Entity!"); + return; + } + + // Check if entity exists in the script storage + if (!scripts.ContainsKey(entity)) + return; + + // Clear all + System::Collections::Generic::List^ scriptList = scripts[entity]; + for each (Script ^ script in scriptList) + { + // Call OnDestroy only if indicated and also in play mode + if (callOnDestroy) + { + script->OnDestroy(); + } + script->OnDetached(); + + // Remove scripts from awakening if they were not woken up to begin with + awakeList.Remove(script); + startList.Remove(script); + } + scriptList->Clear(); + SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + } + + /*---------------------------------------------------------------------------------*/ + /* Lifecycle Functions */ + /*---------------------------------------------------------------------------------*/ + void ScriptStore::Init() + { + // Create an enumerable list of script types + refreshScriptTypeList(); + // Get stored methods for interop variants of functions + getGenericMethods(); + } + void ScriptStore::FrameSetUp() + { + SAFE_NATIVE_CALL_BEGIN + // Clear the awake queue + for each (Script^ script in awakeList) + { + script->Awake(); + } + awakeList.Clear(); + + // Clear the start queue + for each (Script^ script in startList) + { + if (script->Owner.IsActiveInHierarchy) + { + script->Start(); + } + else + { + inactiveStartList.Add(script); + } + } + startList.Clear(); + startList.AddRange(%inactiveStartList); + inactiveStartList.Clear(); + + SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + } + void ScriptStore::FrameCleanUp() + { + SAFE_NATIVE_CALL_BEGIN + // Clear the queue + while (disposalQueue.Count > 0) + { + Script^ script = disposalQueue.Dequeue(); + /*if (Application::IsPlaying) + { + script->OnDestroy(); + }*/ + auto entity = script->Owner.GetEntity(); + auto scriptList = scripts[script->Owner.GetEntity()]; + scriptList->Remove(script); + if (scriptList->Count <= 0) + { + scripts.Remove(entity); + } + } + SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + } + void ScriptStore::Exit() + { + SAFE_NATIVE_CALL_BEGIN + // Run the deinit all scripts if needed + //if (Application::IsPlaying) + { + Debug::Log("Running OnDestroy() for scripts."); + for each (System::Collections::Generic::KeyValuePair entity in scripts) + { + for each (Script^ script in entity.Value) + { + script->OnDestroy(); + } + } + } + + // Clear Script Storage + scripts.Clear(); + awakeList.Clear(); + startList.Clear(); + disposalQueue.Clear(); + scriptTypeList = nullptr; + SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + } + + /*---------------------------------------------------------------------------------*/ + /* Script Information Functions */ + /*---------------------------------------------------------------------------------*/ + System::Collections::Generic::IEnumerable^ ScriptStore::GetAvailableScriptList() + { + return scriptTypeList; + } + + /*---------------------------------------------------------------------------------*/ + /* Script Execution Functions */ + /*---------------------------------------------------------------------------------*/ + void ScriptStore::ExecuteFixedUpdate() + { + SAFE_NATIVE_CALL_BEGIN + for each (System::Collections::Generic::KeyValuePair entity in scripts) + { + // Check active state + if (!isEntityActive(entity.Key)) + continue; + + // Update each script + for each (Script^ script in entity.Value) + { + script->FixedUpdate(); + } + } + SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + } + void ScriptStore::ExecuteUpdate() + { + SAFE_NATIVE_CALL_BEGIN + for each (System::Collections::Generic::KeyValuePair entity in scripts) + { + // Check active state + if (!isEntityActive(entity.Key)) + continue; + + // Update each script + for each (Script^ script in entity.Value) + { + script->Update(); + } + } + SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + } + void ScriptStore::ExecuteLateUpdate() + { + SAFE_NATIVE_CALL_BEGIN + for each (System::Collections::Generic::KeyValuePair entity in scripts) + { + // Check active state + if (!isEntityActive(entity.Key)) + continue; + + // Update each script + for each (Script^ script in entity.Value) + { + script->LateUpdate(); + } + } + SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + } + bool ScriptStore::SerialiseScripts(Entity entity, System::Text::StringBuilder^ buffer, int bufferSize) + { + SAFE_NATIVE_CALL_BEGIN + // Create a buffer that we can work with temporarily + System::Text::StringBuilder^ jsonString = gcnew System::Text::StringBuilder(); + + // Check if entity exists, otherwise nothing + if (!EntityUtils::IsValid(entity)) + return true; + + + // Check if entity exists in the script storage + if (!scripts.ContainsKey(entity)) + return true; + + // Serialise each script + System::Collections::Generic::List^ scriptList = scripts[entity]; + for (int i = 0; i < scriptList->Count; ++i) + { + throw gcnew System::NotImplementedException; + //jsonString->Append(ReflectionUtilities::Serialise(scriptList[i])); + + // Only add separator if is not last script + if (i != scriptList->Count - 1) + { + jsonString->Append(",\r\n"); + } + } + + // Check if the size is too big + if (jsonString->Length > bufferSize) + return false; + + // Otherwise we copy it over + buffer->Clear(); + buffer->Append(jsonString->ToString()); + return true; + SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + return false; + } + + bool ScriptStore::DeserialiseScript(Entity entity, System::String^ yaml) + { + SAFE_NATIVE_CALL_BEGIN + // Check if entity exists, otherwise nothing + if (!EntityUtils::IsValid(entity)) + return false; + + // Get the name of the script + const int FIRST_QUOTE = yaml->IndexOf('\"'); + const int FIRST_COLON = yaml->IndexOf(':'); + if (FIRST_QUOTE < 0 || FIRST_COLON < 0) // No script name, it's invalid + return false; + const int SCRIPT_NAME_START = FIRST_QUOTE + 1; + const int SCRIPT_NAME_END = FIRST_COLON - 1; + System::String^ typeName = yaml->Substring(SCRIPT_NAME_START, SCRIPT_NAME_END - SCRIPT_NAME_START); + + // Create the script + Script^ script; + if (AddScriptViaNameWithRef(entity, typeName, script)) + { + // Copy the data in + throw gcnew System::NotImplementedException; + //ReflectionUtilities::Deserialise(json, script); + return true; + } + + SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + return false; + } + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + void ScriptStore::removeScript(Script^ script) + { + // Prepare for disposal + disposalQueue.Enqueue(script); + + // Also remove it fromm awake and start queues if they were created but not initialised + awakeList.Remove(script); + startList.Remove(script); + script->OnDetached(); + } + + namespace + { + /* Select Many */ + ref struct Pair + { + System::Reflection::Assembly^ assembly; + System::Type^ type; + }; + + System::Collections::Generic::IEnumerable^ selectorFunc(System::Reflection::Assembly^ assembly) + { + return assembly->GetExportedTypes(); + } + Pair^ resultSelectorFunc(System::Reflection::Assembly^ assembly, System::Type^ type) + { + Pair^ p = gcnew Pair(); + p->assembly = assembly; + p->type = type; + return p; + } + + /* Where */ + bool predicateFunc(Pair^ pair) + { + return pair->type->IsSubclassOf(Script::typeid) && !pair->type->IsAbstract; + } + + /* Select */ + System::Type^ selectorFunc(Pair^ pair) + { + return pair->type; + } + } + + void ScriptStore::refreshScriptTypeList() + { + using namespace System; + using namespace System::Reflection; + using namespace System::Linq; + using namespace System::Collections::Generic; + + /* Select Many: Types in Loaded Assemblies */ + IEnumerable^ assemblies = AppDomain::CurrentDomain->GetAssemblies(); + Func^>^ collectionSelector = gcnew Func^>(selectorFunc); + Func^ resultSelector = gcnew Func(resultSelectorFunc); + IEnumerable^ selectManyResult = Enumerable::SelectMany(assemblies, collectionSelector, resultSelector); + + /* Where: Are concrete Scripts */ + Func^ predicate = gcnew Func(predicateFunc); + IEnumerable^ whereResult = Enumerable::Where(selectManyResult, predicate); + + /* Select: Select them all */ + Func^ selector = gcnew Func(selectorFunc); + scriptTypeList = Enumerable::Select(whereResult, selector); + + // Log + std::ostringstream oss; + oss << "[ScriptStore] Successfully retrieved references to " << Enumerable::Count(scriptTypeList) + << " Script(s) from currently loaded assemblies."; + Debug::Log(oss.str()); + } + + void ScriptStore::getGenericMethods() + { + addScriptMethod = ScriptStore::typeid->GetMethod("AddScript"); + if (addScriptMethod == nullptr) + { + Debug::LogError("[ScriptStore] Failed to get MethodInfo of \"AddScript()\". Adding of scripts from native code will fail."); + } + } + + System::Type^ ScriptStore::getScriptType(System::String^ scriptName) + { + // Remove any whitespaces just in case + scriptName = scriptName->Trim(); + + // Look for the correct script + for each (System::Type^ type in scriptTypeList) + { + if (type->FullName == scriptName || type->Name == scriptName) + { + return type; + } + } + + return nullptr; + } + + bool ScriptStore::isEntityActive(Entity entity) + { + // Get native Entity + SHEntity* nativeEntity = SHEntityManager::GetEntityByID(entity); + + // Entity Validity Check + if (nativeEntity == nullptr) + throw gcnew System::InvalidOperationException("Attempted to get native Component to an invalid Entity."); + + // Check active state + return nativeEntity->isActive; + } +} diff --git a/SHADE_Managed/src/Scripts/ScriptStore.hxx b/SHADE_Managed/src/Scripts/ScriptStore.hxx new file mode 100644 index 00000000..cc0c1db5 --- /dev/null +++ b/SHADE_Managed/src/Scripts/ScriptStore.hxx @@ -0,0 +1,301 @@ +/************************************************************************************//*! +\file ScriptStore.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the definitions of the GameObject managed class which define an + abstraction for working with Entities in managed code. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +// Project Includes +#include "Engine/Entity.hxx" +#include "Script.hxx" + +namespace SHADE +{ + /// + /// Responsible for managing all scripts attached to Entities as well as executing + /// all lifecycle functions of scripts. + /// + public ref class ScriptStore abstract sealed + { + public: + /*-----------------------------------------------------------------------------*/ + /* Scripts Manipulation Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Adds a Script to a specified Entity. + /// + /// + /// Type of script to add. + /// This needs to be a default constructable PlushieScript. + /// + /// The entity to add a script to. + /// Reference to the script added. + /// + /// If the specified Entity is invalid. + /// + generic where T : ref class, Script + static T AddScript(Entity entity); + /// + /// Adds a Script to a specified Entity. + ///
+ /// This function is meant for consumption from native code. If you are writing + /// in C# or C++/CLI, use AddScript<T>() instead as it is faster. + ///
+ /// The entity to add a script to. + /// The entity to add a script to. + /// + /// True if successfully added. False otherwise with the error logged to the + /// console. + /// + static bool AddScriptViaName(Entity entity, System::String^ scriptName); + /// + /// Adds a Script to a specified Entity. + ///
+ /// This function is meant for consumption from native code or for serialisation + /// purposes. If you are writing in C# or C++/CLI and not doing serialisation, + /// use AddScript<T>() instead as it is faster. + ///
+ /// The entity to add a script to. + /// The entity to add a script to. + /// + /// Out parameter handle to the Script that was created. + /// + /// + /// True if successfully added. False otherwise with the error logged to the + /// console. + /// + static bool AddScriptViaNameWithRef(Entity entity, System::String^ scriptName, [System::Runtime::InteropServices::Out] Script^% createdScript); + /// + /// Retrieves the first Script from the specified Entity that matches the + /// specified type. + /// + /// + /// Type of script to get. + /// This needs to be a default constructable Script. + /// + /// + /// The entity which the script to retrieve is attached. + /// + /// + /// Reference to the script. This can be null if no script of the specified + /// type is attached. + /// + /// + /// If the specified Entity is invalid. + /// + generic where T : ref class, Script + static T GetScript(Entity entity); + /// + /// Retrieves the first Script from the specified Entity's children that matches + /// the specified type. + /// + /// + /// Type of script to get. + /// This needs to be a default constructable Script. + /// + /// + /// The entity which the script to retrieve is attached. + /// + /// + /// Reference to the script. This can be null if no script of the specified + /// type is attached. + /// + /// + /// If the specified Entity is invalid. + /// + generic where T : ref class, Script + static T GetScriptInChildren(Entity entity); + /// + /// Retrieves a immutable list of scripts from the specified Entity that + /// matches the specified type. + ///
+ /// Note that this function allocates. It should be used sparingly. + ///
+ /// + /// Type of scripts to get. + /// This needs to be a default constructable Script. + /// + /// + /// The entity which the scripts to retrieve are attached. + /// + /// + /// Immutable list of references to scripts of the specified type. + /// + generic where T : ref class, Script + static System::Collections::Generic::IEnumerable ^ GetScripts(Entity entity); + /// + /// Retrieves an immutable list of all scripts attached to a specified Entity. + /// + /// + /// The entity which the scripts to retrieve are attached. + /// + /// + /// Immutable list of references to scripts attached to the specified Entity. + /// This can also be null if there are no scripts at all or an invalid Entity + /// was specified. + /// + static System::Collections::Generic::IEnumerable^ GetAllScripts(Entity entity); + /// + /// Removes all Scripts of the specified type from the specified Entity. + /// + /// + /// Type of script to remove. + /// This needs to be a default constructable Script. + /// + /// The entity to remove the script from. + /// + /// If the specified Entity is invalid. + /// + generic where T : ref class, Script + static void RemoveScript(Entity entity); + /// + /// Removes a specific script from the + /// + /// The entity to remove the script from. + /// The script to remove. + /// True if successfully removed. False otherwise. + static bool RemoveScript(Entity entity, Script^ script); + /// + /// Removes all Scripts attached to the specified Entity. Does not do anything + /// if the specified Entity is invalid or does not have any Scripts + /// attached. + /// + /// The entity to remove the scripts from. + static void RemoveAllScripts(Entity entity); + /// + /// Removes all Scripts attached to the specified Entity. Unlike + /// RemoveAllScripts(), this removes all the scripts immediately. + /// Does not do anything if the specified Entity is invalid or does not have any + /// Scripts attached. + /// + /// The entity to remove the scripts from. + /// + /// Whether or not to call OnDestroy on the scripts.This is ignored if not in + /// play mode. + /// + static void RemoveAllScriptsImmediately(Entity entity, bool callOnDestroy); + + internal: + /*-----------------------------------------------------------------------------*/ + /* Lifecycle Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Initializes the ScriptStore to allocate and pre-populate reflection data. + /// + static void Init(); + /// + /// Sets up scripts that were marked for initialization. This calls the Awake() + /// and Start() for Scripts that have yet to have done so. + /// + static void FrameSetUp(); + /// + /// Cleans up scripts that were marked for deletion. This calls the OnDestroy() + /// for these Scripts. + /// + static void FrameCleanUp(); + /// + /// Cleans up data stored in the ScriptStore to free up memory for garbage + /// collection. + /// + static void Exit(); + + /*-----------------------------------------------------------------------------*/ + /* Script Information Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Retrieves a immutable list of available scripts that can be added. + /// + /// Immutable list of available scripts that can be added. + static System::Collections::Generic::IEnumerable^ GetAvailableScriptList(); + + /*-----------------------------------------------------------------------------*/ + /* Script Execution Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Executes FixedUpdate() for all scripts. + /// + static void ExecuteFixedUpdate(); + /// + /// Executes Update() for all scripts. + /// + static void ExecuteUpdate(); + /// + /// Executes LateUpdate() for all scripts. + /// + static void ExecuteLateUpdate(); + + /*-----------------------------------------------------------------------------*/ + /* Serialisation Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Generates a JSON string that represents the set of Scripts attached + /// to the specified Entity. + ///

+ /// This function should only be called from native unmanaged code. + ///
+ /// The Entity to Serialise. + /// + /// StringBuilder handle that maps to a native char array that will contain the + /// serialised string. + /// + /// + /// The size of the char array. + /// + /// + /// True if serialisation is successful. False if the buffer is too small for + /// the serialised output. + /// + static bool SerialiseScripts(Entity entity, System::Text::StringBuilder^ buffer, int bufferSize); + /// + /// Processes a JSON string that represents a single Script and attaches + /// it onto the specified Entity. + ///

+ /// This function should only be called from native unmanaged code. + ///
+ /// + /// The Entity to attach the deserialised Scripts to. + /// + /// + /// JSON string that describes the Script to serialise. + /// + /// + static bool DeserialiseScript(Entity entity, System::String^ yaml); + + private: + /*-----------------------------------------------------------------------------*/ + /* Type Definition */ + /*-----------------------------------------------------------------------------*/ + using ScriptList = System::Collections::Generic::List; + using ScriptDictionary = System::Collections::Generic::Dictionary; + using ScriptQueue = System::Collections::Generic::Queue; + + /*-----------------------------------------------------------------------------*/ + /* Static Data Members */ + /*-----------------------------------------------------------------------------*/ + static ScriptDictionary scripts; + static ScriptList awakeList; + static ScriptList startList; + static ScriptList inactiveStartList; + static ScriptQueue disposalQueue; + static System::Collections::Generic::IEnumerable^ scriptTypeList; + static System::Reflection::MethodInfo^ addScriptMethod; + + /*-----------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------*/ + static void removeScript(Script^ script); + static void refreshScriptTypeList(); + static void getGenericMethods(); + static System::Type^ getScriptType(System::String^ scriptName); + static bool isEntityActive(Entity entity); + }; +} // namespace PlushieAPI \ No newline at end of file diff --git a/SHADE_Managed/src/Utility/Convert.cxx b/SHADE_Managed/src/Utility/Convert.cxx new file mode 100644 index 00000000..8a8aff70 --- /dev/null +++ b/SHADE_Managed/src/Utility/Convert.cxx @@ -0,0 +1,45 @@ +/************************************************************************************//*! +\file Convert.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the definition of the functions for the Convert managed static + class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "Convert.hxx" +// External Dependencies +#include "ECS_Base/System//SHEntityManager.h" +#include + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* ECS Conversions */ + /*---------------------------------------------------------------------------------*/ + Entity Convert::ToCLI(SHEntity entity) + { + return static_cast(entity.GetEID()); + } + + /*---------------------------------------------------------------------------------*/ + /* String Conversions */ + /*---------------------------------------------------------------------------------*/ + std::string Convert::ToNative(System::String^ str) + { + return msclr::interop::marshal_as(str); + } + + System::String^ Convert::ToCLI(const std::string& str) + { + return msclr::interop::marshal_as(str); + } +} // namespace PlushieAPI diff --git a/SHADE_Managed/src/Utility/Convert.hxx b/SHADE_Managed/src/Utility/Convert.hxx new file mode 100644 index 00000000..241e5863 --- /dev/null +++ b/SHADE_Managed/src/Utility/Convert.hxx @@ -0,0 +1,62 @@ +/************************************************************************************//*! +\file Convert.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the definition of the Convert static class and the + declaration of its functions. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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 + +// External Dependencies +#include "ECS_Base/Entity/SHEntity.h" +// Project Includes +#include "Engine/Entity.hxx" + +namespace SHADE +{ + /// + /// Provides functions easy and consistent syntax for converting between custom + /// managed and native types that are aligned. + /// + class Convert + { + public: + /*-----------------------------------------------------------------------------*/ + /* Deleted Destructors (Static Class) */ + /*-----------------------------------------------------------------------------*/ + Convert() = delete; + + /*-----------------------------------------------------------------------------*/ + /* ECS Conversions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Converts from a native Entity to a managed Entity (UInt32). + /// + /// Native Entity to convert from. + /// Managed representation of the specified Entity. + static Entity ToCLI(SHEntity entity); + + /*-----------------------------------------------------------------------------*/ + /* String Conversions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Converts from a managed String to a native std::string. + /// + /// The managed String to convert from. + /// Native copy of a managed String. + static std::string ToNative(System::String^ str); + /// + /// Converts from a native std::Stringto a managed String. + /// + /// The native std::string to convert from. + /// Managed copy of a native std::string. + static System::String^ ToCLI(const std::string& str); + }; +} diff --git a/SHADE_Managed/src/Utility/Debug.cxx b/SHADE_Managed/src/Utility/Debug.cxx new file mode 100644 index 00000000..330375b0 --- /dev/null +++ b/SHADE_Managed/src/Utility/Debug.cxx @@ -0,0 +1,122 @@ +/************************************************************************************//*! +\file Debug.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 28, 2021 +\brief Contains the definition of the functions for the Debug managed static + class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "Debug.hxx" +// Standard Libraries +#include +// Project Headers +#include "Convert.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Logging Functions */ + /*---------------------------------------------------------------------------------*/ + void Debug::Log(const std::string& str) + { + std::cout << str << std::endl; + } + void Debug::Log(System::String^ str) + { + System::Console::WriteLine(str); + } + + void Debug::Log(System::String^ str, Object^ owner) + { + Log(str, owner->GetType()->Name); + } + void Debug::Log(System::String^ str, System::String^ throwerName) + { + Log("[" + throwerName + "] " + str); + } + void Debug::Log(System::String^ str, const std::string& throwerName) + { + std::ostringstream oss; + oss << "[" << throwerName << "] " << Convert::ToNative(str); + std::cout << oss.str() << std::endl; + } + void Debug::LogWarning(const std::string& str) + { + std::cout << str << std::endl; + } + void Debug::LogWarning(System::String^ str) + { + System::Console::WriteLine(str); + } + void Debug::LogWarning(System::String^ str, Object^ thrower) + { + LogWarning(str, thrower->GetType()->Name); + } + void Debug::LogWarning(System::String^ str, System::String^ throwerName) + { + LogWarning("[" + throwerName + "] " + str); + } + + void Debug::LogWarning(System::String^ str, const std::string& throwerName) + { + std::ostringstream oss; + oss << "[" << throwerName << "] " << Convert::ToNative(str); + std::cout << oss.str() << std::endl; + } + void Debug::LogError(const std::string& str) + { + std::cout << str << std::endl; + } + void Debug::LogError(System::String^ str) + { + System::Console::WriteLine(str); + } + void Debug::LogError(System::String^ str, Object^ thrower) + { + LogError(str, thrower->GetType()->Name); + } + void Debug::LogErrorNative(System::String^ str, const std::string& throwerName) + { + std::ostringstream oss; + oss << "[" << throwerName << "] -> " << Convert::ToNative(str); + std::cout << oss.str() << std::endl; + } + void Debug::LogError(System::String^ str, System::String^ throwerName) + { + LogError("[" + throwerName + "] " + str); + } + void Debug::LogException(System::Exception^ exception) + { + LogError("Unhandled exception: " + exception->ToString(), exception->Source); + } + + void Debug::LogException(System::Exception^ exception, Object^ thrower) + { + LogError("Unhandled exception: " + exception->ToString(), thrower->GetType()->Name); + } + void Debug::LogException(const std::exception& exception, Object^ thrower) + { + LogExceptionNative(exception, Convert::ToNative(thrower->GetType()->Name)); + } + void Debug::LogExceptionNative(System::Exception^ exception, const std::string& throwerName) + { + std::ostringstream oss; + oss << "[" << throwerName << "] Unhandled exception: " << Convert::ToNative(exception->ToString()); + std::cout << oss.str() << std::endl; + } + void Debug::LogExceptionNative(const std::exception& exception, const std::string& throwerName) + { + std::ostringstream oss; + oss << "[" << throwerName << "] Unhandled exception: " << exception.what(); + std::cout << oss.str() << std::endl; + } +} diff --git a/SHADE_Managed/src/Utility/Debug.hxx b/SHADE_Managed/src/Utility/Debug.hxx new file mode 100644 index 00000000..28f2bc88 --- /dev/null +++ b/SHADE_Managed/src/Utility/Debug.hxx @@ -0,0 +1,255 @@ +/************************************************************************************//*! +\file Debug.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 30, 2021 +\brief Contains the definition of the Debug static class and the declaration of + its functions. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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 + +// Standard Library +#include +#include + +/*-------------------------------------------------------------------------------------*/ +/* Macro Functions */ +/*-------------------------------------------------------------------------------------*/ +/// +/// Macro expansion that is used together with SAFE_NATIVE_CALL_END or +/// SAFE_NATIVE_CALL_END_N to wrap the body of a function with a try and catch that +/// catches native and managed exceptions. This is needed to prevent crashes when calling +/// managed code from native code. +/// + +#define SAFE_NATIVE_CALL_BEGIN try { +/// +/// Macro expansion that is used together with SAFE_NATIVE_CALL_BEGIN or to wrap the body +/// of a function with a try and catch that catches native and managed exceptions. This +/// is needed to prevent crashes when calling managed code from native code. +///
+/// Use this instead of SAFE_NATIVE_CALL_END_N if passing in managed types as the owner. +///
+/// +/// The managed object that owns the function that this macro encapsulates. +/// +#define SAFE_NATIVE_CALL_END(OWNER) \ +} \ +catch (System::Exception^ e) \ +{ \ + Debug::LogException(e); \ +} \ +catch (const std::exception& e) \ +{ \ + Debug::LogException(e, OWNER); \ +} \ +catch (...) \ +{ \ + Debug::LogError("Unsupported native exception.", OWNER); \ +} \ +/// +/// Macro expansion that is used together with SAFE_NATIVE_CALL_BEGIN or to wrap the body +/// of a function with a try and catch that catches native and managed exceptions. This +/// is needed to prevent crashes when calling managed code from native code. +///
+/// Use this instead of SAFE_NATIVE_CALL_END if passing in a native string that specifies +/// the owner. +///
+/// +/// The managed object that owns the function that this macro encapsulates. +/// + +#define SAFE_NATIVE_CALL_END_N(OWNER) \ +} \ +catch (System::Exception^ e) \ +{ \ + Debug::LogExceptionNative(e, OWNER); \ +} \ +catch (const std::exception& e) \ +{ \ + Debug::LogExceptionNative(e, OWNER); \ +} \ +catch (...) \ +{ \ + Debug::LogErrorNative("Unsupported native exception.", OWNER); \ +} \ + +/*-------------------------------------------------------------------------------------*/ +/* Type Definitions */ +/*-------------------------------------------------------------------------------------*/ +namespace SHADE +{ + /// + /// Static class that contains the functions for working with time. + /// + public ref class Debug abstract sealed + { + public: + /*-----------------------------------------------------------------------------*/ + /* Logging Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Logs a message to the output. + /// + /// The string to output. + static void Log(const std::string& str); + /// + /// Logs a message to the output. + /// + /// The string to output. + static void Log(System::String^ str); + /// + /// Logs a message to the output with a label such that it looks like this: + /// "[Label] Message" + /// + /// The string to output. + /// + /// Object that sent the message to label the message. + /// The name of the object will be used. + /// + static void Log(System::String^ str, Object^ owner); + /// + /// Logs a message to the output with a label such that it looks like this: + /// "[Label] Message" + /// + /// The string to output. + /// + /// Name of the object that sent the message to label the message. + /// The name of the object will be used. + /// + static void Log(System::String^ str, System::String^ throwerName); + /// + /// Logs a message to the output with a label such that it looks like this: + /// "[Label] Message" + /// + /// The string to output. + /// + /// Name of the object that sent the message to label the message. + /// The name of the object will be used. + /// + static void Log(System::String^ str, const std::string& throwerName); + /// + /// Logs a warning message to the output. + /// + /// The string to output. + static void LogWarning(const std::string& str); + /// + /// Logs a warning message to the output. + /// + /// The string to output. + static void LogWarning(System::String^ str); + /// + /// Logs a warning message to the output with a label such that it looks like this: + /// "[Label] Message" + /// + /// The string to output. + /// + /// Object that threw the warning to label the warning message. + /// The name of the object will be used. + /// + static void LogWarning(System::String^ str, Object^ thrower); + /// + /// Logs a warning message to the output with a label such that it looks like this: + /// "[Label] Message" + /// + /// The string to output. + /// + /// Name of the object that threw the warning to label the warning message. + /// The name of the object will be used. + /// + static void LogWarning(System::String^ str, System::String^ throwerName); + /// + /// Logs a warning message to the output with a label such that it looks like this: + /// "[Label] Message" + /// + /// The string to output. + /// + /// Name of the object that threw the warning to label the warning message. + /// The name of the object will be used. + /// + static void LogWarning(System::String^ str, const std::string& throwerName); + /// + /// Logs a error message to the output. + /// + /// The string to output. + static void LogError(const std::string& str); + /// + /// Logs a error message to the output. + /// + /// The string to output. + static void LogError(System::String^ str); + /// + /// Logs a error message to the output with a label such that it looks like this: + /// "[Label] Message" + /// + /// The string to output. + /// + /// Object that threw the error to label the error message. + /// The name of the object will be used. + /// + static void LogError(System::String^ str, Object^ thrower); + /// + /// Logs a error message to the output with a label such that it looks like this: + /// "[Label] Message" + /// + /// The string to output. + /// + /// Name of the object that threw the error to label the error message. + /// The name of the object will be used. + /// + static void LogErrorNative(System::String^ str, const std::string& throwerName); + /// + /// Logs a error message to the output with a label such that it looks like this: + /// "[Label] Message" + /// + /// The string to output. + /// + /// Name of the object that threw the error to label the error message. + /// The name of the object will be used. + /// + static void LogError(System::String^ str, System::String^ throwerName); + /// + /// Logs an exception that is formatted nicely to the output. + /// + /// Exception to log. + static void LogException(System::Exception^ exception); + /// + /// Logs an exception that is formatted nicely to the output. + /// + /// Exception to log. + /// + /// Object that threw the exception to label the exception message. + /// The name of the object will be used. + /// + static void LogException(System::Exception^ exception, Object^ thrower); + /// + /// Logs a native exception that is formatted nicely to the output. + /// Equivalent to calling + /// LogException(exception, Convert::ToNative(thrower->GetType()->Name)); + /// + /// Native exception to log. + /// + /// Object that threw the exception to label the exception message. + /// The name of the object will be used. + /// + static void LogException(const std::exception& exception, Object^ thrower); + /// + /// Logs an exception that is formatted nicely to the output. + /// + /// Name of the one responsible for the exception. + /// Exception to log. + static void LogExceptionNative(System::Exception^ exception, const std::string& throwerName); + /// + /// Logs a native exception that is formatted nicely to the output. + /// + /// Native exception to log. + /// Name of the one responsible for the exception. + static void LogExceptionNative(const std::exception& exception, const std::string& throwerName); + }; +} diff --git a/SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.cxx b/SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.cxx new file mode 100644 index 00000000..ebf2e987 --- /dev/null +++ b/SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.cxx @@ -0,0 +1,36 @@ +/************************************************************************************//*! +\file DisposableAssemblyLoadContext.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Jan 20, 2022 +\brief Contains the implementation of the managed DisposableAssemblyLoadContext + class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "DisposableAssemblyLoadContext.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Constructor */ + /*---------------------------------------------------------------------------------*/ + DisposableAssemblyLoadContext::DisposableAssemblyLoadContext() + : AssemblyLoadContext { true } + {} + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + System::Reflection::Assembly^ DisposableAssemblyLoadContext::Load(System::Reflection::AssemblyName^) + { + return nullptr; + } +} // namespace PlushieAPI \ No newline at end of file diff --git a/SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.hxx b/SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.hxx new file mode 100644 index 00000000..433dd85e --- /dev/null +++ b/SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.hxx @@ -0,0 +1,39 @@ +/************************************************************************************//*! +\file DisposableAssemblyLoadContext.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Jan 20, 2022 +\brief Contains the definitions of the managed DisposableAssemblyLoadContext + class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 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 + +namespace SHADE +{ + /// + /// Custom AssemblyLoadContext marked as collectible so that it can be unloaded. + /// + private ref class DisposableAssemblyLoadContext : public System::Runtime::Loader::AssemblyLoadContext + { + public: + /*-----------------------------------------------------------------------------*/ + /* Constructor */ + /*-----------------------------------------------------------------------------*/ + /// + /// Default Constructor + /// + DisposableAssemblyLoadContext(); + + protected: + /*-----------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------*/ + System::Reflection::Assembly^ Load(System::Reflection::AssemblyName^ assemblyName) override; + }; +} // namespace PlushieAPI \ No newline at end of file diff --git a/premake5.lua b/premake5.lua index 45ed41fa..450be7d7 100644 --- a/premake5.lua +++ b/premake5.lua @@ -12,7 +12,7 @@ workspace "SHADE" flags { - "MultiProcessorCompile" + "MultiProcessorCompile" } outputdir = "%{wks.location}/bin/%{cfg.buildcfg}" @@ -20,6 +20,7 @@ workspace "SHADE" include "SHADE_Engine" include "SHADE_Application" + include "SHADE_Managed" group "Dependencies" include "Dependencies/msdf"