From a57c4d0d733045c20df3f8c80bb6dca4b17f3874 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 27 Sep 2022 19:17:53 +0800 Subject: [PATCH] WIP Script Inspector --- SHADE_Engine/src/Editor/SHEditorUI.cpp | 421 +++++++++++++++++++++++++ SHADE_Engine/src/Editor/SHEditorUI.h | 312 ++++++++++++++++++ SHADE_Engine/src/Editor/SHEditorUI.hpp | 49 +++ SHADE_Managed/src/Editor/Editor.cxx | 207 ++++++++++++ SHADE_Managed/src/Editor/Editor.hxx | 65 ++++ 5 files changed, 1054 insertions(+) create mode 100644 SHADE_Engine/src/Editor/SHEditorUI.cpp create mode 100644 SHADE_Engine/src/Editor/SHEditorUI.h create mode 100644 SHADE_Engine/src/Editor/SHEditorUI.hpp create mode 100644 SHADE_Managed/src/Editor/Editor.cxx create mode 100644 SHADE_Managed/src/Editor/Editor.hxx diff --git a/SHADE_Engine/src/Editor/SHEditorUI.cpp b/SHADE_Engine/src/Editor/SHEditorUI.cpp new file mode 100644 index 00000000..cbeb164f --- /dev/null +++ b/SHADE_Engine/src/Editor/SHEditorUI.cpp @@ -0,0 +1,421 @@ +/************************************************************************************//*! +\file EditorUI.cpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 7, 2021 +\brief Contains the implementation of the EditorUI class. + +Copyright (C) 2021 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +// Precompiled Header +#include +// Primary Header +#include "Editor/EditorUI.h" +// External Dependencies +#include +// Project Headers +#include "Core/Resource/ResourceManager.h" +#include "Editor/ImGuiExtensions.h" +#include "Editor/Editor.h" + +namespace Pls +{ + /*---------------------------------------------------------------------------------*/ + /* ImGui Wrapper Functions - ID Stack */ + /*---------------------------------------------------------------------------------*/ + void EditorUI::PushID(const std::string& id) + { + ImGui::PushID(id.c_str()); + } + + void EditorUI::PushID(int id) + { + ImGui::PushID(id); + } + + void EditorUI::PopID() + { + ImGui::PopID(); + } + + /*---------------------------------------------------------------------------------*/ + /* ImGui Wrapper Functions - Indent */ + /*---------------------------------------------------------------------------------*/ + void EditorUI::Indent() + { + ImGui::Indent(); + } + void EditorUI::Unindent() + { + ImGui::Unindent(); + } + + /*---------------------------------------------------------------------------------*/ + /* ImGui Wrapper Functions - Organisers */ + /*---------------------------------------------------------------------------------*/ + bool EditorUI::CollapsingHeader(const std::string& title) + { + return ImGui::CollapsingHeader(title.c_str()); + } + + /*---------------------------------------------------------------------------------*/ + /* ImGui Wrapper Functions - Pop Ups */ + /*---------------------------------------------------------------------------------*/ + bool EditorUI::BeginPopup(const std::string& label) + { + return ImGui::BeginPopup(label.c_str()); + } + void EditorUI::EndPopup() + { + ImGui::EndPopup(); + } + void EditorUI::OpenPopup(const std::string& label) + { + ImGui::OpenPopup(label.c_str()); + } + + bool EditorUI::MenuItem(const std::string& label) + { + return ImGui::MenuItem(label.c_str()); + } + + /*---------------------------------------------------------------------------------*/ + /* ImGui Wrapper Functions - Widgets */ + /*---------------------------------------------------------------------------------*/ + void EditorUI::Text(const std::string& title) + { + ImGui::Text(title.c_str()); + } + bool EditorUI::SmallButton(const std::string& title) + { + return ImGui::SmallButton(title.c_str()); + } + bool EditorUI::Button(const std::string& title) + { + return ImGui::Button(title.c_str()); + } + bool EditorUI::InputCheckbox(const std::string& label, bool& value) + { + return ImGui::Checkbox(label.c_str(), &value); + } + bool EditorUI::InputInt(const std::string& label, int& value) + { + return ImGui::InputInt(label.c_str(), &value, + 1, 10, + ImGuiInputTextFlags_EnterReturnsTrue); + } + bool EditorUI::InputUnsignedInt(const std::string& label, unsigned int& value) + { + int signedVal = static_cast(value); + const bool CHANGED = InputInt(label, signedVal); + if (CHANGED) + { + signedVal = std::clamp(signedVal, 0, std::numeric_limits::max()); + value = static_cast(signedVal); + } + return CHANGED; + } + bool EditorUI::InputFloat(const std::string& label, float& value) + { + return ImGui::InputFloat(label.c_str(), &value, + 0.1f, 1.0f, "%.3f", + ImGuiInputTextFlags_EnterReturnsTrue); + } + bool EditorUI::InputDouble(const std::string& label, double& value) + { + return ImGui::InputDouble(label.c_str(), &value, + 0.1, 1.0, "%.3f", + ImGuiInputTextFlags_EnterReturnsTrue); + } + bool EditorUI::InputAngle(const std::string& label, double& value) + { + return ImGui::InputDouble(label.c_str(), &value, + 1.0, 45.0, "%.3f", + ImGuiInputTextFlags_EnterReturnsTrue); + } + + bool EditorUI::InputSlider(const std::string& label, double min, double max, double& value) + { + float val = static_cast(value); + const bool CHANGED = ImGui::SliderFloat(label.c_str(), &val, + static_cast(min), static_cast(max), "%.3f", + ImGuiInputTextFlags_EnterReturnsTrue); + + if (CHANGED) + { + value = val; + } + + + return CHANGED; + } + + bool EditorUI::InputVec2(const std::string& label, vec2& value) + { + float vec[2] = + { + static_cast(value.x), + static_cast(value.y) + }; + const bool CHANGED = ImGui::InputFloat2(label.c_str(), vec, "%.3f", + ImGuiInputTextFlags_EnterReturnsTrue); + if (CHANGED) + { + value.x = vec[0]; + value.y = vec[1]; + } + return CHANGED; + } + bool EditorUI::InputVec3(const std::string& label, vec3& value) + { + float vec[3] = + { + static_cast(value.x), + static_cast(value.y), + static_cast(value.z) + }; + const bool CHANGED = ImGui::InputFloat3(label.c_str(), vec, "%.3f", + ImGuiInputTextFlags_EnterReturnsTrue); + if (CHANGED) + { + value.x = vec[0]; + value.y = vec[1]; + value.z = vec[2]; + } + return CHANGED; + } + + bool EditorUI::InputSliderVec3(const std::string& label, double min, double max, vec3& value) + { + float vec[3] = + { + static_cast(value.x), + static_cast(value.y), + static_cast(value.z) + }; + const bool CHANGED = ImGui::SliderFloat3(label.c_str(), vec, + static_cast(min), static_cast(max), "%.3f", + ImGuiInputTextFlags_EnterReturnsTrue); + if (CHANGED) + { + value.x = vec[0]; + value.y = vec[1]; + value.z = vec[2]; + } + return CHANGED; + } + + bool EditorUI::InputColor(const std::string& label, GFX::Color& value) + { + float color[4] = + { + value.R, + value.G, + value.B, + value.A + }; + const bool CHANGED = ImGui::ColorEdit4(label.c_str(), color, + ImGuiColorEditFlags_AlphaBar | + ImGuiColorEditFlags_AlphaPreview | + ImGuiColorEditFlags_Float); + if (CHANGED) + { + value.R = color[0]; + value.G = color[1]; + value.B = color[2]; + value.A = std::clamp(color[3], 0.f, 1.f); + } + return CHANGED; + } + bool EditorUI::InputTextField(const std::string& label, std::string& value) + { + std::array buffer = { '\0' }; + strcpy_s(buffer.data(), TEXT_FIELD_MAX_LENGTH, value.c_str()); + const bool CHANGED = ImGui::InputText(label.c_str(), &buffer[0], TEXT_FIELD_MAX_LENGTH); + if (CHANGED) + { + value = std::string(buffer.data(), buffer.data() + TEXT_FIELD_MAX_LENGTH); + } + return CHANGED; + } + + bool EditorUI::InputEnumCombo(const std::string& label, int& v, const std::vector& enumNames) + { + + // Clamp input value + const std::string& INITIAL_NAME = v >= static_cast(enumNames.size()) ? "Unknown" : enumNames[v]; + bool b = false; + if (ImGui::BeginCombo(label.c_str(), INITIAL_NAME.c_str(), ImGuiComboFlags_None)) + { + for (int i = 0; i < enumNames.size(); ++i) + { + const bool IS_SELECTED = v == i; + if (ImGui::Selectable(enumNames[i].c_str(), IS_SELECTED)) + { + v = i; + b = true; + } + if (IS_SELECTED) + { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + return b; + } + + bool EditorUI::InputTextureDropDown(const std::string& label, Resource::Snowflake& v) + { + static std::vector> resources; + + // Register texture popup + bool hasChanges = false; + if (ImGui::BeginPopup("Texture Menu")) + { + if (ImGui::Button("None")) + { + v = Resource::INVALID_SNOWFLAKE; + ImGui::CloseCurrentPopup(); + hasChanges = true; + } + for (auto& rscWrapper : resources) + { + Resource& rsc = rscWrapper.get(); + + ImGui::PushID(static_cast(rsc.GetID())); + + const GFX::Texture* TEX = rsc.LoadTexture(); + if (TEX == nullptr) + { + continue; + } + const ImTextureID TEX_ID = reinterpret_cast(static_cast(TEX->GetHandle())); + const std::string NAME = rsc.GetPath().filename().string(); + const std::string FP = rsc.GetPath().string(); + if (ImGui::ImageButtonWithText(TEX_ID, NAME.c_str())) + { + v = rsc.GetID(); + ImGui::CloseCurrentPopup(); + hasChanges = true; + } + ImGui::TextDisabled(FP.c_str()); + + ImGui::PopID(); + } + ImGui::EndPopup(); + } + + // Get texture ID of resource + ImTextureID texID = 0; + std::string texName = "None"; + Resource* rsc = ResourceManager::Find(v); + if (rsc) + { + // File + if (rsc->IsType()) + { + GFX::Texture* tex = rsc->LoadTexture(); + if (tex) + { + texID = reinterpret_cast(static_cast(tex->GetHandle())); + } + } + else + { + // TODO (Clarence): replace with missing texture image + texID = Editor::Editor::GetInternalTexture("scene.png"); + } + texName = rsc->GetPath().filename().string(); + } + + // Show texture selection menu + if (ImGui::ImageButtonWithText(texID, texName.c_str(), label.c_str())) + { + resources = ResourceManager::FindAllOfType(); + ImGui::OpenPopup("Texture Menu"); + } + + return hasChanges; + } + bool EditorUI::InputFontDropDown(const char* label, Resource::Snowflake& v) + { + static std::vector> resources; + + // Register texture popup + bool hasChanges = false; + if (ImGui::BeginPopup("Font Menu")) + { + if (ImGui::Button("None")) + { + v = Resource::INVALID_SNOWFLAKE; + ImGui::CloseCurrentPopup(); + hasChanges = true; + } + for (auto& rscWrapper : resources) + { + Resource& rsc = rscWrapper.get(); + + ImGui::PushID(static_cast(rsc.GetID())); + + const GFX::Font* FONT = rsc.LoadFont(); + if (FONT == nullptr) + { + continue; + } + const std::string NAME = rsc.GetPath().filename().string(); + const std::string FP = rsc.GetPath().string(); + if (ImGui::Button(NAME.c_str())) + { + v = rsc.GetID(); + ImGui::CloseCurrentPopup(); + hasChanges = true; + } + ImGui::TextDisabled(FP.c_str()); + + ImGui::PopID(); + } + ImGui::EndPopup(); + } + + // Get font ID of resource + ImTextureID fontID = 0; + std::string fontName = "None"; + Resource* rsc = ResourceManager::Find(v); + + if (rsc) + { + // File + if (rsc) + { + GFX::Font* font = rsc->LoadFont(); + if (font) + { + fontID = reinterpret_cast(static_cast(font->GetID())); + if (font->CreatedThisFrame()) + { + font->OnCreate() = false; + hasChanges = true; + } + } + } + else + { + // TODO (Clarence): replace with missing texture image + fontID = Editor::Editor::GetInternalFont("selawk.tff"); + } + fontName = rsc->GetPath().filename().string(); + } + + // Show font selection menu + if (ImGui::ButtonWithText(fontName.c_str(), label)) + { + resources = ResourceManager::FindAllOfType(); + ImGui::OpenPopup("Font Menu"); + } + + return hasChanges; + } +} // namespace PlushieEngine diff --git a/SHADE_Engine/src/Editor/SHEditorUI.h b/SHADE_Engine/src/Editor/SHEditorUI.h new file mode 100644 index 00000000..ce9d8608 --- /dev/null +++ b/SHADE_Engine/src/Editor/SHEditorUI.h @@ -0,0 +1,312 @@ +/************************************************************************************//*! +\file EditorUI.h +\author Tng Kah Wei, kahwei.tng, 390009620 (50%) +\par email: kahwei.tng\@digipen.edu +\author Tay Yan Chong Clarence, t.yanchongclarence, 620008720 (50%) +\par email: t.yanchongclarence\@digipen.edu +\date Nov 7, 2021 +\brief Defines a class that encapsulates the running of a Plushie Engine + application. + +Copyright (C) 2021 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once +// Platform Defines +#include "PlushieDefines.h" +// Standard Library +#include // std::function +#include // std::string + +#include "Core/Resource/Resource.h" +#include "Graphics/Color.h" +#include "Math/Vector2.h" +#include "Math/Vector3.h" + +namespace Pls +{ + /// + /// Static class that contains useful functions for Editor UI using ImGui. + /// + class PLS_API EditorUI final + { + public: + /*-----------------------------------------------------------------------------*/ + /* Constants */ + /*-----------------------------------------------------------------------------*/ + /// + /// Maximum length of a string supported by InputTextField() + /// + static constexpr size_t TEXT_FIELD_MAX_LENGTH = 256; + + /*-----------------------------------------------------------------------------*/ + /* ImGui Wrapper Functions - ID Stack */ + /*-----------------------------------------------------------------------------*/ + /// + /// Marks the start of a stack of ImGui widgets with the specified id. + ///
+ /// Wraps up ImGui::PushID(). + ///
+ /// String-based ID. + static void PushID(const std::string& id); + /// + /// Marks the start of a stack of ImGui widgets with the specified id. + ///
+ /// Wraps up ImGui::PushID(). + ///
+ /// Integer-based ID. + static void PushID(int id); + /// + /// Marks the end of a stack of ImGui widgets from the last PushID() call. + ///
+ /// Wraps up ImGui::PopID(). + ///
+ static void PopID(); + + /*-----------------------------------------------------------------------------*/ + /* ImGui Wrapper Functions - Indent */ + /*-----------------------------------------------------------------------------*/ + /// + /// Indents the widgets rendered after this call. + ///
+ /// Wraps up ImGui::Indent(). + ///
+ static void Indent(); + /// + /// Unindents the widgets rendered after this call. + ///
+ /// Wraps up ImGui::Unindent(). + ///
+ static void Unindent(); + + /*-----------------------------------------------------------------------------*/ + /* ImGui Wrapper Functions - Organisers */ + /*-----------------------------------------------------------------------------*/ + /// + /// Creates a collapsing title header. + ///
+ /// Wraps up ImGui::CollapsingHeader(). + ///
+ /// Label for the header. + /// True if the header is open, false otherwise. + static bool CollapsingHeader(const std::string& title); + + /*-----------------------------------------------------------------------------*/ + /* ImGui Wrapper Functions - Pop Ups */ + /*-----------------------------------------------------------------------------*/ + /// + /// Marks the start of a definition of a mini pop up that can show options. + ///
+ /// Wraps up ImGui::BeginPopup(). + ///
+ /// Label used to identify this widget. + /// Whether or not the pop up is open. + static bool BeginPopup(const std::string& label); + /// + /// Marks the end of a definition of a mini pop up that can show options. + ///
+ /// Wraps up ImGui::EndPopup(). + ///
+ static void EndPopup(); + /// + /// Opens the popup that was defined with the specified label. + ///
+ /// Wraps up ImGui::OpenPopup(). + ///
+ static void OpenPopup(const std::string& label); + /// + /// Creates a menu item in the list of items for a mini popup. + ///
+ /// Wraps up ImGui::MenuItem(). + ///
+ /// Label used to identify this widget. + /// Whether or not the menu item was selected. + static bool MenuItem(const std::string& label); + + /*-----------------------------------------------------------------------------*/ + /* ImGui Wrapper Functions - Widgets */ + /*-----------------------------------------------------------------------------*/ + /// + /// Creates a visual text widget. + ///
+ /// Wraps up ImGui::Text(). + ///
+ /// Text to display. + static void Text(const std::string& title); + /// + /// Creates a small inline button widget. + ///
+ /// Wraps up ImGui::SmallButton(). + ///
+ /// Text to display. + /// True if button was pressed. + static bool SmallButton(const std::string& title); + /// + /// Creates a inline button widget. + ///
+ /// Wraps up ImGui::Button(). + ///
+ /// Text to display. + /// True if button was pressed. + static bool Button(const std::string& title); + /// + /// Creates a checkbox widget for boolean input. + ///
+ /// Wraps up ImGui::Checkbox(). + ///
+ /// Label used to identify this widget. + /// Reference to the variable to store the result. + /// True if the value was changed. + static bool InputCheckbox(const std::string& label, bool& value); + /// + /// Creates a integer field widget for integer input. + ///
+ /// Wraps up ImGui::InputInt(). + ///
+ /// Label used to identify this widget. + /// Reference to the variable to store the result. + /// True if the value was changed. + static bool InputInt(const std::string& label, int& value); + /// + /// Creates a integer field widget for unsigned integer input. + ///
+ /// Wraps up ImGui::InputInt() with an additional clamping of values. + ///
+ /// Note: As a result, the range of this function limits it to the maximum + /// value of a 32-bit signed integer instead of a 32-bit unsigned integer. + ///
+ /// Label used to identify this widget. + /// Reference to the variable to store the result. + /// True if the value was changed. + static bool InputUnsignedInt(const std::string& label, unsigned int& value); + /// + /// Creates a decimal field widget for single precision float input. + ///
+ /// Wraps up ImGui::InputFloat(). + ///
+ /// Label used to identify this widget. + /// Reference to the variable to store the result. + /// True if the value was changed. + static bool InputFloat(const std::string& label, float& value); + /// + /// Creates a decimal field widget for double precision float input. + ///
+ /// Wraps up ImGui::InputDouble(). + ///
+ /// Label used to identify this widget. + /// Reference to the variable to store the result. + /// True if the value was changed. + static bool InputDouble(const std::string& label, double& value); + /// + /// Creates a decimal field widget for double input with increments of higher + /// steps meant for angle variables. + ///
+ /// Wraps up ImGui::InputDouble(). + ///
+ /// Label used to identify this widget. + /// Reference to the variable to store the result. + /// True if the value was changed. + static bool InputAngle(const std::string& label, double& value); + /// + /// Creates a double slider field widget for double input. + ///
+ /// Wraps up ImGui::InputSliderFloat(). + ///
+ /// Label used to identify this widget. + /// Minimum value of the slider. + /// Maximum value of the slider. + /// Reference to the variable to store the result. + /// True if the value was changed. + static bool InputSlider(const std::string& label, double min, double max, double& value); + /// + /// Creates a 2x double field widget for Vector2 input. + ///
+ /// Wraps up ImGui::InputFloat2(). + ///
+ /// Label used to identify this widget. + /// Reference to the variable to store the result. + /// True if the value was changed. + static bool InputVec2(const std::string& label, vec2& value); + /// + /// Creates a 3x double field widget for Vector3 input. + ///
+ /// Wraps up ImGui::InputFloat3(). + ///
+ /// Label used to identify this widget. + /// Reference to the variable to store the result. + /// True if the value was changed. + static bool InputVec3(const std::string& label, vec3& value); + /// + /// Creates a 3x double slider field widget for Vector3 input. + ///
+ /// Wraps up ImGui::InputSliderFloat3(). + ///
+ /// Label used to identify this widget. + /// Minimum value of the slider. + /// Maximum value of the slider. + /// Reference to the variable to store the result. + /// True if the value was changed. + static bool InputSliderVec3(const std::string& label, double min, double max, vec3& value); + /// + /// Creates a colour field widget for Color input. + ///
+ /// Wraps up ImGui::ColorEdit4(). + ///
+ /// Label used to identify this widget. + /// Reference to the variable to store the result. + /// True if the value was changed. + static bool InputColor(const std::string& label, GFX::Color& value); + /// + /// Creates a text field widget for string input. + ///
+ /// Wraps up ImGui::InputText(). + ///
+ /// Label used to identify this widget. + /// Reference to the variable to store the result. + /// True if the value was changed. + static bool InputTextField(const std::string& label, std::string& value); + /// + /// Creates a combo box for enumeration input. + /// + /// The type of enum to input. + /// The name of the input. + /// The reference to the value to modify. + /// The maximum value of the enum. + /// + /// Conversion function from the type of enum + /// to C-style string. + /// Whether the value was modified. + template + static bool InputEnumCombo(const std::string& label, Enum& v, int maxVal, std::function toStrFn); + /// + /// Creates a combo box for enumeration input using a specified list of names. + /// + /// The name of the input. + /// The reference to the value to modify. + /// Vector of names for each enumeration value. + /// Whether the value was modified. + static bool InputEnumCombo(const std::string& label, int& v, const std::vector& enumNames); + /// + /// Creates a drop down widget for texture selection. + /// + /// The name of the input. + /// The reference to the value to modify. + /// Whether the value was modified. + static bool InputTextureDropDown(const std::string& label, Resource::Snowflake& v); + /// + /// Creates a drop down widget for font selection. + /// + /// The name of the input. + /// The reference to the value to modify. + /// Whether the value was modified. + static bool InputFontDropDown(const char* label, Resource::Snowflake& v); + + + private: + // Prevent instantiation of this static class + EditorUI() = delete; + }; +} // namespace PlushieEngine + +#include "EditorUI.hpp" \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/SHEditorUI.hpp b/SHADE_Engine/src/Editor/SHEditorUI.hpp new file mode 100644 index 00000000..d7a3eae7 --- /dev/null +++ b/SHADE_Engine/src/Editor/SHEditorUI.hpp @@ -0,0 +1,49 @@ +/************************************************************************************//*! +\file EditorUI.hpp +\author Tay Yan Chong Clarence, t.yanchongclarence, 620008720 +\par email: t.yanchongclarence\@digipen.edu +\date Nov 8, 2021 +\brief Contains the implementation of editor inspector template functions. + +Copyright (C) 2021 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +// Primary Header +#include "EditorUI.h" + +namespace Pls +{ + /*---------------------------------------------------------------------------------*/ + /* ImGui Wrapper Functions - Widgets */ + /*---------------------------------------------------------------------------------*/ + template + inline bool EditorUI::InputEnumCombo(const std::string& label, Enum& v, int maxVal, std::function toStrFn) + { + std::vector values; + for (int i = 0; i <= maxVal; ++i) + { + values.emplace_back(static_cast(i)); + } + bool b = false; + if (ImGui::BeginCombo(label.c_str(), toStrFn(v), ImGuiComboFlags_None)) + { + for (int i = 0; i <= maxVal; ++i) + { + const auto VALUE = values[i]; + const bool IS_SELECTED = v == VALUE; + if (ImGui::Selectable(toStrFn(VALUE), IS_SELECTED)) + { + v = VALUE; + b = true; + } + if (IS_SELECTED) + { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + return b; + } +} // namespace PlushieEngine diff --git a/SHADE_Managed/src/Editor/Editor.cxx b/SHADE_Managed/src/Editor/Editor.cxx new file mode 100644 index 00000000..d6b02c8c --- /dev/null +++ b/SHADE_Managed/src/Editor/Editor.cxx @@ -0,0 +1,207 @@ +/************************************************************************************//*! +\file Editor.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Sep 27, 2022 +\brief Contains the definition of the functions for the ScriptStore managed + static class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "Editor/Editor.hxx" +// External Dependencies +#include "Editor/SHEditorUI::.h" +// Project Headers +#include "Components/Component.hxx" +#include "Scripts/ScriptStore.hxx" +#include "Utility/Convert.hxx" +#include "Utility/Debug.hxx" +#include "Serialisation/ReflectionUtilities.hxx" + +// Using Directives +using namespace System::Collections::Generic; + +/*-------------------------------------------------------------------------------------*/ +/* Macro Functions */ +/*-------------------------------------------------------------------------------------*/ +/// +/// Macro expansion that is used in renderFieldInInspector() to check the type of a field +/// named "field" against the specified type and if it matches, retrieves the value of +/// that field from an object named "object" and pass it into the specified SHEditorUI:: +/// function named "FUNC" by casting it into the NATIVE_TYPE specified. +///
+/// This only works for primitive types that have the same types for managed and native. +///
+/// The managed type of the object to edit. +/// The native type of the object to edit. +/// The SHEditorUI:: function to use for editing. +#define RENDER_FIELD(MANAGED_TYPE, NATIVE_TYPE, FUNC) \ +(field->FieldType == MANAGED_TYPE::typeid) \ +{ \ + NATIVE_TYPE val = safe_cast(field->GetValue(object)); \ + if (SHEditorUI::::FUNC(Convert::ToNative(field->Name), val)) \ + { \ + field->SetValue(object, val); \ + } \ +} \ +/// +/// Macro expansion that is used in renderFieldInInspector() to check the type of a field +/// named "field" against the specified type and if it matches, retrieves the value of +/// that field from an object named "object" and pass it into the specified SHEditorUI:: +/// function named "FUNC" by casting it into the NATIVE_TYPE specified. +///
+/// This only works for types that have an implementation of Convert::ToNative and +/// Convert::ToCLI. +///
+/// The managed type of the object to edit. +/// The native type of the object to edit. +/// The SHEditorUI:: function to use for editing. +#define RENDER_FIELD_CASTED(MANAGED_TYPE, NATIVE_TYPE, FUNC) \ +(field->FieldType == MANAGED_TYPE::typeid) \ +{ \ + NATIVE_TYPE val = Convert::ToNative(safe_cast(field->GetValue(object))); \ + if (SHEditorUI::::FUNC(Convert::ToNative(field->Name), val)) \ + { \ + field->SetValue(object, Convert::ToCLI(val)); \ + } \ +} \ + +/*-------------------------------------------------------------------------------------*/ +/* Function Definitions */ +/*-------------------------------------------------------------------------------------*/ +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Script Rendering Functions */ + /*---------------------------------------------------------------------------------*/ + void Editor::RenderScriptsInInspector(Entity entity) + { + SAFE_NATIVE_CALL_BEGIN + // Get scripts + IEnumerable^ scripts = ScriptStore::GetAllScripts(entity); + + // Skip if no scripts + if (scripts != nullptr) + { + // Display each script if any + int index = 0; + for each (Script^ script in scripts) + { + renderScriptInInspector(entity, script, index++); + } + } + + // Render Add Script + //RenderScriptAddButton(entity); + SAFE_NATIVE_CALL_END_N("SHADE_Managed.Editor.RenderScriptsInInspector") + } + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + void Editor::renderScriptInInspector(Entity entity, Script^ script, int index) + { + // Constants + const std::string LABEL = Convert::ToNative(script->GetType()->Name); + const char* CONTEXT_MENU_ID = "scriptContextMenu"; + + // Header + SHEditorUI::::PushID(index); + if (SHEditorUI::::CollapsingHeader(LABEL)) + { + SHEditorUI::::PushID(LABEL); + SHEditorUI::::Indent(); + { + // Define context menu + if (SHEditorUI::::BeginPopup(CONTEXT_MENU_ID)) + { + if (SHEditorUI::::MenuItem("Remove Script")) + { + // Mark script for removal + ScriptStore::RemoveScript(entity, script); + } + SHEditorUI::::EndPopup(); + } + + // Context menu button + if (SHEditorUI::::SmallButton("...")) + { + SHEditorUI::::OpenPopup(CONTEXT_MENU_ID); + } + + // Go through all fields and output them + auto fields = ReflectionUtilities::GetInstanceFields(script); + int id = 0; + for each (auto field in fields) + { + // Ignore non-serialisable fields + if (!ReflectionUtilities::FieldIsSerialisable(field)) + continue; + + // Render the input field for this field + SHEditorUI::::PushID(LABEL + std::to_string(id++)); + renderFieldInInspector(field, script); + SHEditorUI::::PopID(); + } + + } + SHEditorUI::::Unindent(); + SHEditorUI::::PopID(); + } + SHEditorUI::::PopID(); + } + void Editor::renderFieldInInspector(Reflection::FieldInfo^ field, Object^ object) + { + if RENDER_FIELD (Int16, int, InputInt) + else if RENDER_FIELD (Int32, int, InputInt) + else if RENDER_FIELD (Int64, int, InputInt) + else if RENDER_FIELD (UInt16, unsigned int, InputUnsignedInt) + else if RENDER_FIELD (UInt32, unsigned int, InputUnsignedInt) + else if RENDER_FIELD (UInt64, unsigned int, InputUnsignedInt) + else if RENDER_FIELD (Byte, int, InputInt) + else if RENDER_FIELD (bool, bool, InputCheckbox) + else if RENDER_FIELD (float, float, InputFloat) + else if RENDER_FIELD (double, double, InputDouble) + else if (field->FieldType->IsSubclassOf(Enum::typeid)) + { + // Get all the names of the enums + const array^ ENUM_NAMES = field->FieldType->GetEnumNames(); + std::vector nativeEnumNames; + for each (String^ str in ENUM_NAMES) + { + nativeEnumNames.emplace_back(Convert::ToNative(str)); + } + + int val = safe_cast(field->GetValue(object)); + if (SHEditorUI::::InputEnumCombo(Convert::ToNative(field->Name), val, nativeEnumNames)) + { + field->SetValue(object, val); + } + } + else if RENDER_FIELD_CASTED(Vector2, vec2, InputVec2) + else if RENDER_FIELD_CASTED(Vector3, vec3, InputVec3) + else if (field->FieldType == String::typeid) + { + // Prevent issues where String^ is null due to being empty + String^ stringVal = safe_cast(field->GetValue(object)); + if (stringVal == nullptr) + { + stringVal = ""; + } + + // Actual Field + std::string val = Convert::ToNative(stringVal); + if (SHEditorUI::::InputTextField(Convert::ToNative(field->Name), val)) + { + field->SetValue(object, Convert::ToCLI(val)); + } + } + } +} diff --git a/SHADE_Managed/src/Editor/Editor.hxx b/SHADE_Managed/src/Editor/Editor.hxx new file mode 100644 index 00000000..b85e9203 --- /dev/null +++ b/SHADE_Managed/src/Editor/Editor.hxx @@ -0,0 +1,65 @@ +/************************************************************************************//*! +\file Editor.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Sep 27, 2022 +\brief Contains the definition of the managed Editor static class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +// Project Includes +#include "Engine/Entity.hxx" +#include "Scripts/Script.hxx" + +namespace SHADE +{ + /// + /// Static class for Editor-related functions + /// + public ref class Editor abstract sealed + { + public: + /*-----------------------------------------------------------------------------*/ + /* Script Rendering Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Renders the set of attached Scripts for the specified Entity into the + /// inspector. + ///
+ /// This function is meant for consumption from native code in the inspector + /// rendering code. + ///
+ /// The Entity to render the Scripts of. + static void RenderScriptsInInspector(Entity entity); + + private: + /*-----------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Renders a single specified Script's inspector. + /// + /// The Entity to render the Scripts of. + /// The Script to render the inspector for. + /// + /// Indices used internally to differentiate each rendered Script + /// inspector. This is required to open and close each Script's inspector + /// independently from each other. + /// + static void renderScriptInInspector(Entity entity, Script^ script, int index); + /// + /// Renders a field specified into the inspector. + /// + /// The field to render. + /// + /// The object that contains the data of the field to render. + /// + static void renderFieldInInspector(System::Reflection::FieldInfo^ field, Object^ object); + }; +}