Merge remote-tracking branch 'origin/main' into SP3-1-Rendering

This commit is contained in:
Brandon Mak 2023-01-16 15:33:37 +08:00
commit 09911cc50c
66 changed files with 2660 additions and 620 deletions

4
.gitignore vendored
View File

@ -364,3 +364,7 @@ MigrationBackup/
*.filters
Assets/Editor/Layouts/UserLayout.ini
JSON/Schemas/Catalog/
Assets/Editor/Editor.SHConfig

View File

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

1
Assets/Bindings.SHConfig Normal file
View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1,4 @@
Start Maximized: true
Working Scene ID: 97161771
Window Size: {x: 1920, y: 1080}
Style: 0

View File

@ -1,7 +1,7 @@
- EID: 0
Name: Canvas
IsActive: true
NumberOfChildren: 1
NumberOfChildren: 2
Components:
Canvas Component:
Canvas Width: 10
@ -28,6 +28,26 @@
Clicked Texture: 0
IsActive: true
Scripts: ~
- EID: 5
Name: Default
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 0, y: -3.9000001, z: 0}
Rotate: {x: 0, y: 0, z: 0}
Scale: {x: 1, y: 1, z: 1}
IsActive: true
Renderable Component:
Mesh: 141771688
Material: 129340704
IsActive: true
Toggle Button Component:
Non Toggled Texture: 0
Toggled Texture: 0
Value: true
IsActive: true
Scripts: ~
- EID: 1
Name: Camera
IsActive: true

View File

@ -1,3 +1,3 @@
Name: UI_Test
ID: 87707373
ID: 87244611
Type: 5

View File

@ -20,10 +20,11 @@ echo "M - SDL"
echo "N - dotnet"
echo "O - tinyddsloader"
echo "P - fmod"
echo "Q - vswhere"
echo ---------------------------------------------------
echo.
choice /C ABCDEFGHIJKLMNOP /T 10 /D A
choice /C ABCDEFGHIJKLMNOPQ /T 10 /D A
set _e=%ERRORLEVEL%
if %_e%==1 goto VMA
@ -42,6 +43,7 @@ if %_e%==13 goto SDL
if %_e%==14 goto dotnet
if %_e%==15 goto tinyddsloader
if %_e%==16 goto fmod
if %_e%==17 goto vswhere
:VMA
echo -----------------------VMA----------------------------
@ -155,6 +157,13 @@ if %_e%==15 (goto :done) else (goto :fmod)
echo --------------------fmod-------------------------
rmdir "Dependencies/fmod" /S /Q
git clone https://github.com/SHADE-DP/FMOD.git "Dependencies/fmod"
if %_e%==16 (goto :done) else (goto :vswhere)
:vswhere
echo -----------------------vswhere----------------------------
rmdir "Dependencies/vswhere" /S /Q
mkdir "Dependencies/vswhere"
powershell -Command "& {wget https://github.com/microsoft/vswhere/releases/download/3.1.1/vswhere.exe -OutFile "Dependencies/vswhere/vswhere.exe"}"
:done
echo DONE!

View File

@ -17,3 +17,4 @@ IncludeDir["VULKAN"] = "$(VULKAN_SDK)"
IncludeDir["dotnet"] = "%{wks.location}\\Dependencies\\dotnet"
IncludeDir["tinyddsloader"] = "%{wks.location}\\Dependencies\\tinyddsloader"
IncludeDir["fmod"] = "%{wks.location}\\Dependencies\\fmod"
IncludeDir["vswhere"] = "%{wks.location}\\Dependencies\\vswhere"

View File

@ -67,6 +67,9 @@ namespace Sandbox
SHFileUtilities::SetWorkDirToExecDir();
WindowData wndData{};
auto& appConfig = SHConfigurationManager::LoadApplicationConfig(&wndData);
#if SHEDITOR
auto& editorConfig = SHConfigurationManager::LoadEditorConfig(&wndData);
#endif
window.Create(hInstance, hPrevInstance, lpCmdLine, nCmdShow, wndData);
// Create Systems
@ -158,8 +161,11 @@ namespace Sandbox
SHSystemManager::Init();
#if SHEDITOR
SHSceneManager::InitSceneManager<SBMainScene>(editorConfig.workingSceneID);
#else
SHSceneManager::InitSceneManager<SBMainScene>(appConfig.startingSceneID);
#endif
SHFrameRateController::UpdateFRC();
// Link up SHDebugDraw

View File

@ -124,7 +124,8 @@ project "SHADE_Engine"
"xcopy /r /y /q \"%{IncludeDir.ModelCompiler}\\bin\\Debug\\ModelCompiler.exe\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.FontCompiler}\\bin\\Debug\\FontCompiler.exe\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodL.dll\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodstudioL.dll\" \"$(OutDir)\""
"xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodstudioL.dll\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.vswhere}\\vswhere.exe\" \"$(OutDir)\""
}
filter "configurations:Release"
@ -134,7 +135,8 @@ project "SHADE_Engine"
"xcopy /r /y /q \"%{IncludeDir.ModelCompiler}\\bin\\Release\\ModelCompiler.exe\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.FontCompiler}\\bin\\Release\\FontCompiler.exe\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmod.dll\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodstudio.dll\" \"$(OutDir)\""
"xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodstudio.dll\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.vswhere}\\vswhere.exe\" \"$(OutDir)\""
}
filter "configurations:Publish"

View File

@ -23,7 +23,7 @@
#include "SH_API.h"
#include "Events/SHEventManager.hpp"
#include <tuple>
#include <cassert>
namespace SHADE
@ -151,6 +151,32 @@ namespace SHADE
return (componentSet.GetSparseSet<T>()->GetElement_s(EntityHandleGenerator::GetIndex(entityID)));
}
/*!*************************************************************************
* \brief
* Gets the Component of the entity with the specified entityID
*
* This is the safe version of GetComponent_s which does a HasComponent to make
* sure that the entity has such a component and returns nullptr if it doesn't
*
* This safe version also checks if the sparse set of this component type
* has been created in SHComponentManager and creates one if it doesn't
*
* @tparam T...
* Pack of Types for all the Components to get.
* \param entityID
* EntityID of the entity that we are trying to get the component of.
* \return
* A tuple of pointers to all the components specified.
* Returns nullptr if the entity does not contain such a component.
***************************************************************************/
template<typename ...T>
static std::enable_if_t<(... && std::is_base_of_v<SHComponent, T>), std::tuple<T*...>> GetComponents(EntityID entityID) noexcept
{
return std::make_tuple<T*...>(GetComponent_s<T>(entityID)...);
}
/*!*************************************************************************
* \brief
* Gets the Component of the entity with the specified entityID

View File

@ -18,11 +18,17 @@ namespace SHADE
SHCommandManager::CommandStackPtr SHCommandManager::pCurrUndoStack(&undoStack);
SHCommandManager::CommandStackPtr SHCommandManager::pCurrRedoStack(&redoStack);
bool SHCommandManager::failedExecution(false);
void SHCommandManager::PerformCommand(BaseCommandPtr commandPtr, bool const& overrideValue)
{
*pCurrRedoStack = CommandStack(defaultStackSize);
commandPtr->Execute();
if(failedExecution)
{
failedExecution = false;
return;
}
*pCurrRedoStack = CommandStack(defaultStackSize);
if (overrideValue && !pCurrUndoStack->Empty())
{
pCurrUndoStack->Top()->Merge(commandPtr);
@ -68,11 +74,13 @@ namespace SHADE
void SHCommandManager::PopLatestCommandFromRedoStack()
{
if(!pCurrRedoStack->Empty())
pCurrRedoStack->Pop();
}
void SHCommandManager::PopLatestCommandFromUndoStack()
{
if(!pCurrUndoStack->Empty())
pCurrUndoStack->Pop();
}

View File

@ -39,6 +39,7 @@ namespace SHADE
static void SwapStacks();
static void ClearAll();
static bool failedExecution;
static constexpr CommandStack::SizeType defaultStackSize = 100;
private:
static CommandStackPtr pCurrUndoStack;

View File

@ -14,6 +14,8 @@
#include "Editor/DragDrop/SHDragDrop.hpp"
#include "Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h"
#include "Editor/EditorWindow/SHEditorWindowManager.h"
#include "Scripting/SHVSUtilities.h"
#include "Scripting/SHScriptEngine.h"
namespace SHADE
{
@ -249,6 +251,12 @@ namespace SHADE
matInspector->OpenMaterial(asset->id);
}
break;
case AssetType::SCRIPT:
if(auto scriptEngine = SHSystemManager::GetSystem<SHScriptEngine>())
{
scriptEngine->OpenFile(asset->path);
}
break;
case AssetType::MAX_COUNT: break;
default:;
}

View File

@ -84,8 +84,7 @@ namespace SHADE
editor->selectedEntities.clear();
}
ImGui::SeparatorEx(ImGuiSeparatorFlags_Horizontal);
if (ImGui::IsWindowFocused())
{
if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_A))
{
SelectAllEntities();
@ -110,7 +109,6 @@ namespace SHADE
{
DeleteSelectedEntities();
}
}
}
if(ImGui::IsWindowHovered() && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left))

View File

@ -19,6 +19,9 @@
#include "Reflection/SHReflectionMetadata.h"
#include "Resource/SHResourceManager.h"
#include "Physics/Collision/SHCollisionTagMatrix.h"
#include "Serialization/SHSerializationHelper.hpp"
#include "Tools/Utilities/SHClipboardUtilities.h"
#include "SHInspectorCommands.h"
namespace SHADE
{
template<typename T>
@ -46,11 +49,12 @@ namespace SHADE
if (ImGui::Selectable(std::format("{} Copy {}", ICON_MD_CONTENT_COPY, componentName.data()).data()))
{
//SHClipboardUtil::WriteStringToClipboard(SHClipboardUtil::CFNAME::CFCOMPONENT, SHComponentToString(component));
SHClipboardUtilities::WriteToClipboard(SHSerializationHelper::SerializeComponentToString<T>(component->GetEID()));
}
if (ImGui::Selectable(std::format("{} Paste {}", ICON_MD_CONTENT_PASTE, componentName.data()).data()))
{
//SHStringToComponent(component, SHClipboardUtil::ReadStringFromClipboard(SHClipboardUtil::CFNAME::CFCOMPONENT));
//SHSerializationHelper::DeserializeComponentFromString<T>(SHClipboardUtilities::GetDataFromClipboard(), component->GetEID());
SHCommandManager::PerformCommand(std::reinterpret_pointer_cast<SHBaseCommand>( std::make_shared<SHPasteComponentCommand<T>>(component->GetEID(), SHClipboardUtilities::GetDataFromClipboard())));
}
if (ImGui::Selectable(std::format("{} Delete {}", ICON_MD_DELETE, componentName.data()).data()))
{

View File

@ -21,6 +21,7 @@
#include "UI/SHUIComponent.h"
#include "UI/SHCanvasComponent.h"
#include "UI/SHButtonComponent.h"
#include "UI/SHToggleButtonComponent.h"
#include "SHEditorComponentView.h"
#include "AudioSystem/SHAudioListenerComponent.h"
#include "Graphics/MiddleEnd/TextRendering/SHTextRenderableComponent.h"
@ -154,6 +155,10 @@ namespace SHADE
{
DrawComponent(buttonComponent);
}
if (auto toggleButton = SHComponentManager::GetComponent_s<SHToggleButtonComponent>(eid))
{
DrawComponent(toggleButton);
}
ImGui::Separator();
// Render Scripts
SHScriptEngine* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>());
@ -167,6 +172,7 @@ namespace SHADE
DrawAddComponentButton<SHLightComponent>(eid);
DrawAddComponentButton<SHCanvasComponent>(eid);
DrawAddComponentButton<SHButtonComponent>(eid);
DrawAddComponentButton<SHToggleButtonComponent>(eid);
// Components that require Transforms

View File

@ -0,0 +1,27 @@
#pragma once
#include "Editor/Command/SHCommand.hpp"
namespace SHADE
{
template<typename T>
class SHPasteComponentCommand final : SHBaseCommand
{
public:
struct Data
{
EntityID eid;
std::string oldComponentData;
std::string newComponentData;
};
SHPasteComponentCommand(EntityID eid, std::string const& newComponentData);
void Execute() override;
void Undo() override;
private:
Data data;
};
}
#include "SHInspectorCommands.hpp"

View File

@ -0,0 +1,26 @@
#pragma once
#include "SHInspectorCommands.h"
namespace SHADE
{
template<typename T>
SHPasteComponentCommand<T>::SHPasteComponentCommand(EntityID eid, std::string const& newComponentData)
:data(eid, {}, newComponentData)
{
data.oldComponentData = SHSerializationHelper::SerializeComponentToString<T>(eid);
}
template<typename T>
void SHPasteComponentCommand<T>::Execute()
{
bool result = SHSerializationHelper::DeserializeComponentFromString<T>(data.newComponentData, data.eid);
if(!result)
SHCommandManager::failedExecution = true;
}
template<typename T>
void SHPasteComponentCommand<T>::Undo()
{
SHSerializationHelper::DeserializeComponentFromString<T>(data.oldComponentData, data.eid);
}
}

View File

@ -81,126 +81,18 @@ namespace SHADE
if (ImGui::BeginMainMenuBar())
{
if (ImGui::BeginMenu("File"))
{
if(ImGui::Selectable("New Scene"))
{
SHSystemManager::GetSystem<SHEditor>()->NewScene();
}
if(ImGui::Selectable("Save"))
{
SHSystemManager::GetSystem<SHEditor>()->SaveScene();
}
if(ImGui::Selectable("Load"))
{
//SHSystemManager::GetSystem<SHEditor>()->LoadScene()
}
ImGui::EndMenu();
}
if(ImGui::BeginMenu("Edit"))
{
ImGui::BeginDisabled(!SHCommandManager::GetUndoStackSize());
if(ImGui::Button(std::format("{} Undo", ICON_MD_UNDO).data()))
{
SHCommandManager::UndoCommand();
}
ImGui::EndDisabled();
ImGui::BeginDisabled(!SHCommandManager::GetRedoStackSize());
if(ImGui::Button(std::format("{} Redo", ICON_MD_REDO).data()))
{
SHCommandManager::RedoCommand();
}
ImGui::EndDisabled();
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Scripts"))
{
if (ImGui::Selectable("Generate Visual Studio Project"))
{
auto* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>());
scriptEngine->GenerateScriptsCsProjFile();
}
ImGui::BeginDisabled(SHSystemManager::GetSystem<SHEditor>()->editorState != SHEditor::State::STOP);
if (ImGui::Selectable("Build Scripts - Debug"))
{
auto* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>());
SHSerialization::SerializeSceneToFile(SHSceneManager::GetCurrentSceneAssetID());
scriptEngine->BuildScriptAssembly(true, true);
SHSceneManager::RestartScene(SHSceneManager::GetCurrentSceneAssetID());
}
if (ImGui::Selectable("Build Scripts - Release"))
{
auto* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>());
SHSerialization::SerializeSceneToFile(SHSceneManager::GetCurrentSceneAssetID());
scriptEngine->BuildScriptAssembly(false, true);
SHSceneManager::RestartScene(SHSceneManager::GetCurrentSceneAssetID());
}
ImGui::EndDisabled();
ImGui::EndMenu();
}
DrawFileMenu();
DrawEditMenu();
DrawScriptsMenu();
DrawWindowMenu();
DrawThemeMenu();
DrawLayoutMenu();
DrawApplicationConfig();
if (ImGui::BeginMenu("Window"))
{
for (const auto& window : SHEditorWindowManager::editorWindows | std::views::values)
{
if (window.get() != this)
ImGui::Checkbox(window->windowName.data(), &window->isOpen);
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Theme"))
{
const auto styles = rttr::type::get<SHEditor::Style>().get_enumeration();
auto values = styles.get_values();
for (auto style : values)
{
if (ImGui::Selectable(style.to_string().c_str()))
{
if (auto editor = SHSystemManager::GetSystem<SHEditor>())
editor->SetStyle(style.convert<SHEditor::Style>());
}
}
ImGui::EndMenu();
}
if(ImGui::BeginMenu("Layout"))
{
for(auto const& entry : layoutPaths)
{
if(ImGui::Selectable(entry.stem().string().c_str()))
{
ImGui::LoadIniSettingsFromDisk(entry.string().c_str());
}
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Application Config"))
{
auto& appConfig = SHConfigurationManager::applicationConfig;
ImGui::InputText("Window Title", &appConfig.windowTitle);
ImGui::Checkbox("Start in Fullscreen", &appConfig.startInFullScreen);
SHEditorWidgets::DragN<float, 2>("Window Size", { "Width", "Height" }, { &appConfig.windowSize.x, &appConfig.windowSize.y });
//ImGui::InputScalar("Starting Scene", ImGuiDataType_U32, &appConfig.startingSceneID);
auto sceneAsset = SHAssetManager::GetData<SHSceneAsset>(appConfig.startingSceneID);
if(ImGui::BeginCombo("Starting Scne", sceneAsset ? sceneAsset->name.data() : ""))
{
auto scenes = SHAssetManager::GetAllRecordOfType(AssetType::SCENE);
for(auto const& scene : scenes)
{
if(ImGui::Selectable(scene.name.data()))
{
appConfig.startingSceneID = scene.id;
}
}
ImGui::EndCombo();
}
if (ImGui::Button("Save"))
{
SHConfigurationManager::SaveApplicationConfig();
}
ImGui::EndMenu();
}
std::string const sceneName{std::format("Current Scene: {}",SHSceneManager::GetSceneName().data())};
auto const size = ImGui::CalcTextSize(sceneName.data());
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - size.x - ImGui::GetStyle().FramePadding.x);
ImGui::Text("%s", sceneName.data());
ImGui::EndMainMenuBar();
}
@ -267,4 +159,148 @@ namespace SHADE
ImGui::PopStyleVar(3);
}
void SHEditorMenuBar::DrawFileMenu() noexcept
{
if (ImGui::BeginMenu("File"))
{
if (ImGui::Selectable("New Scene"))
{
SHSystemManager::GetSystem<SHEditor>()->NewScene();
}
if (ImGui::Selectable("Save"))
{
SHSystemManager::GetSystem<SHEditor>()->SaveScene();
}
if (ImGui::Selectable("Load"))
{
//SHSystemManager::GetSystem<SHEditor>()->LoadScene()
}
ImGui::EndMenu();
}
}
void SHEditorMenuBar::DrawEditMenu() noexcept
{
if (ImGui::BeginMenu("Edit"))
{
ImGui::BeginDisabled(!SHCommandManager::GetUndoStackSize());
if (ImGui::Button(std::format("{} Undo", ICON_MD_UNDO).data()))
{
SHCommandManager::UndoCommand();
}
ImGui::EndDisabled();
ImGui::BeginDisabled(!SHCommandManager::GetRedoStackSize());
if (ImGui::Button(std::format("{} Redo", ICON_MD_REDO).data()))
{
SHCommandManager::RedoCommand();
}
ImGui::EndDisabled();
ImGui::EndMenu();
}
}
void SHEditorMenuBar::DrawScriptsMenu() noexcept
{
if (ImGui::BeginMenu("Scripts"))
{
if (ImGui::Selectable("Generate Visual Studio Project"))
{
auto* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>());
scriptEngine->GenerateScriptsCsProjFile();
}
if (ImGui::Selectable("Open Visual Studio Project"))
{
auto* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>());
scriptEngine->OpenSolution();
}
ImGui::BeginDisabled(SHSystemManager::GetSystem<SHEditor>()->editorState != SHEditor::State::STOP);
if (ImGui::Selectable("Build Scripts - Debug"))
{
auto* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>());
SHSerialization::SerializeSceneToFile(SHSceneManager::GetCurrentSceneAssetID());
scriptEngine->BuildScriptAssembly(true, true);
SHSceneManager::RestartScene(SHSceneManager::GetCurrentSceneAssetID());
}
if (ImGui::Selectable("Build Scripts - Release"))
{
auto* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>());
SHSerialization::SerializeSceneToFile(SHSceneManager::GetCurrentSceneAssetID());
scriptEngine->BuildScriptAssembly(false, true);
SHSceneManager::RestartScene(SHSceneManager::GetCurrentSceneAssetID());
}
ImGui::EndDisabled();
ImGui::EndMenu();
}
}
void SHEditorMenuBar::DrawWindowMenu() noexcept
{
if (ImGui::BeginMenu("Window"))
{
for (const auto& window : SHEditorWindowManager::editorWindows | std::views::values)
{
if (window.get() != this)
ImGui::Checkbox(window->windowName.data(), &window->isOpen);
}
ImGui::EndMenu();
}
}
void SHEditorMenuBar::DrawThemeMenu() noexcept
{
if (ImGui::BeginMenu("Theme"))
{
const auto styles = rttr::type::get<SHEditor::Style>().get_enumeration();
auto values = styles.get_values();
for (auto style : values)
{
if (ImGui::Selectable(style.to_string().c_str()))
{
if (auto editor = SHSystemManager::GetSystem<SHEditor>())
editor->SetStyle(style.convert<SHEditor::Style>());
}
}
ImGui::EndMenu();
}
}
void SHEditorMenuBar::DrawLayoutMenu() noexcept
{
if (ImGui::BeginMenu("Layout"))
{
for (auto const& entry : layoutPaths)
{
if (ImGui::Selectable(entry.stem().string().c_str()))
{
ImGui::LoadIniSettingsFromDisk(entry.string().c_str());
}
}
ImGui::EndMenu();
}
}
void SHEditorMenuBar::DrawApplicationConfig() noexcept
{
if (ImGui::BeginMenu("Application Config"))
{
auto& appConfig = SHConfigurationManager::applicationConfig;
ImGui::InputText("Window Title", &appConfig.windowTitle);
ImGui::Checkbox("Start in Fullscreen", &appConfig.startInFullScreen);
SHEditorWidgets::DragN<float, 2>("Window Size", { "Width", "Height" }, { &appConfig.windowSize.x, &appConfig.windowSize.y });
//ImGui::InputScalar("Starting Scene", ImGuiDataType_U32, &appConfig.startingSceneID);
auto sceneAsset = SHAssetManager::GetData<SHSceneAsset>(appConfig.startingSceneID);
if (ImGui::BeginCombo("Starting Scene", sceneAsset ? sceneAsset->name.data() : ""))
{
auto scenes = SHAssetManager::GetAllRecordOfType(AssetType::SCENE);
for (auto const& scene : scenes)
{
if (ImGui::Selectable(scene.name.data()))
{
appConfig.startingSceneID = scene.id;
}
}
ImGui::EndCombo();
}
if (ImGui::Button("Save"))
{
SHConfigurationManager::SaveApplicationConfig();
}
ImGui::EndMenu();
}
}
}//namespace SHADE

View File

@ -17,6 +17,15 @@ namespace SHADE
void DrawMainMenuBar() noexcept;
void DrawSecondaryBar() const noexcept;
void DrawStatusBar() const noexcept;
void DrawFileMenu() noexcept;
void DrawEditMenu() noexcept;
void DrawScriptsMenu() noexcept;
void DrawWindowMenu() noexcept;
void DrawThemeMenu() noexcept;
void DrawLayoutMenu() noexcept;
void DrawApplicationConfig() noexcept;
float menuBarHeight = 20.0f;
std::vector<std::filesystem::path> layoutPaths;
};//class SHEditorMenuBar

View File

@ -94,6 +94,8 @@ namespace SHADE
}
}
editorConfig = &SHConfigurationManager::LoadEditorConfig();
//Add editor windows
SHEditorWindowManager::CreateEditorWindow<SHEditorMenuBar>();
SHEditorWindowManager::CreateEditorWindow<SHHierarchyPanel>();
@ -122,7 +124,7 @@ namespace SHADE
InitBackend();
SetStyle(Style::SHADE);
SetStyle(static_cast<Style>(editorConfig->style));
for (const auto& window : SHEditorWindowManager::editorWindows | std::views::values)
@ -160,26 +162,7 @@ namespace SHADE
RenderUnsavedChangesPrompt();
//PollPicking();
if(ImGui::IsKeyDown(ImGuiKey_LeftShift) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_Z))
{
SHCommandManager::RedoCommand();
}
else if(ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_Z))
{
SHCommandManager::UndoCommand();
}
if(ImGui::IsKeyReleased(ImGuiKey_F5))
{
Play();
}
else if (ImGui::IsKeyReleased(ImGuiKey_F6))
{
Pause();
}
else if (ImGui::IsKeyReleased(ImGuiKey_F7))
{
Stop();
}
ProcessShortcuts();
Render();
}
@ -376,10 +359,14 @@ namespace SHADE
ImGui_ImplVulkan_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
editorConfig->startMaximized = shWindow->GetWindowData().isMaximised;
SHConfigurationManager::SaveEditorConfig();
}
void SHEditor::SetStyle(Style style)
{
editorConfig->style = static_cast<uint32_t>(style);
switch (style)
{
default:
@ -583,6 +570,7 @@ namespace SHADE
SHSceneManager::SetCurrentSceneName(newSceneName);
SHSceneManager::SetCurrentSceneAssetID(SHAssetManager::CreateNewAsset(AssetType::SCENE, newSceneName));
editorConfig->workingSceneID = SHSceneManager::GetCurrentSceneAssetID();
}
//Get data, if data is null, asset doesn't exist, prompt for a name and create a new asset with the name
@ -591,7 +579,7 @@ namespace SHADE
{
if(shWindow->IsUnsavedChanges())
shWindow->ToggleUnsavedChanges();
editorConfig->workingSceneID = SHSceneManager::GetCurrentSceneAssetID();
return true;
}
return false;
@ -659,6 +647,37 @@ namespace SHADE
LoadScene(SHSceneManager::GetCurrentSceneAssetID());
}
void SHEditor::ProcessShortcuts()
{
if(ImGui::IsKeyDown(ImGuiKey_LeftCtrl))
{
if(ImGui::IsKeyReleased(ImGuiKey_S))
{
SaveScene();
}
}
if (ImGui::IsKeyDown(ImGuiKey_LeftShift) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_Z))
{
SHCommandManager::RedoCommand();
}
else if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_Z))
{
SHCommandManager::UndoCommand();
}
if (ImGui::IsKeyReleased(ImGuiKey_F5))
{
Play();
}
else if (ImGui::IsKeyReleased(ImGuiKey_F6))
{
Pause();
}
else if (ImGui::IsKeyReleased(ImGuiKey_F7))
{
Stop();
}
}
void SHEditor::NewFrame()
{
SDL_Event event;
@ -672,6 +691,18 @@ namespace SHADE
ImGuizmo::BeginFrame();
}
void SHEditor::SetSHWindow(SHWindow* inWindow)
{
shWindow = inWindow;
shWindow->RegisterWindowSizeCallback([&](uint32_t width, uint32_t height)
{
if(width > 0 && height > 0)
{
auto [width, height] = shWindow->GetWindowSize();
editorConfig->windowSize = { static_cast<float>(width), static_cast<float>(height) };
}
});
}
void SHEditor::EditorRoutine::Execute(double dt) noexcept
{

View File

@ -20,6 +20,7 @@
#include "Events/SHEventDefines.h"
#include "Events/SHEvent.h"
#include "Graphics/Windowing/SHWindow.h"
#include "Serialization/Configurations/SHConfigurationManager.h"
//#==============================================================#
//|| Library Includes ||
@ -108,7 +109,7 @@ namespace SHADE
void InitBackend();
void SetSDLWindow(SDL_Window* inSDLWindow){sdlWindow = inSDLWindow;};
void SetSHWindow(SHWindow* inWindow){shWindow = inWindow;}
void SetSHWindow(SHWindow* inWindow);
void PollPicking();
@ -122,11 +123,15 @@ namespace SHADE
void Pause();
void Stop();
void ProcessShortcuts();
// List of selected entities
std::vector<EntityID> selectedEntities;
State editorState = State::STOP;
SHEditorConfig* editorConfig;
private:
/**
* @brief Start new frame for editor

View File

@ -160,7 +160,7 @@ namespace SHADE
template <typename T, std::size_t N>
static bool DragN(const std::string& label, std::vector<std::string>const& componentLabels,
std::vector<T*> values, float speed = 0.1f, const char* displayFormat = "", T valueMin = T(), T valueMax = T(),
std::vector<T*> values, float speed = 0.1f, const char* displayFormat = "%.3f", T valueMin = T(), T valueMax = T(),
ImGuiSliderFlags flags = 0, bool* isHovered = nullptr)
{
const ImGuiWindow* const window = ImGui::GetCurrentWindow();

View File

@ -23,4 +23,5 @@ constexpr SHEventIdentifier SH_SCENE_INIT_POST { 14 };
constexpr SHEventIdentifier SH_SCENE_EXIT_PRE { 15 };
constexpr SHEventIdentifier SH_SCENE_EXIT_POST { 16 };
constexpr SHEventIdentifier SH_GRAPHICS_LIGHT_ENABLE_SHADOW_EVENT { 17 };
constexpr SHEventIdentifier SH_BUTTON_CLICK_EVENT { 18 };

View File

@ -104,6 +104,10 @@ namespace SHADE
auto subPass = renderGraph->GetNode(SHGraphicsConstants::RenderGraphEntityNames::DEBUG_DRAW.data())->GetSubpass("Debug Draw");
subPass->AddExteriorDrawCalls([this](Handle<SHVkCommandBuffer> cmdBuffer, Handle<SHRenderer> renderer, uint32_t frameIndex)
{
// Set line width first
cmdBuffer->SetLineWidth(LineWidth);
// Draw
const uint32_t FRAME_IDX = gfxSystem->GetCurrentFrameIndex();
cmdBuffer->BeginLabeledSegment("SHDebugDraw (No Depth Test)");
{
@ -128,6 +132,10 @@ namespace SHADE
auto subPassWithDepth = renderGraph->GetNode(SHGraphicsConstants::RenderGraphEntityNames::DEBUG_DRAW_DEPTH_PASS.data())->GetSubpass("Debug Draw with Depth");
subPassWithDepth->AddExteriorDrawCalls([this](Handle<SHVkCommandBuffer> cmdBuffer, Handle<SHRenderer> renderer, uint32_t frameIndex)
{
// Set line width first
cmdBuffer->SetLineWidth(LineWidth);
// Draw
const uint32_t FRAME_IDX = gfxSystem->GetCurrentFrameIndex();
cmdBuffer->BeginLabeledSegment("SHDebugDraw (Depth Tested)");
{
@ -207,6 +215,11 @@ namespace SHADE
drawSphere(getMeshBatch(true, depthTested), matrix, color);
}
void SHDebugDrawSystem::DrawWireCapsule(const SHVec3& position, const SHQuaternion& rotation, float height, float radius, const SHColour& color, bool depthTested)
{
drawWireCapsule(getLineBatch(depthTested), getMeshBatch(false, depthTested), position, rotation, height, radius, color);
}
/*-----------------------------------------------------------------------------------*/
/* Persistent Draw Functions */
/*-----------------------------------------------------------------------------------*/
@ -264,6 +277,12 @@ namespace SHADE
markPersistentDrawsDirty();
}
void SHDebugDrawSystem::DrawPersistentWireCapsule(const SHVec3& position, const SHQuaternion& rotation, float height, float radius, const SHColour& color, bool depthTested)
{
drawWireCapsule(getPersistentLineBatch(depthTested), getPersistentMeshBatch(false, depthTested), position, rotation, height, radius, color);
markPersistentDrawsDirty();
}
void SHDebugDrawSystem::ClearPersistentDraws()
{
for (auto& batch : persistentLineBatches)
@ -348,6 +367,53 @@ namespace SHADE
);
}
void SHDebugDrawSystem::drawWireCapsule(LinesBatch& lineBatch, MeshBatch& meshBatch, const SHVec3& position, const SHQuaternion& rotation, float height, float radius, const SHColour& color)
{
// Get local axis vectors
const SHVec3 LOCAL_UP = SHVec3::Rotate(SHVec3::Up, rotation);
const SHVec3 LOCAL_RIGHT = SHVec3::Rotate(SHVec3::Right, rotation);
const SHVec3 LOCAL_FORWARD = SHVec3::Rotate(SHVec3::Forward, rotation);
// Rotate the circle
SHQuaternion circleOrientation = SHQuaternion::FromEuler(SHVec3(SHMath::DegreesToRadians(90.0), 0.0f, 0.0f)) * rotation;
// Compute top and bottom of the cylinder
const SHVec3 HALF_UP = LOCAL_UP * (height * 0.5f - radius);
const SHVec3 TOP_POS = position + HALF_UP;
const SHVec3 BOT_POS = position - HALF_UP;
// Render circles
const SHVec3 CIRCLE_SCALE = SHVec3(radius * 2.0f, radius * 2.0, radius * 2.0);
drawCircle(meshBatch, SHMatrix::Transform(TOP_POS, circleOrientation, CIRCLE_SCALE), color);
drawCircle(meshBatch, SHMatrix::Transform(BOT_POS, circleOrientation, CIRCLE_SCALE), color);
// Render connecting lines
drawLine(lineBatch, TOP_POS + LOCAL_RIGHT * radius, BOT_POS + LOCAL_RIGHT * radius, color);
drawLine(lineBatch, TOP_POS - LOCAL_RIGHT * radius, BOT_POS - LOCAL_RIGHT * radius, color);
drawLine(lineBatch, TOP_POS + LOCAL_FORWARD * radius, BOT_POS + LOCAL_FORWARD * radius, color);
drawLine(lineBatch, TOP_POS - LOCAL_FORWARD * radius, BOT_POS - LOCAL_FORWARD * radius, color);
// Render caps
const SHVec3 RADIUS_SCALE = SHVec3(radius * 2.0, radius * 2.0f, radius * 2.0);
const SHMatrix TOP_CAP_MAT = SHMatrix::Transform(TOP_POS, rotation, RADIUS_SCALE);
drawMesh
(
gfxSystem->GetMeshPrimitive(PrimitiveType::LineCapsuleCap),
meshBatch, TOP_CAP_MAT, color
);
const SHMatrix BOT_CAP_MAT = SHMatrix::Transform
(
BOT_POS,
SHQuaternion::FromEuler(SHVec3(SHMath::DegreesToRadians(180.0), 0.0f, 0.0f)) * rotation,
RADIUS_SCALE
);
drawMesh
(
gfxSystem->GetMeshPrimitive(PrimitiveType::LineCapsuleCap),
meshBatch, BOT_CAP_MAT, color
);
}
/*-----------------------------------------------------------------------------------*/
/* Helper Batch Functions - Lines */
/*-----------------------------------------------------------------------------------*/
@ -448,7 +514,6 @@ namespace SHADE
if (batch.NumPoints[frameIndex] > 0)
{
cmdBuffer->BindPipeline(batch.Pipeline);
cmdBuffer->SetLineWidth(LineWidth);
cmdBuffer->BindVertexBuffer(0, batch.VertexBuffers[frameIndex], 0);
cmdBuffer->DrawArrays(batch.NumPoints[frameIndex], 1, 0, 0);
}

View File

@ -163,6 +163,17 @@ namespace SHADE
/// <param name="color">Colour to draw with.</param>
/// <param name="depthTested">Whether or not drawn object will be occluded.</param>
void DrawSphere(const SHMatrix& matrix, const SHColour& color = SHColour::WHITE, bool depthTested = false);
/// <summary>
/// Draws the outline of a capsule.
/// </summary>
/// <param name="position">Position of the wireframe capsule.</param>
/// <param name="rotation">Rotation of the capsule.</param>
/// <param name="height">Height of the overall capsule.</param>
/// <param name="radius">Radius of the capsule.</param>
/// <param name="color"></param>
/// <param name="color">Colour to draw with.</param>
/// <param name="depthTested">Whether or not drawn object will be occluded.</param>
void DrawWireCapsule(const SHVec3& position, const SHQuaternion& rotation, float height, float radius, const SHColour& color = SHColour::WHITE, bool depthTested = false);
/*---------------------------------------------------------------------------------*/
/* Persistent Draw Functions */
@ -269,6 +280,17 @@ namespace SHADE
/// <param name="depthTested">Whether or not drawn object will be occluded.</param>
void DrawPersistentSphere(const SHMatrix& matrix, const SHColour& color = SHColour::WHITE, bool depthTested = false);
/// <summary>
/// Draws a persistent outline of a capsule.
/// </summary>
/// <param name="position">Position of the wireframe capsule.</param>
/// <param name="rotation">Rotation of the capsule.</param>
/// <param name="height">Height of the overall capsule.</param>
/// <param name="radius">Radius of the capsule.</param>
/// <param name="color"></param>
/// <param name="color">Colour to draw with.</param>
/// <param name="depthTested">Whether or not drawn object will be occluded.</param>
void DrawPersistentWireCapsule(const SHVec3& position, const SHQuaternion& rotation, float height, float radius, const SHColour& color = SHColour::WHITE, bool depthTested = false);
/// <summary>
/// Clears any persistent drawn debug primitives.
/// </summary>
void ClearPersistentDraws();
@ -386,6 +408,7 @@ namespace SHADE
void drawCube(MeshBatch& batch, const SHMatrix& transformMatrix, const SHColour& color);
void drawSphere(MeshBatch& batch, const SHMatrix& transformMatrix, const SHColour& color);
void drawCircle(MeshBatch& batch, const SHMatrix& transformMatrix, const SHColour& color);
void drawWireCapsule(LinesBatch& lineBatch, MeshBatch& meshBatch, const SHVec3& position, const SHQuaternion& rotation, float height, float radius, const SHColour& color);
/*---------------------------------------------------------------------------------*/
/* Helper Batch Functions - Lines */

View File

@ -440,6 +440,7 @@ namespace SHADE
primitiveMeshes[static_cast<int>(PrimitiveType::Sphere)] = SHPrimitiveGenerator::Sphere(meshLibrary);
primitiveMeshes[static_cast<int>(PrimitiveType::LineCube)] = SHPrimitiveGenerator::LineCube(meshLibrary);
primitiveMeshes[static_cast<int>(PrimitiveType::LineCircle)] = SHPrimitiveGenerator::LineCircle(meshLibrary);
primitiveMeshes[static_cast<int>(PrimitiveType::LineCapsuleCap)] = SHPrimitiveGenerator::LineCapsuleCap(meshLibrary);
BuildMeshBuffers();
// Create default materials
@ -895,6 +896,7 @@ namespace SHADE
case PrimitiveType::Sphere:
case PrimitiveType::LineCube:
case PrimitiveType::LineCircle:
case PrimitiveType::LineCapsuleCap:
return primitiveMeshes[static_cast<int>(type)];
default:
return {};

View File

@ -72,9 +72,10 @@ namespace SHADE
Cube,
Sphere,
LineCube,
LineCircle
LineCircle,
LineCapsuleCap
};
static constexpr int MAX_PRIMITIVE_TYPES = 4;
static constexpr int MAX_PRIMITIVE_TYPES = 5;
enum class DebugDrawPipelineType
{
LineNoDepthTest,

View File

@ -29,6 +29,7 @@ namespace SHADE
SHMeshData SHPrimitiveGenerator::sphereMesh;
SHMeshData SHPrimitiveGenerator::lineCubeMesh;
SHMeshData SHPrimitiveGenerator::lineCircleMesh;
SHMeshData SHPrimitiveGenerator::lineCapsuleCapMesh;
/*-----------------------------------------------------------------------------------*/
/* Primitive Generation Functions */
@ -392,6 +393,64 @@ namespace SHADE
return addMeshDataTo(lineCircleMesh, gfxSystem);
}
SHADE::SHMeshData SHPrimitiveGenerator::LineCapsuleCap() noexcept
{
SHMeshData mesh;
// Have multiple semi-circles for the cap
static constexpr int SPLITS = 36;
static constexpr float ANGLE_INCREMENTS = (std::numbers::pi_v<float> * 2.0f) / static_cast<float>(SPLITS);
/* X-Axis */
// Generate points of the circle
for (int i = 0; i <= SPLITS / 2; ++i)
{
const float ANGLE = ANGLE_INCREMENTS * i;
mesh.VertexPositions.emplace_back(cos(ANGLE) * 0.5f, sin(ANGLE) * 0.5f, 0.0f);
}
// Generate lines of the circle
for (int i = 1; i <= SPLITS / 2; ++i)
{
mesh.Indices.emplace_back(static_cast<uint32_t>(i - 1));
mesh.Indices.emplace_back(static_cast<uint32_t>(i));
}
/* Z-Axis */
// Generate points of the circle
for (int i = 0; i <= SPLITS / 2; ++i)
{
const float ANGLE = ANGLE_INCREMENTS * i;
mesh.VertexPositions.emplace_back(0.0f, sin(ANGLE) * 0.5f, cos(ANGLE) * 0.5f);
}
// Generate lines of the circle
for (int i = 2 + SPLITS / 2; i <= SPLITS + 1; ++i)
{
mesh.Indices.emplace_back(static_cast<uint32_t>(i - 1));
mesh.Indices.emplace_back(static_cast<uint32_t>(i));
}
mesh.VertexNormals.resize(mesh.VertexPositions.size());
mesh.VertexTangents.resize(mesh.VertexPositions.size());
mesh.VertexTexCoords.resize(mesh.VertexPositions.size());
return mesh;
}
Handle<SHADE::SHMesh> SHPrimitiveGenerator::LineCapsuleCap(SHMeshLibrary& meshLibrary) noexcept
{
if (lineCapsuleCapMesh.VertexPositions.empty())
lineCapsuleCapMesh = LineCapsuleCap();
return addMeshDataTo(lineCapsuleCapMesh, meshLibrary);
}
Handle<SHADE::SHMesh> SHPrimitiveGenerator::LineCapsuleCap(SHGraphicsSystem& gfxSystem) noexcept
{
if (lineCapsuleCapMesh.VertexPositions.empty())
lineCapsuleCapMesh = LineCapsuleCap();
return addMeshDataTo(lineCapsuleCapMesh, gfxSystem);
}
/*-----------------------------------------------------------------------------------*/
/* Helper Functions */
/*-----------------------------------------------------------------------------------*/

View File

@ -194,6 +194,46 @@ namespace SHADE
*/
/***********************************************************************************/
[[nodiscard]] static Handle<SHMesh> LineCircle(SHGraphicsSystem& gfxSystem) noexcept;
/***********************************************************************************/
/*!
\brief
Produces a cap of a wireframe capsule that is comprised only of lines and
store the data in a SHMeshData object.
\return
SHMeshData object containing vertex data for the line circle.
*/
/***********************************************************************************/
[[nodiscard]] static SHMeshData LineCapsuleCap() noexcept;
/***********************************************************************************/
/*!
\brief
Produces a cap of a wireframe capsule that is comprised only of lines and
constructs a SHMesh using the SHGraphicsSystem provided.
\param meshLibrary
Reference to the SHMeshLibrary to produce and store a line circle mesh in.
\return
SHMesh object that points to the generated line circle mesh in the SHMeshLibrary.
*/
/***********************************************************************************/
[[nodiscard]] static Handle<SHMesh> LineCapsuleCap(SHMeshLibrary& meshLibrary) noexcept;
/***********************************************************************************/
/*!
\brief
Produces a cap of a wireframe capsule that is comprised only of lines and
constructs a SHMesh using the SHGraphicsSystem provided.
\param gfxSystem
Reference to the SHGraphicsSystem to produce and store a line circle mesh in.
\return
SHMesh object that points to the generated line circle mesh in the
SHGraphicsSystem.
*/
/***********************************************************************************/
[[nodiscard]] static Handle<SHMesh> LineCapsuleCap(SHGraphicsSystem& gfxSystem) noexcept;
private:
/*---------------------------------------------------------------------------------*/
@ -209,5 +249,6 @@ namespace SHADE
static SHMeshData sphereMesh;
static SHMeshData lineCubeMesh;
static SHMeshData lineCircleMesh;
static SHMeshData lineCapsuleCapMesh;
};
}

View File

@ -115,6 +115,11 @@ namespace SHADE
SetFocus(wndHWND);
}
if(!wndData.isFullscreen && wndData.isMaximised)
{
Maximize();
}
if (!wndData.icoPath.empty())
{
//HICON hIcon = ::LoadIcon(nullptr, MAKEINTRESOURCE(wndData.icoPath.c_str()));
@ -208,10 +213,12 @@ namespace SHADE
if (!IsZoomed(wndHWND))
{
ShowWindow(wndHWND, SW_MAXIMIZE);
wndData.isMaximised = true;
}
else
{
ShowWindow(wndHWND, SW_RESTORE);
wndData.isMaximised = false;
}
}
@ -428,7 +435,9 @@ namespace SHADE
wndData.isMinimised = true;
}
else
{
wndData.isMinimised = false;
}
for (auto const& entry : windowResizeCallbacks)
{

View File

@ -64,6 +64,8 @@ namespace SHADE
bool isMinimised = false;
bool isMaximised = true;
std::wstring title = L"SHADE ENGINE";
std::wstring name = L"SHADEEngineApp";

View File

@ -11,6 +11,7 @@
#pragma once
#include <SHpch.h>
#include <fstream>
#include "SHInputManager.h"
#include "../Tools/SHException.h"
@ -99,18 +100,173 @@ namespace SHADE
}
}
//The Binding File format presently goes as such:
/*
* Binding count
* (For each binding:)
* Name
Binding Type Enum
Inverted Bool
Gravity Double
Dead Double
Sensitivity Double
Snap Bool
PositiveKeyCode count
PositiveKeyCodes
NegativeKeyCode count
NegativeKeyCodes
PositiveControllerCode Count
PositiveControllerCodes
NegativeControllerCode Count
NegativeControllerCodes
*/
void SHInputManager::SaveBindings(std::string const& targetFile) noexcept
{
std::ofstream file;
file.open(targetFile);
//File cannot be written to
if (!file) return;
//First write the number of bindings
file << bindings.size() << std::endl;
for (auto& b : bindings)
{
//Name
file << b.first << std::endl;
//Data
auto& lbd = b.second;
file << static_cast<int>(lbd.bindingType) << std::endl;
file << static_cast<int>(lbd.inverted) << std::endl;
file << lbd.gravity << std::endl;
file << lbd.dead << std::endl;
file << lbd.sensitivity << std::endl;
file << static_cast<int>(lbd.snap) << std::endl;
//Bindings
file << lbd.positiveKeyCodes.size() << std::endl;
for (auto kc : lbd.positiveKeyCodes)
file << static_cast<int>(kc) << std::endl;
file << lbd.negativeKeyCodes.size() << std::endl;
for (auto kc : lbd.negativeKeyCodes)
file << static_cast<int>(kc) << std::endl;
file << lbd.positiveControllerCodes.size() << std::endl;
for (auto cc : lbd.positiveControllerCodes)
file << static_cast<int>(cc) << std::endl;
file << lbd.negativeControllerCodes.size() << std::endl;
for (auto cc : lbd.negativeControllerCodes)
file << static_cast<int>(cc) << std::endl;
}
file.close();
}
void SHInputManager::LoadBindings(std::string const& sourceFile) noexcept
{
std::ifstream file;
file.open(sourceFile);
//Check
if (!file) return;
//Erase
ClearBindings();
//Read
std::string read;
int count = 0;
std::getline(file, read);
count = std::stoi(read);
std::string bindingName;
for (int b = 0; b < count; ++b)
{
//Name
std::getline(file, read);
bindingName = read;
AddBinding(bindingName);
//Type
std::getline(file, read);
SetBindingType(bindingName, static_cast<SH_BINDINGTYPE>(std::stoi(read)));
//Inversion
std::getline(file, read);
SetBindingInverted(bindingName, static_cast<bool>(std::stoi(read)));
//Gravity
std::getline(file, read);
SetBindingGravity(bindingName, std::stod(read));
//Dead
std::getline(file, read);
SetBindingDead(bindingName, std::stod(read));
//Sensitivity
std::getline(file, read);
SetBindingSensitivity(bindingName, std::stod(read));
//Snap
std::getline(file, read);
SetBindingSnap(bindingName, static_cast<bool>(std::stoi(read)));
int count = 0;
//Positive Key Codes
std::getline(file, read);
count = std::stoi(read);
for (int i = 0; i < count; ++i)
{
std::getline(file, read);
AddBindingPositiveKeyCode(bindingName, static_cast<SH_KEYCODE>(std::stoi(read)));
}
//Negative Key Codes
std::getline(file, read);
count = std::stoi(read);
for (int i = 0; i < count; ++i)
{
std::getline(file, read);
AddBindingNegativeKeyCode(bindingName, static_cast<SH_KEYCODE>(std::stoi(read)));
}
//Positive Controller Codes
std::getline(file, read);
count = std::stoi(read);
for (int i = 0; i < count; ++i)
{
std::getline(file, read);
AddBindingPositiveControllerCode(bindingName, static_cast<SH_CONTROLLERCODE>(std::stoi(read)));
}
//Negative Controller Codes
std::getline(file, read);
count = std::stoi(read);
for (int i = 0; i < count; ++i)
{
std::getline(file, read);
AddBindingNegativeControllerCode(bindingName, static_cast<SH_CONTROLLERCODE>(std::stoi(read)));
}
}
file.close();
}
void SHInputManager::UpdateInput(double dt) noexcept
{
//Keyboard and Mouse Buttons////////////////////////////////////////////////
//Write to lastKeys
memcpy(keysLast, keys, sizeof(keys));
//Poll
//Poll KB/M
unsigned char keyboardState[MAX_KEYS] = {};
SecureZeroMemory(keyboardState, sizeof(keyboardState));
//if (GetKeyboardState(keyboardState) == false) return;
bool keyboardStateGot = GetKeyboardState(keyboardState);
SHASSERT(keyboardStateGot, "SHInputManager:GetKeyboardState() failed ({})", GetLastError());
SHASSERT(keyboardStateGot, "SHInputManager::GetKeyboardState() failed ({})", GetLastError());
keyCount = 0;
keyToggleCount = 0;
for (size_t i = 0; i < MAX_KEYS; ++i)
@ -159,7 +315,7 @@ namespace SHADE
}
}
//Mouse Positioning/////////////////////////////////////
//Mouse Positioning/////////////////////////////////////////////////////////
//https://stackoverflow.com/a/6423739
//Set last positioning
@ -181,7 +337,7 @@ namespace SHADE
mouseWheelVerticalDelta = mouseWheelVerticalDeltaPoll;
mouseWheelVerticalDeltaPoll = 0;
//Controllers//////////////////////////////////////////////////////////////
//Controllers///////////////////////////////////////////////////////////////
controllersConnectedCount = 0;
@ -447,6 +603,200 @@ namespace SHADE
}
}
}
//Bindings//////////////////////////////////////////////////////////////////
for (auto& binding : bindings)
{
SHLogicalBindingData& data = binding.second;
//Set last value
data.positiveInputHeldLast = data.positiveInputHeld;
data.negativeInputHeldLast = data.negativeInputHeld;
//Reset held values
data.positiveInputHeld = false;
data.negativeInputHeld = false;
if (data.bindingType == SHInputManager::SH_BINDINGTYPE::MOUSE_X)
{
double velX = 0.0;
GetMouseVelocity(&velX, nullptr);
if (data.value > 0.0) data.positiveInputHeld = true;
else if (data.value < 0.0) data.negativeInputHeld = true;
data.value = velX * data.sensitivity * (data.inverted ? -1.0 : 1.0);
}
else if (data.bindingType == SHInputManager::SH_BINDINGTYPE::MOUSE_Y)
{
double velY = 0.0;
GetMouseVelocity(nullptr, &velY);
if (data.value > 0.0) data.positiveInputHeld = true;
else if (data.value < 0.0) data.negativeInputHeld = true;
data.value = velY * data.sensitivity * (data.inverted ? -1.0 : 1.0);
}
else if (data.bindingType == SHInputManager::SH_BINDINGTYPE::MOUSE_SCROLL)
{
if (mouseWheelVerticalDelta > 0.0) data.positiveInputHeld = true;
else if (mouseWheelVerticalDelta < 0.0) data.negativeInputHeld = true;
data.value = mouseWheelVerticalDelta * data.sensitivity * (data.inverted ? -1.0 : 1.0);
}
else if (data.bindingType == SHInputManager::SH_BINDINGTYPE::KB_MB_CONTROLLER)
{
//Prioritise the largest magnitude
double largestMagnitude = 0.0;
//If digital input was in, use sensitivity
bool digitalInput = false;
//Over keycodes
for (SH_KEYCODE k : data.positiveKeyCodes)
{
if (GetKey(k))
{
if (std::abs(1.0) > std::abs(largestMagnitude)) largestMagnitude = 1.0 * (data.inverted ? -1.0 : 1.0);
digitalInput = true;
data.positiveInputHeld = true;
}
}
for (SH_KEYCODE k : data.negativeKeyCodes)
{
if (GetKey(k))
{
if (std::abs(-1.0) > std::abs(largestMagnitude)) largestMagnitude = -1.0 * (data.inverted ? -1.0 : 1.0);
digitalInput = true;
data.negativeInputHeld = true;
}
}
//Over controllerCodes
for (SH_CONTROLLERCODE c : data.positiveControllerCodes)
{
double newValue = 0.0;
GetControllerInput(c, &newValue);
if (std::abs(newValue) > std::abs(data.dead))
{
data.positiveInputHeld = true;
if (static_cast<size_t>(c) < NUM_CONTROLLER_BUTTON)
{
digitalInput = true;
}
if (std::abs(newValue) > std::abs(largestMagnitude))
largestMagnitude = newValue * data.sensitivity * (data.inverted ? -1.0 : 1.0);
}
}
for (SH_CONTROLLERCODE c : data.negativeControllerCodes)
{
double newValue = 0.0;
GetControllerInput(c, &newValue);
if (std::abs(newValue) > std::abs(data.dead))
{
data.negativeInputHeld = true;
if (static_cast<size_t>(c) < NUM_CONTROLLER_BUTTON)
{
digitalInput = true;
}
if (std::abs(newValue) > std::abs(largestMagnitude))
largestMagnitude = -newValue * data.sensitivity * (data.inverted ? -1.0 : 1.0);
}
}
//If both positive and negative inputs read, do not modify value
if (data.positiveInputHeld && data.negativeInputHeld)
{
data.value = data.value;
}
else
{
//If no data received, use gravity
if (!data.positiveInputHeld && !data.negativeInputHeld)
{
if (data.value > 0.0)
{
data.value -= data.gravity * dt;
if (data.value < 0.0) data.value = 0.0;
}
if (data.value < 0.0)
{
data.value += data.gravity * dt;
if (data.value > 0.0) data.value = 0.0;
}
}
else //Either positive OR negative input was read
{
//If digital input was in,
//sensitivity is used as a rate at which the axis value is changed
if (digitalInput)
{
//If the input is opposite the current value, gravity works
//with the sensitivity
if (data.value > 0.0 && largestMagnitude < 0.0)
data.value -= data.gravity * dt;
if (data.value < 0.0 && largestMagnitude > 0.0)
data.value += data.gravity * dt;
//Moved by sensitivity
data.value += data.sensitivity * largestMagnitude * dt;
if (data.value > 1.0) data.value = 1.0;
else if (data.value < -1.0) data.value = -1.0;
}
//If analog input was in,
//sensitivity is instead used as a multiplier
else
{
data.value = largestMagnitude * data.sensitivity;
if (data.value > 1.0) data.value = 1.0;
else if (data.value < -1.0) data.value = -1.0;
}
if (data.snap) //Snapping
{
if (data.value > 0.0 && data.negativeInputHeld)
data.value = 0.0;
if (data.value < 0.0 && data.positiveInputHeld)
data.value = 0.0;
}
}
}
}
//Binding timings
//Positivie bindings
if (data.positiveInputHeld) //Input is down
{
if (!data.positiveInputHeldLast) //Input was just pressed
{
data.positiveHeldTime = 0.0; //Reset timer
}
data.positiveHeldTime += dt; //Tick up
}
else //Input is up
{
if (data.positiveInputHeldLast) //Input was just released
{
data.positiveReleasedTime = 0.0; //Reset timer
}
data.positiveReleasedTime += dt; //Tick up
}
if (data.negativeInputHeld) //Input is down
{
if (!data.negativeInputHeldLast) //Input was just pressed
{
data.negativeHeldTime = 0.0; //Reset timer
}
data.negativeHeldTime += dt; //Tick up
}
else //Input is up
{
if (data.negativeInputHeldLast) //Input was just released
{
data.negativeReleasedTime = 0.0; //Reset timer
}
data.negativeReleasedTime += dt; //Tick up
}
}
}
bool SHInputManager::AnyKeyDown(SH_KEYCODE* firstDetected) noexcept
@ -574,40 +924,34 @@ namespace SHADE
return false;
}
//Only get of largest magnitude
double SHInputManager::GetBindingAxis(std::string bindingName, size_t cNum) noexcept
double SHInputManager::GetBindingAxis(std::string const& bindingName, size_t cNum) noexcept
{
//Over keycodes, prioritise positive
for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes)
{
if (GetKey(k)) return 1.0;
}
for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes)
{
if (GetKey(k)) return -1.0;
return bindings[bindingName].value;
}
double largestMagnitude = 0.0;
//Over controllerCodes
for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes)
double SHInputManager::GetBindingAxisRaw(std::string const& bindingName, size_t cNum) noexcept
{
double newValue = 0.0;
if (GetControllerInput(c, &newValue, nullptr, nullptr, cNum))
if (std::abs(newValue) > std::abs(largestMagnitude)) largestMagnitude = newValue;
//Neutral if both positive and negative held
if (bindings[bindingName].positiveInputHeld && bindings[bindingName].negativeInputHeld)
{
return 0.0;
}
for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes)
else if (bindings[bindingName].positiveInputHeld)
{
double newValue = 0.0;
if (GetControllerInput(c, &newValue, nullptr, nullptr, cNum))
if (std::abs(newValue) > std::abs(largestMagnitude)) largestMagnitude = -newValue;
return 1.0;
}
else if (bindings[bindingName].negativeInputHeld)
{
return -1.0;
}
return largestMagnitude;
//No input means neutral
return 0.0;
}
bool SHInputManager::GetBindingPositiveButton(std::string bindingName, size_t cNum) noexcept
bool SHInputManager::GetBindingPositiveButton(std::string const& bindingName, size_t cNum) noexcept
{
/*
if (cNum >= XUSER_MAX_COUNT) return false;
//Over keycodes
@ -622,11 +966,13 @@ namespace SHADE
if (GetControllerInput(c, nullptr, nullptr, nullptr, cNum)) return true;
}
return false;
return false;*/
return bindings[bindingName].positiveInputHeld;
}
bool SHInputManager::GetBindingNegativeButton(std::string bindingName, size_t cNum) noexcept
bool SHInputManager::GetBindingNegativeButton(std::string const& bindingName, size_t cNum) noexcept
{
/*
if (cNum >= XUSER_MAX_COUNT) return false;
//Over keycodes
@ -641,11 +987,13 @@ namespace SHADE
if (GetControllerInput(c, nullptr, nullptr, nullptr, cNum)) return true;
}
return false;
return false;*/
return bindings[bindingName].negativeInputHeld;
}
bool SHInputManager::GetBindingPositiveButtonDown(std::string bindingName, size_t cNum) noexcept
bool SHInputManager::GetBindingPositiveButtonDown(std::string const& bindingName, size_t cNum) noexcept
{
/*
if (cNum >= XUSER_MAX_COUNT) return false;
//Over keycodes
@ -660,11 +1008,13 @@ namespace SHADE
if (GetControllerInputDown(c, nullptr, cNum)) return true;
}
return false;
return false;*/
return (bindings[bindingName].positiveInputHeld && !bindings[bindingName].positiveInputHeldLast);
}
bool SHInputManager::GetBindingNegativeButtonDown(std::string bindingName, size_t cNum) noexcept
bool SHInputManager::GetBindingNegativeButtonDown(std::string const& bindingName, size_t cNum) noexcept
{
/*
if (cNum >= XUSER_MAX_COUNT) return false;
//Over keycodes
@ -679,11 +1029,13 @@ namespace SHADE
if (GetControllerInputDown(c, nullptr, cNum)) return true;
}
return false;
return false;*/
return (bindings[bindingName].negativeInputHeld && !bindings[bindingName].negativeInputHeldLast);
}
bool SHInputManager::GetBindingPositiveButtonUp(std::string bindingName, size_t cNum) noexcept
bool SHInputManager::GetBindingPositiveButtonUp(std::string const& bindingName, size_t cNum) noexcept
{
/*
if (cNum >= XUSER_MAX_COUNT) return false;
//Over keycodes
@ -698,11 +1050,13 @@ namespace SHADE
if (GetControllerInputUp(c, nullptr, cNum)) return true;
}
return false;
return false;*/
return (!bindings[bindingName].positiveInputHeld && bindings[bindingName].positiveInputHeldLast);
}
bool SHInputManager::GetBindingNegativeButtonUp(std::string bindingName, size_t cNum) noexcept
bool SHInputManager::GetBindingNegativeButtonUp(std::string const& bindingName, size_t cNum) noexcept
{
/*
if (cNum >= XUSER_MAX_COUNT) return false;
//Over keycodes
@ -717,107 +1071,7 @@ namespace SHADE
if (GetControllerInputUp(c, nullptr, cNum)) return true;
}
return false;
return false;*/
return (!bindings[bindingName].positiveInputHeld && bindings[bindingName].positiveInputHeldLast);
}
//Fetches longest hold time
double SHInputManager::GetBindingPositiveHeldTime(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return 0.0;
double maxHeldTime = 0.0;
//Over keycodes
for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes)
{
if (GetKeyHeldTime(k) > maxHeldTime) maxHeldTime = GetKeyHeldTime(k);
}
//Over controller buttons
for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes)
{
if (GetControllerInputHeldTime(c, cNum) > maxHeldTime) maxHeldTime = GetControllerInputHeldTime(c);
}
return maxHeldTime;
}
double SHInputManager::GetBindingNegativeHeldTime(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return 0.0;
double maxHeldTime = 0.0;
//Over keycodes
for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes)
{
if (GetKeyHeldTime(k) > maxHeldTime) maxHeldTime = GetKeyHeldTime(k);
}
//Over controller buttons
for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes)
{
if (GetControllerInputHeldTime(c, cNum) > maxHeldTime) maxHeldTime = GetControllerInputHeldTime(c);
}
return maxHeldTime;
}
//Fetches shortest release time
double SHInputManager::GetBindingPositiveReleasedTime(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return 0.0;
double minReleaseTime = _HUGE_ENUF;
//Over keycodes
for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes)
{
if (GetKeyReleasedTime(k) < minReleaseTime) minReleaseTime = GetKeyReleasedTime(k);
}
//Over controller buttons
for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes)
{
if (GetControllerInputReleasedTime(c, cNum) < minReleaseTime) minReleaseTime = GetControllerInputReleasedTime(c);
}
return minReleaseTime;
}
double SHInputManager::GetBindingNegativeReleasedTime(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return 0.0;
double minReleaseTime = _HUGE_ENUF;
//Over keycodes
for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes)
{
if (GetKeyReleasedTime(k) < minReleaseTime) minReleaseTime = GetKeyReleasedTime(k);
}
//Over controller buttons
for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes)
{
if (GetControllerInputReleasedTime(c, cNum) < minReleaseTime) minReleaseTime = GetControllerInputReleasedTime(c);
}
return minReleaseTime;
}
//Only for mouse movement
//Get largest delta
double SHInputManager::GetBindingMouseVelocity(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return 0.0;
//Mouse velocity
double velX = 0.0;
double velY = 0.0;
GetMouseVelocity(&velX, &velY);
return bindings[bindingName].mouseXPositiveMultiplier * velX + bindings[bindingName].mouseYPositiveMultiplier * velY;
}
} //namespace SHADE

View File

@ -14,6 +14,7 @@
#include <map>
#include <set>
#include "../../SHADE_Managed/src/SHpch.h"
#include "../../SHADE_Engine/src/Assets/SHAssetMacros.h"
#include "SH_API.h"
#pragma comment(lib, "xinput.lib")
@ -300,12 +301,28 @@ namespace SHADE
RIGHT_THUMBSTICK_Y
};
//BINDING TYPES///////////////////////////////////////////////////////////
enum class SH_BINDINGTYPE
{
KB_MB_CONTROLLER,
MOUSE_X,
MOUSE_Y,
MOUSE_SCROLL
};
private:
/*------------------------------------------------------------------------*/
/* Struct for logical bindings */
/*------------------------------------------------------------------------*/
struct SH_API SHLogicalBindingData
{
//BINDINGS////////////////////////////////////////////////////////////////
//The type of the binding
SH_BINDINGTYPE bindingType = SH_BINDINGTYPE::KB_MB_CONTROLLER;
//Key codes mapped to positive
std::set<SH_KEYCODE> positiveKeyCodes;
@ -318,9 +335,51 @@ namespace SHADE
//Controller Codes mapped to negative
std::set<SH_CONTROLLERCODE> negativeControllerCodes;
//Mouse movement mapped to axes?
double mouseXPositiveMultiplier;
double mouseYPositiveMultiplier;
//VALUES//////////////////////////////////////////////////////////////////
//The current value of the axis binding
double value = 0.0;
bool positiveInputHeld = false;
bool negativeInputHeld = false;
bool positiveInputHeldLast = false;
bool negativeInputHeldLast = false;
//Whether the input is inverted,
//If so, positive bindings will make the value negative,
// negative bindings will make the value positive,
// moving the mouse up will make the value negative,
// scrolling the mousewheel up will make the value negative,
bool inverted = false;
//When no input is present, how fast does the value fall back to neutral?
//Best to be non-negative
double gravity = 1.0;
//How far the user needs to move an analog stick before application
//registers the movement
double dead = 0.2;
//Speed in units per second that the axis will move toward target value for digital
//For mouse movement / scrolling, serves as multiplier
double sensitivity = 1.0;
//If enabled, axis value will reset to zero when pressing a button
//that corresponds in the opposite direction
bool snap = false;
//How long the positive binding has been held for
double positiveHeldTime = 0.0;
//How long the positive binding has been released for
double positiveReleasedTime = 0.0;
//How long the negative binding has been held for
double negativeHeldTime = 0.0;
//How long the negative binding has been released for
double negativeReleasedTime = 0.0;
};
public:
@ -350,6 +409,12 @@ namespace SHADE
mouseWheelVerticalDeltaPoll += GET_WHEEL_DELTA_WPARAM(wParam);
}
//Get if controller or KB/M is presently being used
static inline bool GetControllerInUse() noexcept
{
return controllerInUse;
}
//For testing purposes
//static void PrintCurrentState() noexcept;
@ -484,7 +549,7 @@ namespace SHADE
}
/*------------------------------------------------------------------------*/
/* Input state accessors (KB & M) */
/* Input state accessors (Controller) */
/*------------------------------------------------------------------------*/
//How many controller inputs of any kind are being used now
@ -617,172 +682,362 @@ namespace SHADE
return controllersReleasedTime[controllerNum][static_cast<size_t>(code)];
}
/*------------------------------------------------------------------------*/
/* Binding I/O */
/*------------------------------------------------------------------------*/
//Save bindings registered into a file
static void SaveBindings(std::string const& targetFile = std::string(ASSET_ROOT) + "/Bindings.SHConfig") noexcept;
//Load and register bindings from a file
//If specified file exists, the current list of bindings will be overwritten, so save them somewhere else before loading
static void LoadBindings(std::string const& sourceFile = std::string(ASSET_ROOT) + "/Bindings.SHConfig") noexcept;
/*------------------------------------------------------------------------*/
/* Binding Functions */
/*------------------------------------------------------------------------*/
//Add a new binding to the map
static inline void BindingsAdd(std::string newBindingName) noexcept
//Get a read-only map of the bindings
static inline std::map<std::string, SHLogicalBindingData> const& GetBindings() noexcept
{
bindings.insert({ newBindingName, SHLogicalBindingData() });
return bindings;
}
//Remove a binding from the map
//Returns 1 if found and removed, 0 if not found
static inline size_t BindingsRemove(std::string targetBindingName) noexcept
//Add a new binding to the map with settings
//Binding type is between four different types,
//KB_MB_CONTROLLER Binding is connected to the keyboard, mouse and controller buttons and its analog inputs
//MOUSE_X Binding is connected to horizontal movement of the mouse
//MOUSE_Y Binding is connected to the vertical movement of the mouse
//MOUSE_SCROLL Binding is connected to the scrolling of the mouse wheel
//Inverted is whether positive inputs result in a negative value and vice-versa
//Snap is if inputting in the opposite direction results in the value snapping to 0 before continuing in the direction
//Sensitivity is how fast the value moves with input
//Dead is how much analogue input magnitude on a scale of 0 to 1 is required before being registered
//Gravity is how fast the value moves to neutral without input
static inline void AddBinding(std::string const& newBindingName,
SH_BINDINGTYPE const bindingType = SH_BINDINGTYPE::KB_MB_CONTROLLER,
bool const inverted = false,
bool const snap = false,
double const sensitivity = 1.0,
double const dead = 0.2,
double const gravity = 1.0) noexcept
{
return bindings.erase(targetBindingName);
bindings.insert({ newBindingName, SHLogicalBindingData() });
bindings[newBindingName].bindingType = bindingType;
bindings[newBindingName].inverted = inverted;
bindings[newBindingName].snap = snap;
bindings[newBindingName].sensitivity = sensitivity;
bindings[newBindingName].dead = dead;
bindings[newBindingName].gravity = gravity;
}
//Remove a binding and all its associated inputs from the list
//Returns 1 if found and removed, 0 if not found
static inline size_t RemoveBinding(std::string const& bindingName) noexcept
{
return bindings.erase(bindingName);
}
//Clears all bindings from the list
static inline void BindingsClear() noexcept
static inline void ClearBindings() noexcept
{
bindings.clear();
}
//Get the number of bindings present
static inline size_t BindingsCount() noexcept
static inline size_t CountBindings() noexcept
{
return bindings.size();
}
//BINDING VALUES////////////////////////////////////////////////////////////
//Get whether the binding is inverted or not
//If inverted, positive inputs subtract the value of the binding
// negative inputs add to the value of the binding
// Moving mouse up / right will be negative
// Scrolling the mouse wheel up will be negative
static inline bool GetBindingInverted(std::string const& bindingName)
{
return bindings[bindingName].inverted;
}
//Set whether the binding is inverted or not
//If inverted, positive inputs subtract the value of the binding
// negative inputs add to the value of the binding
// Moving mouse up / right will be negative
// Scrolling the mouse wheel up will be negative
static inline void SetBindingInverted(std::string const& bindingName, bool const newValue)
{
bindings[bindingName].inverted = newValue;
}
//Gets the gravity of the binding
//The rate at which the value moves to neutral if no input in the direction is read
//Should be non-negative
//Irrelevant for mouse movement and scrolling
static inline double GetBindingGravity(std::string const& bindingName)
{
return bindings[bindingName].gravity;
}
//Sets the gravity of the binding
//The rate at which the value moves to neutral if no input in the direction is read
//Should be non-negative
//Irrelevant for mouse movement and scrolling
static inline void SetBindingGravity(std::string const& bindingName, double const newValue)
{
bindings[bindingName].gravity = newValue;
}
//Get the dead zone of the binding on a scale of 0 to 1,
//Any positive or negative analog input with magnitude less than this will be registered as neutral
//Irrelvant for digital inputs, mouse movement and scrolling
static inline double GetBindingDead(std::string const& bindingName)
{
return bindings[bindingName].dead;
}
//Get the dead zone of the binding on a scale of 0 to 1,
//Any positive or negative analog input with magnitude less than this will be registered as neutral
//Irrelvant for digital inputs, mouse movement and scrolling
static inline void SetBindingDead(std::string const& bindingName, double const newValue)
{
bindings[bindingName].dead = newValue;
}
//Get the sensitivity of the binding
//Serves as a multiplier for mouse movement/scrolling
//For other digital inputs, serves as a rate of how fast axis value goes to maximum positive/negative
//For other analog inputs, serves as a multiplier, but axis value magnitude will still be capped at 1
static inline double GetBindingSensitivity(std::string const& bindingName)
{
return bindings[bindingName].sensitivity;
}
//Set the sensitivity of the binding
//Serves as a multiplier for mouse movement/scrolling
//For other digital inputs, serves as a rate of how fast axis value goes to maximum positive/negative
//For other analog inputs, serves as a multiplier, but axis value magnitude will still be capped at 1
static inline void SetBindingSensitivity(std::string const& bindingName, double const newValue)
{
bindings[bindingName].sensitivity = newValue;
}
//Gets the snap of the binding
//If no other input on the axis is present and a input is made in the opposite direction of the current value,
//the binding's value will jump to neutral 0 before resuming in the input direction
//Irrelevant for mouse movement and scrolling
static inline bool GetBindingSnap(std::string const& bindingName)
{
return bindings[bindingName].snap;
}
//Sets the snap of the binding
//If no other input on the axis is present and a input is made in the opposite direction of the current value,
//the binding's value will jump to neutral 0 before resuming in the input direction
//Irrelevant for mouse movement and scrolling
static inline void SetBindingSnap(std::string const& bindingName, bool const newValue)
{
bindings[bindingName].snap = newValue;
}
//BINDING TYPE//////////////////////////////////////////////////////////////
//Get the type of the binding
//There are four types:
//KB_MB_CONTROLLER Binding is connected to the keyboard, mouse and controller buttons and its analog inputs
//MOUSE_X Binding is connected to horizontal movement of the mouse
//MOUSE_Y Binding is connected to the vertical movement of the mouse
//MOUSE_SCROLL Binding is connected to the scrolling of the mouse wheel
static inline SH_BINDINGTYPE GetBindingType(std::string const& bindingName)
{
return bindings[bindingName].bindingType;
}
//Set the type of the binding
//There are four types:
//KB_MB_CONTROLLER Binding is connected to the keyboard, mouse and controller buttons and its analog inputs
//MOUSE_X Binding is connected to horizontal movement of the mouse
//MOUSE_Y Binding is connected to the vertical movement of the mouse
//MOUSE_SCROLL Binding is connected to the scrolling of the mouse wheel
static inline void SetBindingType(std::string const& bindingName, SH_BINDINGTYPE const newType)
{
bindings[bindingName].bindingType = newType;
}
//POSITIVE KEYCODES/////////////////////////////////////////////////////////
//Check positive keycodes to binding
static inline std::set<SH_KEYCODE> const& BindingsGetPositiveKeyCodes(std::string bindingName) noexcept
static inline std::set<SH_KEYCODE> const& GetBindingPositiveKeyCodes(std::string const& bindingName) noexcept
{
return bindings[bindingName].positiveKeyCodes;
}
//Add positive SH_KEYCODE to binding
static inline void BindingsAddPositiveKeyCode(std::string targetBindingName,
static inline void AddBindingPositiveKeyCode(std::string const& bindingName,
SH_KEYCODE toAdd) noexcept
{
bindings[targetBindingName].positiveKeyCodes.insert(toAdd);
bindings[bindingName].positiveKeyCodes.insert(toAdd);
}
//Remove positive SH_KEYCODE from binding
//If toRemove found and removed, returns 1. Otherwise, 0.
static inline size_t BindingsRemovePositiveKeyCode(std::string targetBindingName,
static inline size_t RemoveBindingPositiveKeyCode(std::string const& bindingName,
SH_KEYCODE toRemove) noexcept
{
return bindings[targetBindingName].positiveKeyCodes.erase(toRemove);
return bindings[bindingName].positiveKeyCodes.erase(toRemove);
}
//Clear all positive SH_KEYCODEs from binding
static inline void ClearBindingPositiveKeyCodes(std::string const& bindingName) noexcept
{
bindings[bindingName].positiveKeyCodes.clear();
}
//NEGATIVE KEYCODES/////////////////////////////////////////////////////////
//Check negative keycodes to binding
static inline std::set<SH_KEYCODE> const& BindingsGetNegativeKeyCodes(std::string bindingName) noexcept
static inline std::set<SH_KEYCODE> const& GetBindingNegativeKeyCodes(std::string const& bindingName) noexcept
{
return bindings[bindingName].negativeKeyCodes;
}
//Add negative SH_KEYCODE to binding
static inline void BindingsAddNegativeKeyCode(std::string targetBindingName,
static inline void AddBindingNegativeKeyCode(std::string const& bindingName,
SH_KEYCODE toAdd) noexcept
{
bindings[targetBindingName].negativeKeyCodes.insert(toAdd);
bindings[bindingName].negativeKeyCodes.insert(toAdd);
}
//Remove negative SH_KEYCODE from binding
//If toRemove found and removed, returns 1. Otherwise, 0.
static inline size_t BindingsRemoveNegativeKeyCode(std::string targetBindingName,
static inline size_t RemoveBindingNegativeKeyCode(std::string const& bindingName,
SH_KEYCODE toRemove) noexcept
{
return bindings[targetBindingName].negativeKeyCodes.erase(toRemove);
return bindings[bindingName].negativeKeyCodes.erase(toRemove);
}
//Clear all negative SH_KEYCODEs from binding
static inline void ClearBindingNegativeKeyCodes(std::string const& bindingName) noexcept
{
bindings[bindingName].negativeKeyCodes.clear();
}
//POSITIVE CONTROLLERCODES//////////////////////////////////////////////////
//Check positive controllercodes to binding
static inline std::set<SH_CONTROLLERCODE> const& BindingsGetPositiveControllerCodes(std::string bindingName) noexcept
static inline std::set<SH_CONTROLLERCODE> const& GetBindingPositiveControllerCodes(std::string const& bindingName) noexcept
{
return bindings[bindingName].positiveControllerCodes;
}
//Add positive SH_CONTROLLERCODE to binding
static inline void BindingsAddPositiveControllerCode(std::string targetBindingName,
static inline void AddBindingPositiveControllerCode(std::string const& bindingName,
SH_CONTROLLERCODE toAdd) noexcept
{
bindings[targetBindingName].positiveControllerCodes.insert(toAdd);
bindings[bindingName].positiveControllerCodes.insert(toAdd);
}
//Remove positive SH_CONTROLLERCODE from binding
//If toRemove found and removed, returns 1. Otherwise, 0.
static inline size_t BindingsRemovePositiveControllerCode(std::string targetBindingName,
static inline size_t RemoveBindingPositiveControllerCode(std::string const& bindingName,
SH_CONTROLLERCODE toRemove) noexcept
{
return bindings[targetBindingName].positiveControllerCodes.erase(toRemove);
return bindings[bindingName].positiveControllerCodes.erase(toRemove);
}
//Clear all positive SH_CONTROLLERCODEs from binding
static inline void ClearBindingPositiveControllerCodes(std::string const& bindingName) noexcept
{
bindings[bindingName].positiveControllerCodes.clear();
}
//NEGATIVE CONTROLLERCODES//////////////////////////////////////////////////
//Check negative controllercodes to binding
static inline std::set<SH_CONTROLLERCODE> const& BindingsGetNegativeControllerCodes(std::string bindingName) noexcept
static inline std::set<SH_CONTROLLERCODE> const& GetBindingNegativeControllerCodes(std::string const& bindingName) noexcept
{
return bindings[bindingName].negativeControllerCodes;
}
//Add negative SH_CONTROLLERCODE to binding
static inline void BindingsAddNegativeControllerCode(std::string targetBindingName,
static inline void AddBindingNegativeControllerCode(std::string const& bindingName,
SH_CONTROLLERCODE toAdd) noexcept
{
bindings[targetBindingName].negativeControllerCodes.insert(toAdd);
bindings[bindingName].negativeControllerCodes.insert(toAdd);
}
//Remove negative SH_CONTROLLERCODE from binding
//If toRemove found and removed, returns 1. Otherwise, 0.
static inline size_t BindingsRemoveNegativeControllerCode(std::string targetBindingName,
static inline size_t RemoveBindingNegativeControllerCode(std::string const& bindingName,
SH_CONTROLLERCODE toRemove) noexcept
{
return bindings[targetBindingName].negativeControllerCodes.erase(toRemove);
return bindings[bindingName].negativeControllerCodes.erase(toRemove);
}
//Mouse movement bindings
static inline double const BindingsGetMouseXPositiveMultiplier(std::string bindingName) noexcept
//Clear all negative SH_CONTROLLERCODEs from binding
static inline void ClearBindingNegativeControllerCodes(std::string const& bindingName) noexcept
{
return bindings[bindingName].mouseXPositiveMultiplier;
bindings[bindingName].negativeControllerCodes.clear();
}
static inline void BindingsSetMouseXPositiveMultiplier(std::string bindingName, double newValue) noexcept
{
bindings[bindingName].mouseXPositiveMultiplier = newValue;
}
//Get the axis value of binding, between -1 and 1 for non-mouse movement/wheel
//For mouse movement/wheel, it won't be between -1 and 1. It will also be multiplied by sensitivity
//To avoid interference between mouse movement/wheel and keyboard/mouse/controller input,
//Set mouseXBound, mouseYBound and mouseScrollBound to false
//controllerNumber is not used
static double GetBindingAxis(std::string const& bindingName, size_t controllerNumber = 0) noexcept;
static inline double const BindingsGetMouseYPositiveMultiplier(std::string bindingName) noexcept
{
return bindings[bindingName].mouseXPositiveMultiplier;
}
static inline void BindingsSetMouseYPositiveMultiplier(std::string bindingName, double newValue) noexcept
{
bindings[bindingName].mouseXPositiveMultiplier = newValue;
}
//Get the axis value of binding, between -1 and 1
static double GetBindingAxis(std::string bindingName, size_t controllerNumber = 0) noexcept;
//Get the axis value of binding, which will be fixed among -1, 0 and 1 for non-mouse movement/wheel
//No difference between this and GetBindingAxis for mouse movement/wheel
//But for other inputs, does not consider smoothing options such as gravity and sensitivity
//If both positive and negative input is detected, returns neutral 0
//controllerNumber is not used
static double GetBindingAxisRaw(std::string const& bindingName, size_t controllerNumber = 0) noexcept;
//Whether binding is being held or not
//Does not work for mouse movement
static bool GetBindingPositiveButton(std::string bindingName, size_t controllerNumber = 0) noexcept;
static bool GetBindingNegativeButton(std::string bindingName, size_t controllerNumber = 0) noexcept;
//Does not work for mouse movement or wheel
static bool GetBindingPositiveButton(std::string const& bindingName, size_t controllerNumber = 0) noexcept;
static bool GetBindingNegativeButton(std::string const& bindingName, size_t controllerNumber = 0) noexcept;
//Whether binding is pressed down IN THIS FRAME ONLY
//Does not work for mouse movement
static bool GetBindingPositiveButtonDown(std::string bindingName, size_t controllerNumber = 0) noexcept;
static bool GetBindingNegativeButtonDown(std::string bindingName, size_t controllerNumber = 0) noexcept;
//Does not work for mouse movement or wheel
static bool GetBindingPositiveButtonDown(std::string const& bindingName, size_t controllerNumber = 0) noexcept;
static bool GetBindingNegativeButtonDown(std::string const& bindingName, size_t controllerNumber = 0) noexcept;
//Whether binding is released IN THIS FRAME ONLY
//Does not work for mouse movement
static bool GetBindingPositiveButtonUp(std::string bindingName, size_t controllerNumber = 0) noexcept;
static bool GetBindingNegativeButtonUp(std::string bindingName, size_t controllerNumber = 0) noexcept;
//Does not work for mouse movement or wheel
static bool GetBindingPositiveButtonUp(std::string const& bindingName, size_t controllerNumber = 0) noexcept;
static bool GetBindingNegativeButtonUp(std::string const& bindingName, size_t controllerNumber = 0) noexcept;
//Binding times
//Does not work for mouse movement
static double GetBindingPositiveHeldTime(std::string bindingName, size_t controllerNumber = 0) noexcept;
static double GetBindingNegativeHeldTime(std::string bindingName, size_t controllerNumber = 0) noexcept;
//Gets how long the binding has been considered positive
//Does not work for mouse movement or wheel
static inline double GetBindingPositiveHeldTime(std::string const& bindingName, size_t controllerNumber = 0) noexcept
{
return bindings[bindingName].positiveHeldTime;
}
//Gets how long the binding has been considered negative
//Does not work for mouse movement or wheel
static inline double GetBindingNegativeHeldTime(std::string const& bindingName, size_t controllerNumber = 0) noexcept
{
return bindings[bindingName].negativeHeldTime;
}
//Does not work for mouse movement
static double GetBindingPositiveReleasedTime(std::string bindingName, size_t controllerNumber = 0) noexcept;
static double GetBindingNegativeReleasedTime(std::string bindingName, size_t controllerNumber = 0) noexcept;
//Binding mouse velocity
//Only for mouse movement
static double GetBindingMouseVelocity(std::string bindingName, size_t controllerNumber = 0) noexcept;
//Gets how long the binding has been not considered positive
//Does not work for mouse movement or wheel
static inline double GetBindingPositiveReleasedTime(std::string const& bindingName, size_t controllerNumber = 0) noexcept
{
return bindings[bindingName].positiveReleasedTime;
}
//Gets how long the binding has been not considered negative
//Does not work for mouse movement or wheel
static inline double GetBindingNegativeReleasedTime(std::string const& bindingName, size_t controllerNumber = 0) noexcept
{
return bindings[bindingName].negativeReleasedTime;
}
/*------------------------------------------------------------------------*/
/* Other Functions */

View File

@ -35,7 +35,17 @@ namespace SHADE
: position { pos }
, orientation { SHQuaternion::FromEuler(rot) }
, scale { scl }
{}
{
ComputeTRS();
}
SHTransform::SHTransform(const SHVec3& pos, const SHQuaternion& quat, const SHVec3& scl) noexcept
: position { pos }
, orientation { quat }
, scale { scl }
{
ComputeTRS();
}
/*-----------------------------------------------------------------------------------*/
/* Operator Overload Definitions */

View File

@ -28,8 +28,9 @@ of DigiPen Institute of Technology is prohibited.
#include "Physics/System/SHPhysicsSystem.h"
#include "Physics/SHPhysicsEvents.h"
#include "Scene/SHSceneEvents.h"
#include "Assets/SHAssetMacros.h"
#include "Tools/Utilities/SHExecUtilities.h"
#include "SHVSUtilities.h"
namespace SHADE
{
@ -189,12 +190,9 @@ namespace SHADE
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 \"" + generateBuildCommand(debug) + L" & exit\""
) == 0;
if (BUILD_SUCCESS)
const auto BUILD_SUCCESS = SHExecUtilties::ExecBlockingProcess(L"C:\\Program Files\\dotnet\\dotnet.exe", generateBuildCommand(debug), false, false);
SHLOG_WARNING("Build Output: {}", SHStringUtilities::WstrToStr(BUILD_SUCCESS.StdOutput));
if (BUILD_SUCCESS.Code == 0)
{
// Copy to built dll to the working directory and replace
if (!copyFile("./tmp/SHADE_Scripting.dll", "SHADE_Scripting.dll", std::filesystem::copy_options::overwrite_existing))
@ -238,7 +236,7 @@ namespace SHADE
LoadScriptAssembly();
}
return BUILD_SUCCESS;
return BUILD_SUCCESS.Code == 0;
}
void SHScriptEngine::GenerateScriptsCsProjFile(const std::filesystem::path& path) const
@ -306,6 +304,16 @@ namespace SHADE
file.close();
}
void SHScriptEngine::OpenSolution()
{
SHVSUtilties::OpenProject(CSPROJ_PATH);
}
void SHScriptEngine::OpenFile(const std::filesystem::path& path)
{
SHVSUtilties::OpenFile(CSPROJ_PATH, path);
}
/*-----------------------------------------------------------------------------------*/
/* Event Handler Functions */
/*-----------------------------------------------------------------------------------*/
@ -610,64 +618,12 @@ namespace SHADE
}
}
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
<< " (" << SHStringUtilities::GetWin32ErrorMessage(err) << ")";
throw std::runtime_error(oss.str());
}
// Wait for execution to end
DWORD status;
while (true)
{
const auto EXEC_SUCCESS = GetExitCodeProcess(procInfo.hProcess, &status);
if (!EXEC_SUCCESS)
{
auto err = GetLastError();
std::ostringstream oss;
oss << "[ScriptEngine] Failed to query process. Error code: " << std::hex << err
<< " (" << SHStringUtilities::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;
}
}
}
std::wstring SHScriptEngine::generateBuildCommand(bool debug)
{
std::wostringstream oss;
oss << "dotnet build \"" << SHStringUtilities::StrToWstr(CSPROJ_PATH) << "\" -c ";
oss << " build \"" << SHStringUtilities::StrToWstr(std::filesystem::absolute(CSPROJ_PATH).string()) << "\" -c ";
oss << debug ? "Debug" : "Release";
oss << " -o \"./tmp/\" -fl -flp:LogFile=build.log;Verbosity=quiet -r \"win-x64\"";
oss << " -o \"./tmp/\" -r \"win-x64\"";
return oss.str();
}
}

View File

@ -1,5 +1,5 @@
/************************************************************************************//*!
\file ScriptEngine.h
\file SHScriptEngine.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Sep 17, 2021
@ -217,6 +217,15 @@ namespace SHADE
/// </summary>
/// <param name="path">File path to the generated file.</param>
void GenerateScriptsCsProjFile(const std::filesystem::path& path = CSPROJ_PATH) const;
/// <summary>
/// Opens the script solution in Visual Studio.
/// </summary>
void OpenSolution();
/// <summary>
/// Opens the file in Visual Studio.
/// </summary>
/// <param name="path"></param>
void OpenFile(const std::filesystem::path& path);
private:
/*-----------------------------------------------------------------------------*/
@ -320,7 +329,6 @@ namespace SHADE
/// <returns> True if the file exists </returns>
static bool fileExists(const std::filesystem::path& filePath);
static bool copyFile(const std::filesystem::path& from, const std::filesystem::path& to, const std::filesystem::copy_options options) noexcept;
static DWORD execProcess(const std::wstring& path, const std::wstring& args);
static std::wstring generateBuildCommand(bool debug);
};
}

View File

@ -0,0 +1,127 @@
/************************************************************************************//*!
\file SHVSUtilities.cpp
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Dec 21, 2022
\brief Contains the implementation for SHVSUtilities static class.
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.
*//*************************************************************************************/
// Precompiled Headers
#include <SHpch.h>
// Primary Header
#include "SHVSUtilities.h"
// Project Headers
#include "Tools/Utilities/SHExecUtilities.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Static Data Members */
/*-----------------------------------------------------------------------------------*/
std::filesystem::path SHVSUtilties::devEnvPath;
HANDLE SHVSUtilties::devEnvInst = nullptr;
/*-----------------------------------------------------------------------------------*/
/* Usage Functions */
/*-----------------------------------------------------------------------------------*/
void SHVSUtilties::OpenProject(const std::filesystem::path& projPath)
try
{
// Check if there is an instance
bool devEnvActive = false;
if (devEnvInst)
{
DWORD status;
const auto GET_CODE_STATUS = GetExitCodeProcess(devEnvInst, &status);
devEnvActive = GET_CODE_STATUS && status == STILL_ACTIVE;
}
// Reuse the existing instance if there is one
if (devEnvActive)
{
// No need to reopen one
return;
}
else
{
if (devEnvPath.empty())
{
devEnvPath = SHVSUtilties::getDevEnvPath();
}
auto absProjPath = std::filesystem::canonical(projPath);
auto pi = SHExecUtilties::ExecProcess(devEnvPath.generic_wstring(), L" " + absProjPath.generic_wstring());
// Cache the process handle
devEnvInst = pi.hProcess;
}
}
catch (std::exception& e)
{
SHLOG_ERROR("{}", e.what());
SHLOG_ERROR("[SHVSUtilities] Failed to launch Visual Studio.");
}
void SHVSUtilties::OpenFile(const std::filesystem::path& projPath, const std::filesystem::path& path)
try
{
// Check if there is an instance
bool devEnvActive = false;
if (devEnvInst)
{
DWORD status;
const auto GET_CODE_STATUS = GetExitCodeProcess(devEnvInst, &status);
devEnvActive = GET_CODE_STATUS && status == STILL_ACTIVE;
}
auto absPath = std::filesystem::canonical(path);
// Reuse the existing instance if there is one
if (devEnvActive)
{
// Edit the file only
SHExecUtilties::ExecProcess(devEnvPath.generic_wstring(), L" /Edit " + absPath.generic_wstring());
}
else
{
if (devEnvPath.empty())
{
devEnvPath = SHVSUtilties::getDevEnvPath();
}
auto absProjPath = std::filesystem::canonical(projPath);
auto pi = SHExecUtilties::ExecProcess(devEnvPath.generic_wstring(), absProjPath.generic_wstring() + L" " + absPath.generic_wstring());
// Cache the process handle
devEnvInst = pi.hProcess;
}
}
catch (std::exception& e)
{
SHLOG_ERROR("{}", e.what());
SHLOG_ERROR("[SHVSUtilities] Failed to launch Visual Studio.");
}
/*-----------------------------------------------------------------------------------*/
/* Helper Functions */
/*-----------------------------------------------------------------------------------*/
std::filesystem::path SHVSUtilties::getDevEnvPath()
{
#ifdef _PUBLISH
return {}; // Don't do anything if it's a published build
#else
static constexpr int EXCESS_CHARS_COUNT = 2;
const auto RESULT = SHExecUtilties::ExecBlockingPowerShellCommand(L"./vswhere -version \"[15.0,19.0]\" -requires Microsoft.NetCore.Component.DevelopmentTools -find Common7\\\\IDE\\\\devenv.exe | Select-Object -first 1", true, true);
if (RESULT.StdOutput.size() < EXCESS_CHARS_COUNT)
{
SHLOG_ERROR("[SHVSUtilities] Unable to get path to Visual Studio installation. SHVSUtilities will not work.");
return {};
}
return RESULT.StdOutput.substr(0, RESULT.StdOutput.size() - EXCESS_CHARS_COUNT);
#endif
}
}

View File

@ -0,0 +1,61 @@
/************************************************************************************//*!
\file SHVSUtilties.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Dec 21, 2022
\brief Contains the interface for SHVSUtilties class.
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.
*//*************************************************************************************/
// STL Includes
#include <filesystem>
// External Dependencies
#include <Windows.h>
namespace SHADE
{
/// <summary>
/// Static class containing functions for working with Visual Studio installation and
/// running instances.
/// </summary>
class SH_API SHVSUtilties final
{
public:
/*---------------------------------------------------------------------------------*/
/* Usage Functions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Opens the project at the specified path with a new or existing instance of Visual
/// Studio if it exists.
/// </summary>
/// <param name="projPath">Path to the project file to open.</param>
static void OpenProject(const std::filesystem::path& projPath);
/// <summary>
/// Opens the file at the specified path with a new or existing instance of Visual
/// Studio if it exists.
/// </summary>
/// <param name="projPath">Path to the project file to open.</param>
/// <param name="path">Path to the file to open.</param>
static void OpenFile(const std::filesystem::path& projPath, const std::filesystem::path& path);
private:
/*---------------------------------------------------------------------------------*/
/* Constructors/Destructors */
/*---------------------------------------------------------------------------------*/
SHVSUtilties() = delete;
/*---------------------------------------------------------------------------------*/
/* Static Data Members */
/*---------------------------------------------------------------------------------*/
static std::filesystem::path devEnvPath;
static HANDLE devEnvInst;
/*---------------------------------------------------------------------------------*/
/* Helper Functions */
/*---------------------------------------------------------------------------------*/
static std::filesystem::path getDevEnvPath();
};
}

View File

@ -52,15 +52,27 @@ namespace SHADE
SHFileIO::WriteStringToFile(editorConfigPath, out.c_str());
}
SHEditorConfig& SHConfigurationManager::LoadEditorConfig()
SHEditorConfig& SHConfigurationManager::LoadEditorConfig(WindowData* windowData)
{
if (!std::filesystem::exists(editorConfigPath))
{
SaveApplicationConfig();
return editorConfig;
}
auto const node = YAML::Load(SHFileIO::GetStringFromFile(editorConfigPath));
auto properties = rttr::type::get<SHApplicationConfig>().get_properties();
auto properties = rttr::type::get<SHEditorConfig>().get_properties();
for(auto const& property : properties)
{
if(node[property.get_name().data()].IsDefined())
SHSerializationHelper::InitializeProperty(&editorConfig, property, node[property.get_name().data()]);
}
if(windowData)
{
windowData->isMaximised = editorConfig.startMaximized;
windowData->width = static_cast<uint32_t>(editorConfig.windowSize.x);
windowData->height = static_cast<uint32_t>(editorConfig.windowSize.y);
}
return editorConfig;
}
@ -86,4 +98,10 @@ RTTR_REGISTRATION
.property("Starting Scene ID", &SHApplicationConfig::startingSceneID)
.property("Window Size", &SHApplicationConfig::windowSize)
.property("Window Title", &SHApplicationConfig::windowTitle);
registration::class_<SHEditorConfig>("Editor Config")
.property("Start Maximized", &SHEditorConfig::startMaximized)
.property("Working Scene ID", &SHEditorConfig::workingSceneID)
.property("Window Size", &SHEditorConfig::windowSize)
.property("Style", &SHEditorConfig::style);
}

View File

@ -7,7 +7,6 @@
namespace SHADE
{
struct SHApplicationConfig
{
bool startInFullScreen{ false };
@ -19,7 +18,11 @@ namespace SHADE
struct SHEditorConfig
{
bool startMaximized{true};
AssetID workingSceneID{};
SHVec2 windowSize {1920, 1080};
uint32_t style = 0;
RTTR_ENABLE()
};
class SH_API SHConfigurationManager
@ -33,7 +36,7 @@ namespace SHADE
static SHApplicationConfig applicationConfig;
#ifdef SHEDITOR
static void SaveEditorConfig();
static SHEditorConfig& LoadEditorConfig();
static SHEditorConfig& LoadEditorConfig(WindowData* windowData = nullptr);
static SHEditorConfig editorConfig;
private:
static void FetchEditorCameraData();

View File

@ -18,6 +18,7 @@
#include "Physics/Interface/SHRigidBodyComponent.h"
#include "UI/SHCanvasComponent.h"
#include "UI/SHButtonComponent.h"
#include "UI/SHToggleButtonComponent.h"
#include "ECS_Base/Managers/SHSystemManager.h"
#include "Graphics/MiddleEnd/Lights/SHLightComponent.h"
#include "Scripting/SHScriptEngine.h"
@ -171,24 +172,14 @@ namespace SHADE
template<typename ComponentType, std::enable_if_t<std::is_base_of_v<SHComponent, ComponentType>, bool> = true>
static void AddComponentToComponentNode(YAML::Node& componentsNode, EntityID const& eid)
{
if (const ComponentType* component = SHComponentManager::GetComponent_s<ComponentType>(eid))
{
componentsNode[rttr::type::get<ComponentType>().get_name().data()] = SHSerializationHelper::SerializeComponentToNode(component);
}
}
template<typename ComponentType, std::enable_if_t<std::is_base_of_v<SHComponent, ComponentType>, bool> = true>
static void AddConvComponentToComponentNode(YAML::Node& componentsNode, EntityID const& eid)
{
YAML::Node node{};
if (ComponentType* component = SHComponentManager::GetComponent_s<ComponentType>(eid))
{
auto componentNode = YAML::convert<ComponentType>::encode(*component);
componentNode[IsActive.data()] = component->isActive;
componentsNode[rttr::type::get<ComponentType>().get_name().data()] = componentNode;
node = SHSerializationHelper::SerializeComponentToNode(component);
componentsNode[rttr::type::get<ComponentType>().get_name().data()] = node;
}
}
YAML::Node SHSerialization::SerializeEntityToNode(SHSceneNode* sceneNode)
{
YAML::Node node;
@ -211,15 +202,16 @@ namespace SHADE
AddComponentToComponentNode<SHTransformComponent>(components, eid);
AddComponentToComponentNode<SHCameraComponent>(components, eid);
AddComponentToComponentNode<SHCameraArmComponent>(components, eid);
AddConvComponentToComponentNode<SHRenderable>(components, eid);
AddComponentToComponentNode<SHRenderable>(components, eid);
AddComponentToComponentNode<SHLightComponent>(components, eid);
AddComponentToComponentNode<SHRigidBodyComponent>(components, eid);
AddConvComponentToComponentNode<SHColliderComponent>(components, eid);
AddComponentToComponentNode<SHColliderComponent>(components, eid);
AddComponentToComponentNode<SHCanvasComponent>(components, eid);
AddComponentToComponentNode<SHButtonComponent>(components, eid);
AddComponentToComponentNode<SHToggleButtonComponent>(components, eid);
AddConvComponentToComponentNode<SHTextRenderableComponent>(components, eid);
AddComponentToComponentNode<SHTextRenderableComponent>(components, eid);
node[ComponentsNode] = components;
@ -275,6 +267,7 @@ namespace SHADE
AddComponentID<SHCanvasComponent>(componentIDList, componentsNode);
AddComponentID<SHButtonComponent>(componentIDList, componentsNode);
AddComponentID<SHToggleButtonComponent>(componentIDList, componentsNode);
AddComponentID<SHTextRenderableComponent>(componentIDList, componentsNode);
return componentIDList;
@ -351,12 +344,13 @@ namespace SHADE
SHSerializationHelper::InitializeComponentFromNode<SHCameraComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHCameraArmComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHRigidBodyComponent>(componentsNode, eid);
SHSerializationHelper::ConvertNodeToComponent<SHRenderable>(componentsNode, eid);
SHSerializationHelper::ConvertNodeToComponent<SHColliderComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHRenderable>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHColliderComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHCanvasComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHButtonComponent>(componentsNode, eid);
SHSerializationHelper::ConvertNodeToComponent<SHTextRenderableComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHToggleButtonComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHTextRenderableComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHLightComponent>(componentsNode, eid);
}
}

View File

@ -104,12 +104,6 @@ namespace SHADE
return node;
}
template <typename ComponentType, std::enable_if_t<std::is_base_of_v<SHComponent, ComponentType>, bool> = true>
static std::string SerializeComponentToString(ComponentType* component)
{
return std::string();
}
template <typename ComponentType, std::enable_if_t<std::is_base_of_v<SHComponent, ComponentType>, bool> = true>
static void SerializeComponentToFile(ComponentType* component, std::filesystem::path const& path)
{
@ -118,14 +112,29 @@ namespace SHADE
template <typename ComponentType, std::enable_if_t<std::is_base_of_v<SHComponent, ComponentType>, bool> = true>
static YAML::Node SerializeComponentToNode(ComponentType* component)
{
YAML::Node node{};
if constexpr (YAML::HasYAMLConv<ComponentType>())
{
if (component)
{
auto componentNode = YAML::convert<ComponentType>::encode(*component);
componentNode[IsActive.data()] = component->isActive;
return componentNode;
}
}
else
{
if (component)
{
YAML::Node compNode{};
if (!component)
return node;
return compNode;
auto componentType = rttr::type::get<ComponentType>();
node = RTTRToNode(*component);
node[IsActive.data()] = component->isActive;
compNode = RTTRToNode(*component);
compNode[IsActive.data()] = component->isActive;
return compNode;
return node;
}
}
}
template <typename Type>
@ -192,15 +201,30 @@ namespace SHADE
}
template <typename ComponentType, std::enable_if_t<std::is_base_of_v<SHComponent, ComponentType>, bool> = true>
static void InitializeComponentFromNode(YAML::Node const& componentsNode, EntityID const& eid)
static bool InitializeComponentFromNode(YAML::Node const& componentsNode, EntityID const& eid)
{
if constexpr (YAML::HasYAMLConv<ComponentType>())
{
auto component = SHComponentManager::GetComponent_s<ComponentType>(eid);
if (componentsNode.IsNull() || !component)
return false;
auto componentNode = GetComponentNode<ComponentType>(componentsNode, eid);
if (componentNode.IsNull() || !componentNode.IsDefined())
return false;
if (componentNode[IsActive.data()].IsDefined())
component->isActive = componentNode[IsActive.data()].as<bool>();
YAML::convert<ComponentType>::decode(componentNode, *component);
return true;
}
else
{
ComponentType* component = SHComponentManager::GetComponent_s<ComponentType>(eid);
if (componentsNode.IsNull() && !component)
return;
return false;
auto rttrType = rttr::type::get<ComponentType>();
auto componentNode = componentsNode[rttrType.get_name().data()];
if (!componentNode.IsDefined())
return;
return false;
if (componentNode[IsActive.data()].IsDefined())
component->isActive = componentNode[IsActive.data()].as<bool>();
@ -213,6 +237,8 @@ namespace SHADE
}
}
}
return true;
}
template <typename ComponentType, std::enable_if_t<std::is_base_of_v<SHComponent, ComponentType>, bool> = true>
static YAML::Node GetComponentNode(YAML::Node const& componentsNode, EntityID const& eid)
@ -227,16 +253,49 @@ namespace SHADE
return componentNode;
}
//template<typename ComponentType, std::enable_if_t<std::is_base_of_v<SHComponent, ComponentType>, bool> = true>
//static void AddComponentToComponentNode(YAML::Node& componentsNode, EntityID const& eid)
//{
// if constexpr (YAML::HasYAMLConv<ComponentType>())
// {
// if (ComponentType* component = SHComponentManager::GetComponent_s<ComponentType>(eid))
// {
// auto componentNode = YAML::convert<ComponentType>::encode(*component);
// componentNode[IsActive.data()] = component->isActive;
// componentsNode[rttr::type::get<ComponentType>().get_name().data()] = componentNode;
// }
// }
// else
// {
// if (const ComponentType* component = SHComponentManager::GetComponent_s<ComponentType>(eid))
// {
// componentsNode[rttr::type::get<ComponentType>().get_name().data()] = SHSerializationHelper::SerializeComponentToNode(component);
// }
// }
//}
template <typename ComponentType, std::enable_if_t<std::is_base_of_v<SHComponent, ComponentType>, bool> = true>
static void ConvertNodeToComponent(YAML::Node const& componentsNode, EntityID const& eid)
static std::string SerializeComponentToString(EntityID const& eid)
{
auto component = SHComponentManager::GetComponent_s<ComponentType>(eid);
if (componentsNode.IsNull() && !component)
return;
auto componentNode = GetComponentNode<ComponentType>(componentsNode, eid);
if (componentNode[IsActive.data()].IsDefined())
component->isActive = componentNode[IsActive.data()].as<bool>();
YAML::convert<ComponentType>::decode(componentNode, *component);
if (ComponentType* component = SHComponentManager::GetComponent_s<ComponentType>(eid))
{
YAML::Emitter out;
YAML::Node node{};
node[rttr::type::get<ComponentType>().get_name().data()] = SerializeComponentToNode(component);
out << node;
return out.c_str();
}
return std::string();
}
template <typename ComponentType, std::enable_if_t<std::is_base_of_v<SHComponent, ComponentType>, bool> = true>
static bool DeserializeComponentFromString(std::string data, EntityID const& eid)
{
YAML::Node node = YAML::Load(data);
return InitializeComponentFromNode<ComponentType>(node, eid);
}
template <typename ComponentType, std::enable_if_t<std::is_base_of_v<SHComponent, ComponentType>, bool> = true>

View File

@ -20,6 +20,16 @@ namespace YAML
{
using namespace SHADE;
template<typename T>
struct HasYAMLConv : std::false_type{};
template<>
struct HasYAMLConv<SHColliderComponent> : std::true_type {};
template<>
struct HasYAMLConv<SHRenderable> : std::true_type {};
template<>
struct HasYAMLConv<SHTextRenderableComponent> : std::true_type {};
template<>
struct convert<SHVec4>
{

View File

@ -128,6 +128,10 @@ namespace SHADE
dbgDrawSys->DrawWireSphere(SHMatrix::Transform(center, SHQuaternion(), scale), color, depthTested);
}
void SHDebugDraw::WireCapsule(const SHVec3& position, const SHQuaternion& rotation, float height, float radius, const SHColour& color, bool depthTested)
{
dbgDrawSys->DrawWireCapsule(position, rotation, height, radius, color, depthTested);
}
/*-----------------------------------------------------------------------------------*/
/* Persistent Draw Functions */
/*-----------------------------------------------------------------------------------*/
@ -216,6 +220,10 @@ namespace SHADE
dbgDrawSys->DrawPersistentWireSphere(SHMatrix::Transform(center, SHQuaternion(), scale), color, depthTested);
}
void SHDebugDraw::Persistent::WireCapsule(const SHVec3& position, const SHQuaternion& rotation, float height, float radius, const SHColour& color, bool depthTested)
{
dbgDrawSys->DrawPersistentWireCapsule(position, rotation, height, radius, color, depthTested);
}
void SHDebugDraw::Persistent::ClearDraws()
{
dbgDrawSys->ClearPersistentDraws();

View File

@ -194,6 +194,18 @@ namespace SHADE
/// <param name="color">Colour to draw with.</param>
/// <param name="depthTested">Whether or not drawn object will be occluded.</param>
static void WireSphere(const SHVec3& center, const SHVec3& scale, const SHVec4& color = SHColour::WHITE, bool depthTested = false);
/// <summary>
/// Draws the outline of a capsule.
/// This will remain drawn until ClearDraws() is called.
/// </summary>
/// <param name="position">Position of the wireframe capsule.</param>
/// <param name="rotation">Rotation of the capsule.</param>
/// <param name="height">Height of the overall capsule.</param>
/// <param name="radius">Radius of the capsule.</param>
/// <param name="color"></param>
/// <param name="color">Colour to draw with.</param>
/// <param name="depthTested">Whether or not drawn object will be occluded.</param>
static void WireCapsule(const SHVec3& position, const SHQuaternion& rotation, float height, float radius, const SHColour& color = SHColour::WHITE, bool depthTested = false);
/*---------------------------------------------------------------------------------*/
/* Persistent Draw Function Class "Folder" */
@ -366,6 +378,18 @@ namespace SHADE
/// <param name="depthTested">Whether or not drawn object will be occluded.</param>
static void WireSphere(const SHVec3& center, const SHVec3& scale, const SHVec4& color = SHColour::WHITE, bool depthTested = false);
/// <summary>
/// Draws the outline of a capsule.
/// This will remain drawn until ClearDraws() is called.
/// </summary>
/// <param name="position">Position of the wireframe capsule.</param>
/// <param name="rotation">Rotation of the capsule.</param>
/// <param name="height">Height of the overall capsule.</param>
/// <param name="radius">Radius of the capsule.</param>
/// <param name="color"></param>
/// <param name="color">Colour to draw with.</param>
/// <param name="depthTested">Whether or not drawn object will be occluded.</param>
static void WireCapsule(const SHVec3& position, const SHQuaternion& rotation, float height, float radius, const SHColour& color = SHColour::WHITE, bool depthTested = false);
/// <summary>
/// Clears any persistent drawn debug primitives.
/// </summary>
static void ClearDraws();

View File

@ -0,0 +1,215 @@
/************************************************************************************//*!
\file SHExecUtilities.cpp
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Dec 21, 2022
\brief Contains the implementation for SHExecUtilities static class.
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.
*//*************************************************************************************/
// Precompiled Headers
#include <SHpch.h>
// Primary Header
#include "SHExecUtilities.h"
// Project Includes
#include "SHStringUtilities.h"
#include "SHFileUtilties.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Process Execution Functions */
/*-----------------------------------------------------------------------------------*/
PROCESS_INFORMATION SHExecUtilties::ExecProcess(const std::wstring& path, const std::wstring& args)
{
return execProcess(path, args, nullptr, nullptr);
}
ExecResult SHExecUtilties::ExecBlockingProcess(const std::wstring& path, const std::wstring& args, bool output, bool errorOutput)
{
// Create pipes for stdout and stderr if specified
HANDLE stdoutReadPipe = nullptr;
HANDLE stdoutWritePipe = nullptr;
HANDLE stderrReadPipe = nullptr;
HANDLE stderrWritePipe = nullptr;
if (output)
{
std::tie(stdoutReadPipe, stdoutWritePipe) = createIoPipes();
}
if (errorOutput)
{
std::tie(stderrReadPipe, stderrWritePipe) = createIoPipes();
}
PROCESS_INFORMATION procInfo = execProcess(path, args, stdoutWritePipe, stderrWritePipe);
// Wait for execution to end
ExecResult result;
while (true)
{
const auto EXEC_SUCCESS = GetExitCodeProcess(procInfo.hProcess, &result.Code);
if (!EXEC_SUCCESS)
{
auto err = GetLastError();
std::ostringstream oss;
oss << "[SHExecUtilties] Failed to query process. Error code: " << std::hex << err
<< " (" << SHStringUtilities::GetWin32ErrorMessage(err) << ")";
throw std::runtime_error(oss.str());
}
// Break only if process ends
if (result.Code != STILL_ACTIVE)
{
// Get stdout/stderror output
if (stdoutReadPipe)
{
readPipeData(stdoutReadPipe, stdoutWritePipe, result.StdOutput);
}
if (stderrReadPipe)
{
readPipeData(stderrReadPipe, stderrWritePipe, result.StdErrOutput);
}
// Close the process and threads for that process
CloseHandle(procInfo.hProcess);
CloseHandle(procInfo.hThread);
// Return results
return result;
}
}
}
/*-----------------------------------------------------------------------------------*/
/* Command Execution Functions */
/*-----------------------------------------------------------------------------------*/
PROCESS_INFORMATION SHExecUtilties::ExecCommand(const std::wstring& command)
{
return ExecProcess
(
L"C:\\Windows\\system32\\cmd.exe",
L"/K \"" + command + L" & exit\""
);
}
ExecResult SHExecUtilties::ExecBlockingCommand(const std::wstring& command, bool output, bool errorOutput)
{
return ExecBlockingProcess
(
L"C:\\Windows\\system32\\cmd.exe",
L"/K \"" + command + L" & exit\"",
output,
errorOutput
);
}
PROCESS_INFORMATION SHExecUtilties::ExecPowerShellCommand(const std::wstring& command)
{
return ExecProcess
(
L"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
L"-Command \"& {" + command + L"} \""
);
}
ExecResult SHExecUtilties::ExecBlockingPowerShellCommand(const std::wstring& command, bool output, bool errorOutput)
{
return ExecBlockingProcess
(
L"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
L"-Command \"& { cd \"" + SHFileUtilities::GetExecDir().generic_wstring() + L"\";" + command + L"} \"",
output,
errorOutput
);
}
PROCESS_INFORMATION SHExecUtilties::execProcess(const std::wstring& path, const std::wstring& args, HANDLE outputWritePipe, HANDLE errorOutputWritePipe)
{
STARTUPINFOW startInfo;
PROCESS_INFORMATION procInfo;
ZeroMemory(&startInfo, sizeof(startInfo));
ZeroMemory(&procInfo, sizeof(procInfo));
startInfo.cb = sizeof(startInfo);
if (outputWritePipe)
{
startInfo.hStdOutput = outputWritePipe;
startInfo.dwFlags |= STARTF_USESTDHANDLES;
startInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
}
if (errorOutputWritePipe)
{
startInfo.hStdError = errorOutputWritePipe;
startInfo.dwFlags |= STARTF_USESTDHANDLES;
startInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
}
std::wstring argsWstr = args;
// Start Process
const auto SUCCESS = CreateProcess
(
path.data(), argsWstr.data(),
nullptr, nullptr, true, NULL, nullptr, nullptr,
&startInfo, &procInfo
);
// Error Check
if (!SUCCESS)
{
auto err = GetLastError();
std::ostringstream oss;
oss << "[SHExecUtilties] Failed to launch process. Error code: " << std::hex << err
<< " (" << SHStringUtilities::GetWin32ErrorMessage(err) << ")";
throw std::runtime_error(oss.str());
}
return procInfo;
}
std::pair<HANDLE, HANDLE> SHExecUtilties::createIoPipes()
{
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = true;
saAttr.lpSecurityDescriptor = nullptr;
// First: Read | Second: Write
std::pair<HANDLE, HANDLE> output = { nullptr, nullptr };
if (CreatePipe(&output.first, &output.second, &saAttr, 0))
{
if (!SetHandleInformation(output.first, HANDLE_FLAG_INHERIT, 0))
{
SHLOG_ERROR("[SHExecUtilities] Failed to initialize pipe for process IO.");
CloseHandle(output.first);
CloseHandle(output.second);
output = { nullptr, nullptr };
}
}
else
{
SHLOG_ERROR("[SHExecUtilities] Failed to create pipe for process IO.");
}
return output;
}
void SHExecUtilties::readPipeData(HANDLE readPipe, HANDLE writePipe, std::wstring& output)
{
CloseHandle(writePipe);
LARGE_INTEGER stdoutSize = {};
while (true)
{
std::array<char, 256> buffer{};
DWORD bytesRead = 0;
const auto RESULT = ReadFile(readPipe, buffer.data(), buffer.size(), &bytesRead, nullptr);
if (!RESULT || bytesRead <= 0)
break;
output.insert(output.end(), buffer.data(), buffer.data() + bytesRead);
}
CloseHandle(readPipe);
}
}

View File

@ -0,0 +1,142 @@
/************************************************************************************//*!
\file SHExecUtilties.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Dec 21, 2022
\brief Contains the interface for SHExecUtilities class.
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.
*//*************************************************************************************/
// STL Includes
#include <string>
// External Dependencies
#include <Windows.h>
namespace SHADE
{
/// <summary>
/// Stores the result of an execution of a process.
/// </summary>
struct SH_API ExecResult final
{
/// <summary>
/// Exit code of the process.
/// </summary>
DWORD Code;
/// <summary>
/// Stored text output from the process.
/// </summary>
std::wstring StdOutput;
/// <summary>
/// Stored error text output from the process.
/// </summary>
std::wstring StdErrOutput;
};
/// <summary>
/// Static class containing functions for executing external processes or commands.
/// </summary>
class SH_API SHExecUtilties final
{
public:
/*---------------------------------------------------------------------------------*/
/* Process Execution Functions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Executes a process at the specified path with the specified arguments. This call
/// does not wait for the process to finish executing.
/// </summary>
/// <param name="path">Path to the processs to start.</param>
/// <param name="args">Arguments to pass to the process.</param>
/// <returns>Information about the started process.</returns>
/// <exception cref="std::runtime_error">
/// Thrown if failed to start the process.
/// </exception>
static PROCESS_INFORMATION ExecProcess(const std::wstring& path, const std::wstring& args);
/// <summary>
/// Executes a process at the specified path with the specified arguments and waits
/// for that process to finish executing.
/// </summary>
/// <param name="path">Path to the processs to start.</param>
/// <param name="args">Arguments to pass to the process.</param>
/// <param name="output">If true, stdout will be routed to return.</param>
/// <param name="errorOutput">If true, outstderr will be routed to return.</param>
/// <returns>Information about the process's execution.</returns>
/// <exception cref="std::runtime_error">
/// Thrown if failed to start the process.
/// </exception>
static ExecResult ExecBlockingProcess(const std::wstring& path, const std::wstring& args, bool output = false, bool errorOutput = false);
/*---------------------------------------------------------------------------------*/
/* Command Execution Functions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Executes a specified command in cmd.
/// This call does not wait for the command to finish executing.
/// </summary>
/// <param name="command">Command to execute.</param>
/// <returns>
/// Information about the started cmd process that executes the command.
/// </returns>
/// <exception cref="std::runtime_error">
/// Thrown if failed to start the process.
/// </exception>
static PROCESS_INFORMATION ExecCommand(const std::wstring& command);
/// <summary>
/// Executes a specified command in cmd and waits for that process to finish
/// executing.
/// </summary>
/// <param name="command">Command to execute.</param>
/// <param name="output">If true, stdout will be routed to return.</param>
/// <param name="errorOutput">If true, outstderr will be routed to return.</param>
/// <returns>Information about the process's execution.</returns>
/// <exception cref="std::runtime_error">
/// Thrown if failed to start the process.
/// </exception>
static ExecResult ExecBlockingCommand(const std::wstring& command, bool output = false, bool errorOutput = false);
/*---------------------------------------------------------------------------------*/
/* PowerShell Execution Functions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Executes a specified command in PowerShell.
/// This call does not wait for the command to finish executing.
/// </summary>
/// <param name="command">Command to execute.</param>
/// <returns>
/// Information about the started cmd process that executes the command.
/// </returns>
/// <exception cref="std::runtime_error">
/// Thrown if failed to start the process.
/// </exception>
static PROCESS_INFORMATION ExecPowerShellCommand(const std::wstring& command);
/// <summary>
/// Executes a specified command in PowerShell and waits for that process to finish
/// executing.
/// </summary>
/// <param name="command">Command to execute.</param>
/// <param name="output">If true, stdout will be routed to return.</param>
/// <param name="errorOutput">If true, outstderr will be routed to return.</param>
/// <returns>Information about the process's execution.</returns>
/// <exception cref="std::runtime_error">
/// Thrown if failed to start the process.
/// </exception>
static ExecResult ExecBlockingPowerShellCommand(const std::wstring& command, bool output, bool errorOutput);
private:
/*---------------------------------------------------------------------------------*/
/* Constructors/Destructors */
/*---------------------------------------------------------------------------------*/
SHExecUtilties() = delete;
/*---------------------------------------------------------------------------------*/
/* Helper Functions */
/*---------------------------------------------------------------------------------*/
static PROCESS_INFORMATION execProcess(const std::wstring& path, const std::wstring& args, HANDLE outputWritePipe, HANDLE errorOutputWritePipe);
static std::pair<HANDLE, HANDLE> createIoPipes();
static void readPipeData(HANDLE readPipe, HANDLE writePipe, std::wstring& output);
};
}

View File

@ -21,10 +21,22 @@ of DigiPen Institute of Technology is prohibited.
namespace SHADE
{
void SHFileUtilities::SetWorkDirToExecDir()
{
std::filesystem::current_path(GetExecDir());
}
std::filesystem::path SHFileUtilities::GetExecDir()
{
TCHAR currentExecFilePath[MAX_PATH] = { '\0' };
GetModuleFileName(nullptr, currentExecFilePath, MAX_PATH);
PathRemoveFileSpec(currentExecFilePath);
std::filesystem::current_path(currentExecFilePath);
return std::filesystem::path(currentExecFilePath);
}
std::filesystem::path SHFileUtilities::GetExecPath()
{
TCHAR currentExecFilePath[MAX_PATH] = { '\0' };
GetModuleFileName(nullptr, currentExecFilePath, MAX_PATH);
return std::filesystem::path(currentExecFilePath);
}
}

View File

@ -15,24 +15,29 @@ of DigiPen Institute of Technology is prohibited.
namespace SHADE
{
/*!************************************************************************************
\class SHFileUtilities
\brief
Static class that contains functions for working with files and directories.
**************************************************************************************/
/// <summary>
/// Static class that contains functions for working with files and directories.
/// </summary>
class SH_API SHFileUtilities
{
public:
/*!**********************************************************************************
\brief
Sets the application's current working directory to the application executable's
directory.
************************************************************************************/
/*---------------------------------------------------------------------------------*/
/* Executable Directory Functions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Sets the application's current working directory to the application executable's
/// directory.
/// </summary>
static void SetWorkDirToExecDir();
/// <summary>
/// Retrieves the file path to the executable's directory.
/// </summary>
/// <returns>File path to the executable's directory.</returns>
static std::filesystem::path GetExecDir();
/// <summary>
/// Retrieves the file path to the executable.
/// </summary>
/// <returns>File path to the executable.</returns>
static std::filesystem::path GetExecPath();
};
}

View File

@ -30,16 +30,18 @@ namespace SHADE
std::string SHStringUtilities::WstrToStr(const std::wstring& wstr)
{
static std::vector<char> buffer;
buffer.clear();
const int STR_SIZE = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.size()), nullptr, 0, nullptr, nullptr) + 1 /* Null Terminator */;
buffer.resize(STR_SIZE);
buffer.resize(STR_SIZE, '\0');
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.size()), buffer.data(), MAX_PATH, nullptr, nullptr);
return std::string(buffer.data());
}
std::wstring SHStringUtilities::StrToWstr(const std::string& str)
{
static std::vector<wchar_t> buffer;
buffer.clear();
const int WSTR_SIZE = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()), nullptr, 0) + 1 /* Null Terminator */;
buffer.resize(WSTR_SIZE);
buffer.resize(WSTR_SIZE, '\0');
MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()), buffer.data(), WSTR_SIZE);
return std::wstring(buffer.data());
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "ECS_Base/SHECSMacros.h"
namespace SHADE
{
struct SHButtonClickEvent
{
EntityID EID;
// value of the toggle button, default to false if its a button and not a toggle button
bool value{false};
};
}

View File

@ -5,7 +5,7 @@
namespace SHADE
{
SHButtonComponent::SHButtonComponent()
:size(1.0f), offset(0.0f), isHovered(false), isClicked(false),
:size(1.0f), isHovered(false), isClicked(false),
defaultTexture(0), hoveredTexture(0), clickedTexture(0)
{
}

View File

@ -18,7 +18,6 @@ namespace SHADE
virtual ~SHButtonComponent() = default;
SHVec2 size;
SHVec2 offset;
AssetID GetClickedTexture() const noexcept;
AssetID GetDefaultTexture() const noexcept;
@ -33,8 +32,10 @@ namespace SHADE
friend class SHUISystem;
private:
//Set to true when mouse is hovering over the button.
bool isHovered;
//This is set to true when the mouse clicks down, and set back to false when mouse releases.
//The event for the button click will be broadcasted when mouse release.
bool isClicked;
AssetID defaultTexture;
AssetID hoveredTexture;

View File

@ -0,0 +1,39 @@
#include "SHpch.h"
#include "SHSliderComponent.h"
namespace SHADE
{
SHSliderComponent::SHSliderComponent()
:size(1.0f), isHovered(false), isClicked(false), value(0.0f)
{
}
float SHSliderComponent::GetValue() const noexcept
{
return value;
}
void SHSliderComponent::SetValue(float value) noexcept
{
this->value = value;
}
}
RTTR_REGISTRATION
{
using namespace SHADE;
using namespace rttr;
registration::class_<SHSliderComponent>("Slider Component")
.property("Slider Value", &SHSliderComponent::GetValue, &SHSliderComponent::SetValue)
;
}

View File

@ -0,0 +1,41 @@
#pragma once
#include <rttr/registration>
#include "SH_API.h"
#include "ECS_Base/Components/SHComponent.h"
#include "Math/Vector/SHVec3.h"
#include "Math/Vector/SHVec2.h"
#include "Assets/SHAssetMacros.h"
namespace SHADE
{
class SH_API SHSliderComponent final: public SHComponent
{
public:
SHSliderComponent();
virtual ~SHSliderComponent() = default;
SHVec2 size;
float GetValue() const noexcept;
void SetValue(float value) noexcept;
friend class SHUISystem;
private:
bool isHovered;
bool isClicked;
float value;
RTTR_ENABLE()
};
}

View File

@ -0,0 +1,59 @@
#include "SHpch.h"
#include "SHToggleButtonComponent.h"
namespace SHADE
{
SHToggleButtonComponent::SHToggleButtonComponent()
:size(1.0f), isHovered(false), isClicked(false), value(false),
defaultTexture(0), toggledTexture(0)
{
}
AssetID SHToggleButtonComponent::GetDefaultTexture() const noexcept
{
return defaultTexture;
}
AssetID SHToggleButtonComponent::GetToggledTexture() const noexcept
{
return toggledTexture;
}
bool SHToggleButtonComponent::GetValue() const noexcept
{
return value;
}
void SHToggleButtonComponent::SetDefaultTexture(AssetID texture) noexcept
{
defaultTexture = texture;
}
void SHToggleButtonComponent::SetToggledTexture(AssetID texture) noexcept
{
toggledTexture = texture;
}
void SHToggleButtonComponent::SetValue(bool value) noexcept
{
this->value = value;
}
}
RTTR_REGISTRATION
{
using namespace SHADE;
using namespace rttr;
registration::class_<SHToggleButtonComponent>("Toggle Button Component")
.property("Non Toggled Texture", &SHToggleButtonComponent::GetDefaultTexture, &SHToggleButtonComponent::SetDefaultTexture)
.property("Toggled Texture", &SHToggleButtonComponent::GetToggledTexture, &SHToggleButtonComponent::SetToggledTexture)
.property("Value", &SHToggleButtonComponent::GetValue, &SHToggleButtonComponent::SetValue)
;
}

View File

@ -0,0 +1,51 @@
#pragma once
#include <rttr/registration>
#include "SH_API.h"
#include "ECS_Base/Components/SHComponent.h"
#include "Math/Vector/SHVec3.h"
#include "Math/Vector/SHVec2.h"
#include "Assets/SHAssetMacros.h"
namespace SHADE
{
class SH_API SHToggleButtonComponent final: public SHComponent
{
public:
SHToggleButtonComponent();
virtual ~SHToggleButtonComponent() = default;
SHVec2 size;
AssetID GetToggledTexture() const noexcept;
AssetID GetDefaultTexture() const noexcept;
bool GetValue() const noexcept;
void SetDefaultTexture(AssetID texture) noexcept;
void SetToggledTexture(AssetID texture) noexcept;
void SetValue(bool value) noexcept;
friend class SHUISystem;
private:
//Set to true when mouse is hovering over the button.
bool isHovered;
//This is set to true when the mouse clicks down, and set back to false when mouse releases.
//The event for the button click will be broadcasted when mouse release.
bool isClicked;
bool value;
AssetID defaultTexture;
AssetID toggledTexture;
RTTR_ENABLE()
};
}

View File

@ -9,7 +9,15 @@
#include "Editor/EditorWindow/ViewportWindow/SHEditorViewport.h"
#include "Editor/SHEditor.h"
#include "Resource/SHResourceManager.h"
#include "Assets/SHAssetManager.h"
#include "Input/SHInputManager.h"
#include "SHUIComponent.h"
#include "SHButtonComponent.h"
#include "SHToggleButtonComponent.h"
#include "SHSliderComponent.h"
#include "SHCanvasComponent.h"
#include "Events/SHEventManager.hpp"
#include "Events/SHButtonClickEvent.h"
namespace SHADE
{
@ -103,7 +111,7 @@ namespace SHADE
{
auto transform = SHComponentManager::GetComponent<SHTransformComponent>(comp.GetEID());
if (canvasComp != nullptr)
comp.localToCanvasMatrix = canvasComp->GetMatrix()* transform->GetTRS();
comp.localToCanvasMatrix = transform->GetTRS() * canvasComp->GetMatrix();
else
comp.localToCanvasMatrix = transform->GetTRS();
}
@ -139,76 +147,234 @@ namespace SHADE
void SHUISystem::UpdateButtonComponent(SHButtonComponent& comp) noexcept
{
if (!SHComponentManager::HasComponent<SHTransformComponent>(comp.GetEID()) || !SHComponentManager::HasComponent<SHUIComponent>(comp.GetEID()))
if (!SHComponentManager::HasComponent<SHUIComponent>(comp.GetEID()))
{
return;
}
auto cameraSystem = SHSystemManager::GetSystem<SHCameraSystem>();
auto uiComp = SHComponentManager::GetComponent<SHUIComponent>(comp.GetEID());
//auto canvasComp = SHComponentManager::GetComponent_s<SHCanvasComponent>(uiComp->canvasID);
SHVec4 topExtent4 = SHMatrix::Translate(-comp.size.x * 0.5f, comp.size.y * 0.5f, 0.0f) * uiComp->GetMatrix() * SHVec4(0.0f, 0.0f, 0.0f, 1.0f);
SHVec4 btmExtent4 = SHMatrix::Translate(comp.size.x * 0.5f, -comp.size.y * 0.5f, 0.0f) * uiComp->GetMatrix() * SHVec4(0.0f,0.0f, 0.0f,1.0f);
SHVec2 topExtent{ topExtent4.x,topExtent4.y };
SHVec2 btmExtent{ btmExtent4.x,btmExtent4.y };
SHVec2 mousePos;
SHVec2 windowSize;
#ifdef SHEDITOR
windowSize = SHEditorWindowManager::GetEditorWindow<SHEditorViewport>()->beginContentRegionAvailable;
mousePos = SHEditorWindowManager::GetEditorWindow<SHEditorViewport>()->viewportMousePos;
//mousePos.y = windowSize.y - mousePos.y;
//SHLOG_INFO("mouse pos: {}, {}", mousePos.x, mousePos.y)
mousePos /= windowSize;
//SHLOG_INFO("mouse pos normalized: {}, {}", mousePos.x, mousePos.y)
#else
int x, y;
SHInputManager::GetMouseScreenPosition(&x, &y);
mousePos.x = x;
mousePos.y = y;
auto ws = SHSystemManager::GetSystem<SHGraphicsSystem>()->GetWindow()->GetWindowSize();
windowSize = { ws.first,ws.second };
mousePos /= windowSize;
#endif
SHVec2 camSize{ cameraSystem->GetCameraWidthHeight(0)};
//SHLOG_INFO("TopExtent: {}, {}", topExtent.x, topExtent.y)
topExtent = CanvasToScreenPoint(topExtent,true);
btmExtent = CanvasToScreenPoint(btmExtent,true);
//SHLOG_INFO("TopExtent: {}, {} Btm Extent: {}, {}", topExtent.x, topExtent.y, btmExtent.x, btmExtent.y)
//comp.isClicked = false;
if (mousePos.x >= topExtent.x && mousePos.x <= btmExtent.x
&& mousePos.y >= topExtent.y && mousePos.y <= btmExtent.y)
{
comp.isHovered = true;
#ifdef SHEDITOR
//if (SHSystemManager::GetSystem<SHEditor>()->editorState == SHEditor::State::PLAY)
{
if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::LMB))
{
comp.isClicked = true;
}
}
#else
if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::LMB))
{
comp.isClicked = true;
}
#endif
//SHLOG_INFO("HOVERED")
}
else
{
comp.isHovered = false;
//SHLOG_INFO("NOT HOVERED")
}
if (comp.isClicked && SHInputManager::GetKeyUp(SHInputManager::SH_KEYCODE::LMB))
{
comp.isClicked = false;
SHButtonClickEvent clickEvent;
clickEvent.EID = comp.GetEID();
SHEventManager::BroadcastEvent(clickEvent, SH_BUTTON_CLICK_EVENT);
}
if (SHComponentManager::HasComponent<SHRenderable>(comp.GetEID()))
{
auto renderable = SHComponentManager::GetComponent_s<SHRenderable>(comp.GetEID());
//auto texture = SHResourceManager::Get<SHTexture>(comp.GetDefaultTexture());
auto material = renderable->GetModifiableMaterial();
if(!comp.isHovered && !comp.isClicked)
if (comp.GetDefaultTexture() != 0 && SHAssetManager::GetType(comp.GetDefaultTexture()) == AssetType::TEXTURE)
{
material->SetProperty("data.textureIndex", comp.GetDefaultTexture());
//SHLOG_INFO("SETTING DEFAULT TEXTURE")
}
else if (comp.isClicked)
{
if (comp.GetClickedTexture() != 0 && SHAssetManager::GetType(comp.GetClickedTexture()) == AssetType::TEXTURE)
{
material->SetProperty("data.textureIndex", comp.GetClickedTexture());
//SHLOG_INFO("SETTING CLICKED TEXTURE")
}
}
else
{
if (comp.GetHoveredTexture() != 0 && SHAssetManager::GetType(comp.GetHoveredTexture()) == AssetType::TEXTURE)
{
material->SetProperty("data.textureIndex", comp.GetHoveredTexture());
//SHLOG_INFO("SETTING HOVERED TEXTURE")
}
}
}
}
void SHUISystem::UpdateToggleButtonComponent(SHToggleButtonComponent& comp) noexcept
{
if (!SHComponentManager::HasComponent<SHUIComponent>(comp.GetEID()))
{
return;
}
auto cameraSystem = SHSystemManager::GetSystem<SHCameraSystem>();
auto uiComp = SHComponentManager::GetComponent<SHUIComponent>(comp.GetEID());
SHVec4 topExtent4 = uiComp->GetMatrix() * SHVec4(-comp.size.x * 0.5f, comp.size.y * 0.5f , 0.0f,1.0f);
SHVec4 btmExtent4 = uiComp->GetMatrix() * SHVec4(comp.size.x * 0.5f , -comp.size.y * 0.5f , 0.0f, 1.0f);
SHVec2 topExtent{ topExtent4.x,-topExtent4.y };
SHVec2 btmExtent{ btmExtent4.x,-btmExtent4.y };
SHVec4 topExtent4 = SHMatrix::Translate(-comp.size.x * 0.5f, comp.size.y * 0.5f, 0.0f) * uiComp->GetMatrix() * SHVec4(0.0f, 0.0f, 0.0f, 1.0f);
SHVec4 btmExtent4 = SHMatrix::Translate(comp.size.x * 0.5f, -comp.size.y * 0.5f, 0.0f) * uiComp->GetMatrix() * SHVec4(0.0f, 0.0f, 0.0f, 1.0f);
SHVec2 topExtent{ topExtent4.x,topExtent4.y };
SHVec2 btmExtent{ btmExtent4.x,btmExtent4.y };
SHVec2 windowSize;
SHVec2 mousePos;
SHVec2 windowSize;
#ifdef SHEDITOR
windowSize = SHEditorWindowManager::GetEditorWindow<SHEditorViewport>()->windowSize;
windowSize = SHEditorWindowManager::GetEditorWindow<SHEditorViewport>()->beginContentRegionAvailable;
mousePos = SHEditorWindowManager::GetEditorWindow<SHEditorViewport>()->viewportMousePos;
//mousePos.y = windowSize.y - mousePos.y;
//SHLOG_INFO("mouse pos: {}, {}", mousePos.x, mousePos.y)
mousePos /= windowSize;
//SHLOG_INFO("mouse pos normalized: {}, {}", mousePos.x, mousePos.y)
#else
int x, y;
SHInputManager::GetMouseScreenPosition(&x, &y);
mousePos.x = x;
mousePos.y = y;
auto ws = SHSystemManager::GetSystem<SHGraphicsSystem>()->GetWindow()->GetWindowSize();
windowSize = { ws.first,ws.second };
mousePos /= windowSize;
#endif
SHVec2 camSize{ cameraSystem->GetCameraWidthHeight(0) };
SHVec2 camSize{ cameraSystem->GetCameraWidthHeight(0).x , cameraSystem->GetCameraWidthHeight(0).y };
topExtent += camSize * 0.5f;
btmExtent += camSize * 0.5f;
topExtent = CanvasToScreenPoint(topExtent,true);
btmExtent = CanvasToScreenPoint(btmExtent, true);
//Convert everything to using ratios
topExtent /= camSize;
btmExtent /= camSize;
mousePos /= windowSize;
//SHLOG_INFO("mousePos: {} , {}", mousePos.x, mousePos.y);
comp.isClicked = false;
if (mousePos.x >= topExtent.x && mousePos.x <= btmExtent.x
&& mousePos.y >= topExtent.y && mousePos.y <= btmExtent.y)
{
comp.isHovered = true;
if (SHInputManager::GetKeyUp(SHInputManager::SH_KEYCODE::LMB))
#ifdef SHEDITOR
if (SHSystemManager::GetSystem<SHEditor>()->editorState == SHEditor::State::PLAY)
{
if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::LMB))
{
comp.isClicked = true;
}
//SHLOG_INFO("BUTTON HOVERED");
}
#else
if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::LMB))
{
comp.isClicked = true;
}
#endif
}
else
{
comp.isHovered = false;
//SHLOG_INFO("BUTTON NOT HOVERED")
}
if (comp.isClicked && SHInputManager::GetKeyUp(SHInputManager::SH_KEYCODE::LMB))
{
comp.isClicked = false;
comp.value = !comp.value;
SHButtonClickEvent clickEvent;
clickEvent.EID = comp.GetEID();
clickEvent.value = comp.value;
SHEventManager::BroadcastEvent(clickEvent, SH_BUTTON_CLICK_EVENT);
}
if (SHComponentManager::HasComponent<SHRenderable>(comp.GetEID()))
{
//auto renderable = SHComponentManager::GetComponent_s<SHRenderable>(comp.GetEID());
auto renderable = SHComponentManager::GetComponent_s<SHRenderable>(comp.GetEID());
//auto texture = SHResourceManager::Get<SHTexture>(comp.GetDefaultTexture());
//auto material = renderable->GetModifiableMaterial();
//material->SetProperty("texture", comp.GetDefaultTexture());
auto material = renderable->GetModifiableMaterial();
if (comp.GetValue() == false)
{
if (comp.GetDefaultTexture()!= 0 && SHAssetManager::GetType(comp.GetDefaultTexture()) == AssetType::TEXTURE)
{
material->SetProperty("data.textureIndex", comp.GetDefaultTexture());
//SHLOG_INFO("SETTING DEFAULT TEXTURE")
}
}
else
{
if (comp.GetToggledTexture() != 0 && SHAssetManager::GetType(comp.GetToggledTexture()) == AssetType::TEXTURE)
{
material->SetProperty("data.textureIndex", comp.GetToggledTexture());
//SHLOG_INFO("SETTING DEFAULT TEXTURE")
}
}
}
}
void SHUISystem::UpdateButtonsRoutine::Execute(double dt) noexcept
{
@ -219,6 +385,34 @@ namespace SHADE
if (SHSceneManager::CheckNodeAndComponentsActive<SHButtonComponent>(comp.GetEID()))
system->UpdateButtonComponent(comp);
}
auto& toggleButtonDense = SHComponentManager::GetDense<SHToggleButtonComponent>();
for (auto& comp : toggleButtonDense)
{
if (SHSceneManager::CheckNodeAndComponentsActive<SHToggleButtonComponent>(comp.GetEID()))
system->UpdateToggleButtonComponent(comp);
}
}
SHVec2 SHUISystem::CanvasToScreenPoint(SHVec2& const canvasPoint, bool normalized) noexcept
{
SHVec2 result{canvasPoint};
auto cameraSystem = SHSystemManager::GetSystem<SHCameraSystem>();
SHVec2 camSize{ cameraSystem->GetCameraWidthHeight(0) };
//camSize.y *= -1.0f;
result.y *= -1.0f;
result += camSize * 0.5f;
if (normalized)
return result / camSize;
else
return result;
}
}//end namespace

View File

@ -3,14 +3,20 @@
#include "SH_API.h"
#include "ECS_Base/System/SHSystem.h"
#include "ECS_Base/System/SHSystemRoutine.h"
#include "SHUIComponent.h"
#include "SHButtonComponent.h"
#include "SHCanvasComponent.h"
#include "Scene/SHSceneGraph.h"
#include "Scene/SHSceneManager.h"
#include "Math/Vector/SHVec2.h"
namespace SHADE
{
class SHButtonComponent;
class SHUIComponent;
class SHToggleButtonComponent;
class SHSliderComponent;
class SHCanvasComponent;
class SH_API SHUISystem final: public SHSystem
{
public:
@ -64,8 +70,12 @@ namespace SHADE
private:
void UpdateUIComponent(SHUIComponent& comp) noexcept;
void UpdateButtonComponent(SHButtonComponent& comp) noexcept;
void UpdateToggleButtonComponent(SHToggleButtonComponent& comp) noexcept;
void UpdateCanvasComponent(SHCanvasComponent& comp) noexcept;
SHVec2 CanvasToScreenPoint(SHVec2& const canvasPoint, bool normalized) noexcept;
};

View File

@ -107,8 +107,7 @@ namespace SHADE
Vector3 Transform::Forward::get()
{
const SHVec3 DIRECTION = SHVec3::Rotate(-SHVec3::UnitZ, Convert::ToNative(GlobalRotation));
return Convert::ToCLI(DIRECTION);
return Vector3::Rotate(Vector3::Forward, GlobalRotation);
}
/*-----------------------------------------------------------------------------------*/

View File

@ -44,6 +44,10 @@ namespace SHADE
return false;
}
bool Application::IsEditor::get()
{
return SHSystemManager::GetSystem<SHEditor>() != nullptr;
}
int Application::WindowWidth::get()
{
return SHGraphicsSystemInterface::GetWindowWidth();
@ -65,7 +69,10 @@ namespace SHADE
/* Usage Functions */
/*---------------------------------------------------------------------------------*/
void Application::Quit()
{
if (!IsEditor)
{
SHGraphicsSystemInterface::CloseWindow();
}
}
}

View File

@ -43,6 +43,13 @@ namespace SHADE
bool get();
}
/// <summary>
/// True if the engine is running in the editor.
/// </summary>
static property bool IsEditor
{
bool get();
}
/// <summary>
/// Retrieves the designated width of the current window.
/// </summary>
static property int WindowWidth
@ -71,6 +78,7 @@ namespace SHADE
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Marks the application to stop at the end of the current frame.
/// If running in the editor, this function does nothing.
/// </summary>
static void Quit();
};

View File

@ -167,6 +167,10 @@ namespace SHADE
{
return Convert::ToCLI(SHVec3::Rotate(Convert::ToNative(vec), Convert::ToNative(axis), radians));
}
Vector3 Vector3::Rotate(Vector3 vec, Quaternion quat)
{
return Convert::ToCLI(SHVec3::Rotate(Convert::ToNative(vec), Convert::ToNative(quat)));
}
Vector3 Vector3::Min(Vector3 lhs, Vector3 rhs)
{
float lx = lhs.x, rx = rhs.x;

View File

@ -19,10 +19,17 @@ of DigiPen Institute of Technology is prohibited.
// Project Includes
#include "Vector2.hxx"
value struct Quaternion;
namespace SHADE
{
/*---------------------------------------------------------------------------------*/
/* Forward Declarations */
/*-------------------------------------------------------------------------- --- */
value struct Quaternion;
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
/*-------------------------------------------------------------------------- --- */
///<summary>
/// CLR version of SHADE Engine's Vector3 class that represents a 3-Dimensional Vector.
/// Designed to closely match Unity's Vector3 struct.
@ -308,6 +315,12 @@ namespace SHADE
/// <returns>The Vector3 that represents the rotated vector.</returns>
static Vector3 Rotate(Vector3 vec, Vector3 axis, float radians);
/// <summary>
/// Rotates a Vector3 using a Quaternion.
/// </summary>
/// <param name="vec">A Vector3 to rotate.</param>
/// <param name="quat">A Quaternion to rotate the vector with.</param>
static Vector3 Rotate(Vector3 vec, Quaternion quat);
/// <summary>
/// Computes and returns a Vector3 that is made from the smallest components of
/// the two specified Vector3s.
/// </summary>