Merge branch 'main' into SP3-4-Editor
This commit is contained in:
commit
66dbd7df93
|
@ -1,5 +1,6 @@
|
|||
using SHADE;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class RaccoonShowcase : Script
|
||||
{
|
||||
|
@ -17,6 +18,11 @@ public class RaccoonShowcase : Script
|
|||
private double rotation = 0.0;
|
||||
private Vector3 scale = Vector3.Zero;
|
||||
private double originalScale = 1.0f;
|
||||
[Tooltip("Sample list of Vector3s.")]
|
||||
public List<Vector3> vecList = new List<Vector3>(new Vector3[] { new Vector3(1, 2, 3), new Vector3(4, 5, 6) });
|
||||
[Range(-5, 5)]
|
||||
public List<int> intList = new List<int>(new int[] { 2, 8, 2, 6, 8, 0, 1 });
|
||||
public List<Light.Type> enumList = new List<Light.Type>(new Light.Type[] { Light.Type.Point, Light.Type.Directional, Light.Type.Ambient });
|
||||
public RaccoonShowcase(GameObject gameObj) : base(gameObj) {}
|
||||
|
||||
protected override void awake()
|
||||
|
|
|
@ -7,13 +7,15 @@
|
|||
#include "ECS_Base/Managers/SHComponentManager.h"
|
||||
#include "Math/Transform/SHTransformComponent.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "Scene/SHSceneManager.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
void SHCameraSystem::UpdateEditorCamera(double dt) noexcept
|
||||
{
|
||||
|
||||
|
||||
|
||||
auto& camera = editorCamera;
|
||||
SHVec3 view, right, UP;
|
||||
GetCameraAxis(camera, view, right, UP);
|
||||
|
@ -62,62 +64,50 @@ namespace SHADE
|
|||
|
||||
UpdateCameraComponent(editorCamera);
|
||||
|
||||
}
|
||||
void SHCameraSystem::EditorCameraUpdate::Execute(double dt) noexcept
|
||||
if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::LEFT_ALT))
|
||||
{
|
||||
SHCameraSystem* system = static_cast<SHCameraSystem*>(GetSystem());
|
||||
auto& camera = system->editorCamera;
|
||||
SHVec3 view, right, UP;
|
||||
system->GetCameraAxis(camera, view, right, UP);
|
||||
UpdateEditorArm(dt, true, SHVec3{ 0.0f });
|
||||
}
|
||||
else
|
||||
UpdateEditorArm(dt, false, SHVec3{ 0.0f });
|
||||
|
||||
if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::A))
|
||||
{
|
||||
//std::cout << "Camera movement: "<<right.x<<", " << right.y << std::endl;
|
||||
camera.position -= right * dt * camera.movementSpeed;
|
||||
camera.dirtyView = true;
|
||||
}
|
||||
if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::D))
|
||||
|
||||
void SHCameraSystem::UpdateEditorArm(double dt,bool active ,SHVec3 const& targetPos) noexcept
|
||||
{
|
||||
camera.position += right * dt * camera.movementSpeed;
|
||||
camera.dirtyView = true;
|
||||
if (active == false)
|
||||
{
|
||||
editorCameraArm.offset = SHVec3{0.0f};
|
||||
return;
|
||||
}
|
||||
if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::W))
|
||||
{
|
||||
camera.position += view * dt * camera.movementSpeed;
|
||||
camera.dirtyView = true;
|
||||
}
|
||||
if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::S))
|
||||
{
|
||||
camera.position -= view * dt * camera.movementSpeed;
|
||||
camera.dirtyView = true;
|
||||
}
|
||||
if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::Q))
|
||||
{
|
||||
camera.position += UP * dt * camera.movementSpeed;
|
||||
camera.dirtyView = true;
|
||||
}
|
||||
if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::E))
|
||||
{
|
||||
camera.position -= UP * dt * camera.movementSpeed;
|
||||
camera.dirtyView = true;
|
||||
}
|
||||
if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::RMB))
|
||||
{
|
||||
|
||||
editorCamera.SetPosition(targetPos);
|
||||
double mouseX, mouseY;
|
||||
SHInputManager::GetMouseVelocity(&mouseX,&mouseY);
|
||||
SHInputManager::GetMouseVelocity(&mouseX, &mouseY);
|
||||
|
||||
//std::cout << camera.yaw << std::endl;
|
||||
editorCameraArm.pitch -= mouseY * dt * editorCamera.turnSpeed.x;
|
||||
editorCameraArm.yaw -= mouseX * dt * editorCamera.turnSpeed.y;
|
||||
|
||||
constexpr float pitchClamp = 85.0f;
|
||||
|
||||
if (editorCameraArm.pitch > pitchClamp)
|
||||
editorCameraArm.pitch = pitchClamp;
|
||||
if (editorCameraArm.pitch < -pitchClamp)
|
||||
editorCameraArm.pitch = -pitchClamp;
|
||||
|
||||
editorCameraArm.armLength -= SHInputManager::GetMouseWheelVerticalDelta() * dt;
|
||||
|
||||
if (editorCameraArm.armLength < 1.0f)
|
||||
editorCameraArm.armLength = 1.0f;
|
||||
|
||||
UpdatePivotArmComponent(editorCameraArm);
|
||||
|
||||
editorCamera.offset = editorCameraArm.GetOffset();
|
||||
|
||||
CameraLookAt(editorCamera, targetPos);
|
||||
|
||||
camera.pitch -= mouseY * dt * camera.turnSpeed.x;
|
||||
camera.yaw -= mouseX * dt * camera.turnSpeed.y;
|
||||
camera.dirtyView = true;
|
||||
}
|
||||
|
||||
//std::cout << "Camera position: " << camera.position.x << " " << camera.position.y << std::endl;
|
||||
system->UpdateCameraComponent(system->editorCamera);
|
||||
|
||||
system->DecomposeViewMatrix(camera.viewMatrix, camera.pitch, camera.yaw, camera.roll, camera.position);
|
||||
}
|
||||
|
||||
void SHCameraSystem::Init(void)
|
||||
{
|
||||
|
@ -164,6 +154,9 @@ namespace SHADE
|
|||
|
||||
void SHCameraSystem::UpdateCameraComponent(SHCameraComponent& camera) noexcept
|
||||
{
|
||||
if (camera.isActive == false)
|
||||
return;
|
||||
|
||||
if (SHComponentManager::HasComponent<SHTransformComponent>(camera.GetEID()) == true && &camera != &editorCamera)
|
||||
{
|
||||
auto transform = SHComponentManager::GetComponent<SHTransformComponent>(camera.GetEID());
|
||||
|
@ -183,11 +176,17 @@ namespace SHADE
|
|||
if (SHComponentManager::HasComponent<SHCameraArmComponent>(camera.GetEID()))
|
||||
{
|
||||
auto arm = SHComponentManager::GetComponent<SHCameraArmComponent>(camera.GetEID());
|
||||
if (arm->isActive == true)
|
||||
{
|
||||
camera.offset = arm->GetOffset();
|
||||
if(arm->lookAtCameraOrigin)
|
||||
if (arm->lookAtCameraOrigin)
|
||||
CameraLookAt(camera, camera.position);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
SHVec3 view, right, UP;
|
||||
|
||||
|
||||
|
@ -287,11 +286,13 @@ namespace SHADE
|
|||
|
||||
for (auto& pivot : pivotDense)
|
||||
{
|
||||
if(SHSceneManager::CheckNodeAndComponentsActive<SHCameraArmComponent>(pivot.GetEID()))
|
||||
system->UpdatePivotArmComponent(pivot);
|
||||
}
|
||||
|
||||
for (auto& cam : dense)
|
||||
{
|
||||
if (SHSceneManager::CheckNodeAndComponentsActive<SHCameraComponent>(cam.GetEID()))
|
||||
system->UpdateCameraComponent(cam);
|
||||
}
|
||||
for (auto& handle : system->directorHandleList)
|
||||
|
@ -399,7 +400,7 @@ namespace SHADE
|
|||
SHVec3 up = { 0.0f,1.0f,0.0f };
|
||||
|
||||
|
||||
////SHVec3::RotateZ(target, SHMath::DegreesToRadians(camera.roll));
|
||||
//SHVec3::RotateZ(target, SHMath::DegreesToRadians(camera.roll));
|
||||
|
||||
//target = SHVec3::Normalise(target);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "ECS_Base/System/SHSystemRoutine.h"
|
||||
#include "Resource/SHResourceLibrary.h"
|
||||
#include "SHCameraDirector.h"
|
||||
#include "SHCameraArmComponent.h"
|
||||
#include "SH_API.h"
|
||||
|
||||
namespace SHADE
|
||||
|
@ -18,6 +19,7 @@ namespace SHADE
|
|||
//A camera component that represents editor camera.
|
||||
//This is not tied to any entity. Hence this EID should not be used.
|
||||
SHCameraComponent editorCamera;
|
||||
SHCameraArmComponent editorCameraArm;
|
||||
|
||||
SHResourceLibrary<SHCameraDirector> directorLibrary;
|
||||
std::vector<DirectorHandle> directorHandleList;
|
||||
|
@ -34,14 +36,7 @@ namespace SHADE
|
|||
void Init (void);
|
||||
void Exit (void);
|
||||
|
||||
class SH_API EditorCameraUpdate final : public SHSystemRoutine
|
||||
{
|
||||
public:
|
||||
|
||||
EditorCameraUpdate() : SHSystemRoutine("Editor Camera Update", true) { };
|
||||
virtual void Execute(double dt) noexcept override final;
|
||||
|
||||
};
|
||||
friend class EditorCameraUpdate;
|
||||
|
||||
class SH_API CameraSystemUpdate final: public SHSystemRoutine
|
||||
|
@ -63,6 +58,7 @@ namespace SHADE
|
|||
void DecomposeViewMatrix(SHMatrix const& matrix, float& pitch, float& yaw, float& roll, SHVec3& pos) noexcept;
|
||||
void SetCameraViewMatrix(SHCameraComponent& camera, SHMatrix const& viewMatrix) noexcept;
|
||||
void CameraLookAt(SHCameraComponent& camera, SHVec3 target) noexcept;
|
||||
void UpdateEditorArm(double dt,bool active ,SHVec3 const& targetPos) noexcept;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -53,9 +53,12 @@ namespace SHADE
|
|||
/*-----------------------------------------------------------------------------------*/
|
||||
/* ImGui Wrapper Functions - Organizers */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
bool SHEditorUI::CollapsingHeader(const std::string& title)
|
||||
bool SHEditorUI::CollapsingHeader(const std::string& title, bool* isHovered)
|
||||
{
|
||||
return ImGui::CollapsingHeader(title.c_str(), ImGuiTreeNodeFlags_DefaultOpen);
|
||||
const bool OPENED = ImGui::CollapsingHeader(title.c_str(), ImGuiTreeNodeFlags_DefaultOpen);
|
||||
if (isHovered)
|
||||
*isHovered = ImGui::IsItemHovered();
|
||||
return OPENED;
|
||||
}
|
||||
|
||||
void SHEditorUI::SameLine()
|
||||
|
@ -165,8 +168,10 @@ namespace SHADE
|
|||
if (isHovered)
|
||||
*isHovered = ImGui::IsItemHovered();
|
||||
ImGui::SameLine();
|
||||
return ImGui::InputInt("##", &value,
|
||||
1, 10,
|
||||
return ImGui::DragInt("##", &value, 0.001f,
|
||||
std::numeric_limits<int>::min(),
|
||||
std::numeric_limits<int>::max(),
|
||||
"%d",
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
}
|
||||
bool SHEditorUI::InputUnsignedInt(const std::string& label, unsigned int& value, bool* isHovered)
|
||||
|
@ -190,31 +195,22 @@ namespace SHADE
|
|||
if (isHovered)
|
||||
*isHovered = ImGui::IsItemHovered();
|
||||
ImGui::SameLine();
|
||||
return ImGui::InputFloat("##", &value,
|
||||
0.1f, 1.0f, "%.3f",
|
||||
return ImGui::DragFloat("##", &value, 0.001f,
|
||||
std::numeric_limits<float>::lowest(),
|
||||
std::numeric_limits<float>::max(),
|
||||
"%.3f",
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
}
|
||||
bool SHEditorUI::InputDouble(const std::string& label, double& value, bool* isHovered)
|
||||
{
|
||||
ImGui::Text(label.c_str());
|
||||
if (isHovered)
|
||||
*isHovered = ImGui::IsItemHovered();
|
||||
ImGui::SameLine();
|
||||
return ImGui::InputDouble("##", &value,
|
||||
0.1, 1.0, "%.3f",
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
}
|
||||
bool SHEditorUI::InputAngle(const std::string& label, double& value, bool* isHovered)
|
||||
float val = value;
|
||||
const bool CHANGED = InputFloat(label, val, isHovered);
|
||||
if (CHANGED)
|
||||
{
|
||||
ImGui::Text(label.c_str());
|
||||
if (isHovered)
|
||||
*isHovered = ImGui::IsItemHovered();
|
||||
ImGui::SameLine();
|
||||
return ImGui::InputDouble("##", &value,
|
||||
1.0, 45.0, "%.3f",
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
value = static_cast<double>(val);
|
||||
}
|
||||
return CHANGED;
|
||||
}
|
||||
|
||||
bool SHEditorUI::InputSlider(const std::string& label, int min, int max, int& value, bool* isHovered /*= nullptr*/)
|
||||
{
|
||||
ImGui::Text(label.c_str());
|
||||
|
@ -266,10 +262,10 @@ namespace SHADE
|
|||
static const std::vector<std::string> COMPONENT_LABELS = { "X", "Y" };
|
||||
return SHEditorWidgets::DragN<float, 2>(label, COMPONENT_LABELS, { &value.x, &value.y }, 0.1f, "%.3f", float{}, float{}, 0, isHovered);
|
||||
}
|
||||
bool SHEditorUI::InputVec3(const std::string& label, SHVec3& value, bool* isHovered, float speed)
|
||||
bool SHEditorUI::InputVec3(const std::string& label, SHVec3& value, bool* isHovered)
|
||||
{
|
||||
static const std::vector<std::string> COMPONENT_LABELS = { "X", "Y", "Z"};
|
||||
return SHEditorWidgets::DragN<float, 3>(label, COMPONENT_LABELS, { &value.x, &value.y, &value.z }, speed, "%.3f", float{}, float{}, 0, isHovered);
|
||||
return SHEditorWidgets::DragN<float, 3>(label, COMPONENT_LABELS, { &value.x, &value.y, &value.z }, 0.1f, "%.3f", float{}, float{}, 0, isHovered);
|
||||
}
|
||||
|
||||
bool SHEditorUI::InputTextField(const std::string& label, std::string& value, bool* isHovered)
|
||||
|
|
|
@ -85,8 +85,9 @@ namespace SHADE
|
|||
/// Wraps up ImGui::CollapsingHeader().
|
||||
/// </summary>
|
||||
/// <param name="title">Label for the header.</param>
|
||||
/// <param name="isHovered>If set, stores the hover state of this widget.</param>
|
||||
/// <returns>True if the header is open, false otherwise.</returns>
|
||||
static bool CollapsingHeader(const std::string& title);
|
||||
static bool CollapsingHeader(const std::string& title, bool* isHovered = nullptr);
|
||||
static void SameLine();
|
||||
static void Separator();
|
||||
|
||||
|
@ -219,17 +220,6 @@ namespace SHADE
|
|||
/// <returns>True if the value was changed.</returns>
|
||||
static bool InputDouble(const std::string& label, double& value, bool* isHovered = nullptr);
|
||||
/// <summary>
|
||||
/// Creates a decimal field widget for double input with increments of higher
|
||||
/// steps meant for angle variables.
|
||||
/// <br/>
|
||||
/// Wraps up ImGui::InputDouble().
|
||||
/// </summary>
|
||||
/// <param name="label">Label used to identify this widget.</param>
|
||||
/// <param name="value">Reference to the variable to store the result.</param>
|
||||
/// <param name="isHovered>If set, stores the hover state of this widget.</param>
|
||||
/// <returns>True if the value was changed.</returns>
|
||||
static bool InputAngle(const std::string& label, double& value, bool* isHovered = nullptr);
|
||||
/// <summary>
|
||||
/// Creates an int slider field widget for double input.
|
||||
/// <br/>
|
||||
/// Wraps up ImGui::SliderInt().
|
||||
|
@ -296,7 +286,7 @@ namespace SHADE
|
|||
/// <param name="value">Reference to the variable to store the result.</param>
|
||||
/// <param name="isHovered>If set, stores the hover state of this widget.</param>
|
||||
/// <returns>True if the value was changed.</returns>
|
||||
static bool InputVec3(const std::string& label, SHVec3& value, bool* isHovered = nullptr, float speed = 0.1f);
|
||||
static bool InputVec3(const std::string& label, SHVec3& value, bool* isHovered = nullptr);
|
||||
/// <summary>
|
||||
/// Creates a text field widget for string input.
|
||||
/// <br/>
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "SH_API.h"
|
||||
#include "ECS_Base/General/SHFamily.h"
|
||||
#include "Assets/SHAssetMacros.h"
|
||||
#include "ECS_Base/Managers/SHComponentManager.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
|
@ -116,6 +117,56 @@ namespace SHADE
|
|||
sceneChanged = true;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* \brief
|
||||
* Check if the Entity's scene node is active and all the
|
||||
* components specified are active.
|
||||
* This does not check if the entity HasComponent. Please use
|
||||
* CheckNodeAndHasComponentActive for that.
|
||||
* \param eid
|
||||
* EntityID of the entity to check for.
|
||||
* \return
|
||||
* true if scene node is active and all the components specified
|
||||
* are also active.
|
||||
********************************************************************/
|
||||
template<typename ...ComponentTypes>
|
||||
static std::enable_if_t<(... && std::is_base_of_v<SHComponent, ComponentTypes>), bool> CheckNodeAndComponentsActive(EntityID eid)
|
||||
{
|
||||
return CheckNodeActive(eid) && (... && SHComponentManager::GetComponent_s<ComponentTypes>(eid)->isActive);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* \brief
|
||||
* Check if the Entity's scene node is active and all the
|
||||
* components specified are active.
|
||||
* This also checks to verify that the entity has such components.
|
||||
* \param eid
|
||||
* EntityID of the entity to check for.
|
||||
* \return
|
||||
* true if scene node is active and all the components specified
|
||||
* are also active.
|
||||
********************************************************************/
|
||||
template<typename ...ComponentTypes>
|
||||
static std::enable_if_t<(... && std::is_base_of_v<SHComponent, ComponentTypes>), bool> CheckNodeAndHasComponentsActive(EntityID eid)
|
||||
{
|
||||
return CheckNodeActive(eid)
|
||||
&& (... && SHComponentManager::HasComponent<ComponentTypes>(eid))
|
||||
&& (... && SHComponentManager::GetComponent_s<ComponentTypes>(eid)->isActive);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* \brief
|
||||
* Check if Scene node is active.
|
||||
* \param eid
|
||||
* EntityID of the entity to check for.
|
||||
* \return
|
||||
* true if scene node is active
|
||||
********************************************************************/
|
||||
static bool CheckNodeActive(EntityID eid)
|
||||
{
|
||||
return GetCurrentSceneGraph().IsActiveInHierarchy(eid);
|
||||
}
|
||||
|
||||
|
||||
/*!*************************************************************************
|
||||
* \brief
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include "SH_API.h"
|
||||
#include "ECS_Base/Components/SHComponent.h"
|
||||
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
|
||||
class SH_API SHCanvasComponent final: public SHComponent
|
||||
{
|
||||
using CanvasSizeType = uint32_t;
|
||||
|
||||
|
||||
public:
|
||||
SHCanvasComponent();
|
||||
~SHCanvasComponent() = default;
|
||||
|
||||
void SetCanvasSize(CanvasSizeType width, CanvasSizeType height) noexcept;
|
||||
void SetCanvasWidth(CanvasSizeType width) noexcept;
|
||||
void SetCanvasHeight(CanvasSizeType height) noexcept;
|
||||
|
||||
CanvasSizeType const GetCanvasWidth() const noexcept;
|
||||
CanvasSizeType const GetCanvasHeight() const noexcept;
|
||||
|
||||
private:
|
||||
CanvasSizeType width;
|
||||
CanvasSizeType height;
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -18,12 +18,9 @@ of DigiPen Institute of Technology is prohibited.
|
|||
#include "Editor/Editor.hxx"
|
||||
// STL Includes
|
||||
#include <memory>
|
||||
// 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"
|
||||
#include "Editor/IconsMaterialDesign.h"
|
||||
|
@ -31,98 +28,14 @@ of DigiPen Institute of Technology is prohibited.
|
|||
#include "Editor/Command/SHCommand.hpp"
|
||||
#include "TooltipAttribute.hxx"
|
||||
#include "RangeAttribute.hxx"
|
||||
#include "Math/Vector2.hxx"
|
||||
#include "Math/Vector3.hxx"
|
||||
#include <string>
|
||||
|
||||
// Using Directives
|
||||
using namespace System;
|
||||
using namespace System::Collections::Generic;
|
||||
|
||||
/*-------------------------------------------------------------------------------------*/
|
||||
/* Macro Functions */
|
||||
/*-------------------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// <br/>
|
||||
/// This only works for primitive types that have the same types for managed and native.
|
||||
/// </summary>
|
||||
/// <param name="MANAGED_TYPE">The managed type of the object to edit.</param>
|
||||
/// <param name="NATIVE_TYPE">The native type of the object to edit.</param>
|
||||
/// <param name="FUNC">The SHEditorUI:: function to use for editing.</param>
|
||||
#define RENDER_FIELD(MANAGED_TYPE, NATIVE_TYPE, FUNC) \
|
||||
(field->FieldType == MANAGED_TYPE::typeid) \
|
||||
{ \
|
||||
NATIVE_TYPE val = safe_cast<NATIVE_TYPE>(field->GetValue(object)); \
|
||||
NATIVE_TYPE oldVal = val; \
|
||||
if (SHEditorUI::FUNC(Convert::ToNative(field->Name), val, &isHovered))\
|
||||
{ \
|
||||
field->SetValue(object, val); \
|
||||
registerUndoAction(object, field, val, oldVal); \
|
||||
} \
|
||||
} \
|
||||
/// <summary>
|
||||
/// Alternative to RENDER_FIELD that checks for RangeAttribute and switches to a slider
|
||||
/// instead.
|
||||
/// </summary>
|
||||
/// <param name="MANAGED_TYPE">The managed type of the object to edit.</param>
|
||||
/// <param name="NATIVE_TYPE">The native type of the object to edit.</param>
|
||||
/// <param name="FUNC">The SHEditorUI:: function to use for editing.</param>
|
||||
#define RENDER_FIELD_RANGE(MANAGED_TYPE, NATIVE_TYPE, FUNC) \
|
||||
(field->FieldType == MANAGED_TYPE::typeid) \
|
||||
{ \
|
||||
NATIVE_TYPE val = safe_cast<NATIVE_TYPE>(field->GetValue(object)); \
|
||||
NATIVE_TYPE oldVal = val; \
|
||||
\
|
||||
RangeAttribute^ rangeAttrib = hasAttribute<RangeAttribute^>(field);\
|
||||
const std::string FIELD_NAME = Convert::ToNative(field->Name); \
|
||||
bool changed = false; \
|
||||
if (rangeAttrib) \
|
||||
{ \
|
||||
changed = SHEditorUI::InputSlider \
|
||||
( \
|
||||
FIELD_NAME, \
|
||||
static_cast<NATIVE_TYPE>(rangeAttrib->Min), \
|
||||
static_cast<NATIVE_TYPE>(rangeAttrib->Max), \
|
||||
val, &isHovered \
|
||||
); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
changed = SHEditorUI::FUNC(FIELD_NAME, val, &isHovered); \
|
||||
} \
|
||||
\
|
||||
if (changed) \
|
||||
{ \
|
||||
field->SetValue(object, val); \
|
||||
registerUndoAction(object, field, val, oldVal); \
|
||||
} \
|
||||
} \
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// <br/>
|
||||
/// This only works for types that have an implementation of Convert::ToNative and
|
||||
/// Convert::ToCLI.
|
||||
/// </summary>
|
||||
/// <param name="MANAGED_TYPE">The managed type of the object to edit.</param>
|
||||
/// <param name="NATIVE_TYPE">The native type of the object to edit.</param>
|
||||
/// <param name="FUNC">The SHEditorUI:: function to use for editing.</param>
|
||||
#define RENDER_FIELD_CASTED(MANAGED_TYPE, NATIVE_TYPE, FUNC) \
|
||||
(field->FieldType == MANAGED_TYPE::typeid) \
|
||||
{ \
|
||||
NATIVE_TYPE val = Convert::ToNative(safe_cast<MANAGED_TYPE>(field->GetValue(object))); \
|
||||
NATIVE_TYPE oldVal = val; \
|
||||
\
|
||||
if (SHEditorUI::FUNC(Convert::ToNative(field->Name), val, &isHovered)) \
|
||||
{ \
|
||||
field->SetValue(object, Convert::ToCLI(val)); \
|
||||
registerUndoAction(object, field, Convert::ToCLI(val), Convert::ToCLI(oldVal)); \
|
||||
} \
|
||||
} \
|
||||
|
||||
/*-------------------------------------------------------------------------------------*/
|
||||
/* Function Definitions */
|
||||
/*-------------------------------------------------------------------------------------*/
|
||||
|
@ -234,72 +147,66 @@ namespace SHADE
|
|||
}
|
||||
SHEditorUI::PopID();
|
||||
}
|
||||
void Editor::renderFieldInInspector(Reflection::FieldInfo^ field, Object^ object)
|
||||
|
||||
void Editor::renderFieldInInspector(Reflection::FieldInfo^ field, System::Object^ object)
|
||||
{
|
||||
bool isHovered = false;
|
||||
|
||||
if RENDER_FIELD_RANGE (Int16, int, InputInt)
|
||||
else if RENDER_FIELD_RANGE (Int32, int, InputInt)
|
||||
else if RENDER_FIELD_RANGE (Int64, int, InputInt)
|
||||
else if RENDER_FIELD_RANGE (UInt16, unsigned int, InputUnsignedInt)
|
||||
else if RENDER_FIELD_RANGE (UInt32, unsigned int, InputUnsignedInt)
|
||||
else if RENDER_FIELD_RANGE (UInt64, unsigned int, InputUnsignedInt)
|
||||
else if RENDER_FIELD_RANGE (Byte, int, InputInt)
|
||||
else if RENDER_FIELD (bool, bool, InputCheckbox)
|
||||
else if RENDER_FIELD_RANGE (float, float, InputFloat)
|
||||
else if RENDER_FIELD_RANGE (double, double, InputDouble)
|
||||
else if (field->FieldType->IsSubclassOf(Enum::typeid))
|
||||
{
|
||||
// Get all the names of the enums
|
||||
const array<String^>^ ENUM_NAMES = field->FieldType->GetEnumNames();
|
||||
std::vector<std::string> nativeEnumNames;
|
||||
for each (String^ str in ENUM_NAMES)
|
||||
{
|
||||
nativeEnumNames.emplace_back(Convert::ToNative(str));
|
||||
}
|
||||
const bool MODIFIED_PRIMITIVE =
|
||||
renderSpecificField<int , Int16 >(field, object, SHEditorUI::InputInt , &isHovered) ||
|
||||
renderSpecificField<int , Int32 >(field, object, SHEditorUI::InputInt , &isHovered) ||
|
||||
renderSpecificField<int , Int64 >(field, object, SHEditorUI::InputInt , &isHovered) ||
|
||||
renderSpecificField<int , UInt16 >(field, object, SHEditorUI::InputInt , &isHovered) ||
|
||||
renderSpecificField<int , UInt32 >(field, object, SHEditorUI::InputInt , &isHovered) ||
|
||||
renderSpecificField<int , UInt64 >(field, object, SHEditorUI::InputInt , &isHovered) ||
|
||||
renderSpecificField<int , Byte >(field, object, SHEditorUI::InputInt , &isHovered) ||
|
||||
renderSpecificField<bool , bool >(field, object, SHEditorUI::InputCheckbox, &isHovered) ||
|
||||
renderSpecificField<float , float >(field, object, SHEditorUI::InputFloat , &isHovered) ||
|
||||
renderSpecificField<double , double >(field, object, SHEditorUI::InputDouble , &isHovered) ||
|
||||
renderSpecificField<SHVec2 , Vector2 >(field, object, SHEditorUI::InputVec2 , &isHovered) ||
|
||||
renderSpecificField<SHVec3 , Vector3 >(field, object, SHEditorUI::InputVec3 , &isHovered) ||
|
||||
renderSpecificField<uint32_t , GameObject >(field, object, nullptr , &isHovered) ||
|
||||
renderSpecificField<std::string, System::String^>(field, object, nullptr , &isHovered) ||
|
||||
renderSpecificField<int , System::Enum >(field, object, nullptr , &isHovered);
|
||||
|
||||
int val = safe_cast<int>(field->GetValue(object));
|
||||
int oldVal = val;
|
||||
if (SHEditorUI::InputEnumCombo(Convert::ToNative(field->Name), val, nativeEnumNames, &isHovered))
|
||||
if (!MODIFIED_PRIMITIVE)
|
||||
{
|
||||
field->SetValue(object, val);
|
||||
registerUndoAction(object, field, val, oldVal);
|
||||
}
|
||||
}
|
||||
else if RENDER_FIELD_CASTED(Vector2, SHVec2, InputVec2)
|
||||
else if RENDER_FIELD_CASTED(Vector3, SHVec3, InputVec3)
|
||||
else if (field->FieldType == String::typeid)
|
||||
// Any List
|
||||
if (ReflectionUtilities::FieldIsList(field))
|
||||
{
|
||||
// Prevent issues where String^ is null due to being empty
|
||||
String^ stringVal = safe_cast<String^>(field->GetValue(object));
|
||||
if (stringVal == nullptr)
|
||||
{
|
||||
stringVal = "";
|
||||
}
|
||||
System::Type^ listType = field->FieldType->GenericTypeArguments[0];
|
||||
RangeAttribute^ rangeAttrib = hasAttribute<RangeAttribute^>(field);
|
||||
System::Collections::IList^ iList = safe_cast<System::Collections::IList^>(field->GetValue(object));
|
||||
|
||||
// Actual Field
|
||||
std::string val = Convert::ToNative(stringVal);
|
||||
std::string oldVal = val;
|
||||
if (SHEditorUI::InputTextField(Convert::ToNative(field->Name), val, &isHovered))
|
||||
if (SHEditorUI::CollapsingHeader(Convert::ToNative(field->Name), &isHovered))
|
||||
{
|
||||
field->SetValue(object, Convert::ToCLI(val));
|
||||
registerUndoAction(object, field, Convert::ToCLI(val), Convert::ToCLI(oldVal));
|
||||
if (SHEditorUI::Button("Add Item"))
|
||||
{
|
||||
System::Object^ obj = System::Activator::CreateInstance(listType);
|
||||
iList->Add(obj);
|
||||
registerUndoListAddAction(listType, iList, iList->Count - 1, obj);
|
||||
}
|
||||
for (int i = 0; i < iList->Count; ++i)
|
||||
{
|
||||
SHEditorUI::PushID(i);
|
||||
System::Object^ obj = iList[i];
|
||||
System::Object^ oldObj = iList[i];
|
||||
if (renderFieldEditor(std::to_string(i), obj, rangeAttrib))
|
||||
{
|
||||
iList[i] = obj;
|
||||
registerUndoListChangeAction(listType, iList, i, obj, oldObj);
|
||||
}
|
||||
else if (field->FieldType == GameObject::typeid)
|
||||
SHEditorUI::SameLine();
|
||||
if (SHEditorUI::Button("-"))
|
||||
{
|
||||
GameObject gameObj = safe_cast<GameObject>(field->GetValue(object));
|
||||
uint32_t entityId = gameObj.GetEntity();
|
||||
if (SHEditorUI::InputGameObjectField(Convert::ToNative(field->Name), entityId, &isHovered, !gameObj))
|
||||
{
|
||||
GameObject newVal = GameObject(entityId);
|
||||
if (entityId != MAX_EID)
|
||||
{
|
||||
// Null GameObject set
|
||||
newVal = GameObject(entityId);
|
||||
System::Object^ obj = iList[i];
|
||||
iList->RemoveAt(i);
|
||||
registerUndoListRemoveAction(listType, iList, i, obj);
|
||||
SHEditorUI::PopID();
|
||||
break;
|
||||
}
|
||||
SHEditorUI::PopID();
|
||||
}
|
||||
field->SetValue(object, newVal);
|
||||
registerUndoAction(object, field, newVal, gameObj);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -372,6 +279,7 @@ namespace SHADE
|
|||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the field has a specific attribute
|
||||
TooltipAttribute^ toolTip = hasAttribute<TooltipAttribute^>(field);
|
||||
|
@ -383,6 +291,51 @@ namespace SHADE
|
|||
}
|
||||
}
|
||||
|
||||
bool Editor::renderFieldEditor(const std::string& fieldName, System::Object^% object, RangeAttribute^ rangeAttrib)
|
||||
{
|
||||
bool modified;
|
||||
|
||||
const bool RENDERED =
|
||||
renderFieldEditor<int , Int16 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) ||
|
||||
renderFieldEditor<int , Int32 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) ||
|
||||
renderFieldEditor<int , Int64 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) ||
|
||||
renderFieldEditor<int , UInt16 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) ||
|
||||
renderFieldEditor<int , UInt32 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) ||
|
||||
renderFieldEditor<int , UInt64 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) ||
|
||||
renderFieldEditor<int , Byte >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) ||
|
||||
renderFieldEditor<bool , bool >(fieldName, object, SHEditorUI::InputCheckbox, nullptr, rangeAttrib, modified) ||
|
||||
renderFieldEditor<float , float >(fieldName, object, SHEditorUI::InputFloat , nullptr, rangeAttrib, modified) ||
|
||||
renderFieldEditor<double , double >(fieldName, object, SHEditorUI::InputDouble , nullptr, rangeAttrib, modified) ||
|
||||
renderFieldEditor<SHVec2 , Vector2 >(fieldName, object, SHEditorUI::InputVec2 , nullptr, rangeAttrib, modified) ||
|
||||
renderFieldEditor<SHVec3 , Vector3 >(fieldName, object, SHEditorUI::InputVec3 , nullptr, rangeAttrib, modified) ||
|
||||
renderFieldEditor<uint32_t , GameObject >(fieldName, object, nullptr , nullptr, rangeAttrib, modified) ||
|
||||
renderFieldEditor<std::string, System::String^>(fieldName, object, nullptr , nullptr, rangeAttrib, modified) ||
|
||||
renderFieldEditor<int , System::Enum >(fieldName, object, nullptr , nullptr, rangeAttrib, modified);
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool Editor::renderEnumEditor(const std::string& fieldName, System::Object^% object, bool* isHovered)
|
||||
{
|
||||
// Get all the names of the enums
|
||||
const array<String^>^ ENUM_NAMES = object->GetType()->GetEnumNames();
|
||||
std::vector<std::string> nativeEnumNames;
|
||||
for each (String ^ str in ENUM_NAMES)
|
||||
{
|
||||
nativeEnumNames.emplace_back(Convert::ToNative(str));
|
||||
}
|
||||
|
||||
int val = safe_cast<int>(object);
|
||||
int oldVal = val;
|
||||
if (SHEditorUI::InputEnumCombo(fieldName, val, nativeEnumNames, isHovered))
|
||||
{
|
||||
object = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Editor::renderScriptContextMenu(Entity entity, Script^ script)
|
||||
{
|
||||
// Right Click Menu
|
||||
|
@ -400,12 +353,40 @@ namespace SHADE
|
|||
void Editor::registerUndoAction(System::Object^ object, System::Reflection::FieldInfo^ field, System::Object^ newData, System::Object^ oldData)
|
||||
{
|
||||
// Create command and add it into the undo stack
|
||||
UndoRedoStack::Command cmd;
|
||||
cmd.Field = field;
|
||||
cmd.Object = object;
|
||||
cmd.NewData = newData;
|
||||
cmd.OldData = oldData;
|
||||
actionStack.Add(cmd);
|
||||
actionStack.Add(gcnew FieldChangeCommand(object, field, newData, oldData));
|
||||
|
||||
// Inform the C++ Undo-Redo stack
|
||||
SHCommandManager::RegisterCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCLICommand>()));
|
||||
}
|
||||
|
||||
void Editor::registerUndoListChangeAction(System::Type^ type, System::Collections::IList^ list, int index, System::Object^ newData, System::Object^ oldData)
|
||||
{
|
||||
if (list == nullptr)
|
||||
return;
|
||||
|
||||
actionStack.Add(gcnew ListElementChangeCommand(list, index, newData, oldData));
|
||||
|
||||
// Inform the C++ Undo-Redo stack
|
||||
SHCommandManager::RegisterCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCLICommand>()));
|
||||
}
|
||||
|
||||
void Editor::registerUndoListAddAction(System::Type^ type, System::Collections::IList^ list, int index, System::Object^ data)
|
||||
{
|
||||
if (list == nullptr)
|
||||
return;
|
||||
|
||||
actionStack.Add(gcnew ListElementAddCommand(list, index, data));
|
||||
|
||||
// Inform the C++ Undo-Redo stack
|
||||
SHCommandManager::RegisterCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCLICommand>()));
|
||||
}
|
||||
|
||||
void Editor::registerUndoListRemoveAction(System::Type^ type, System::Collections::IList^ list, int index, System::Object^ data)
|
||||
{
|
||||
if (list == nullptr)
|
||||
return;
|
||||
|
||||
actionStack.Add(gcnew ListElementRemoveCommand(list, index, data));
|
||||
|
||||
// Inform the C++ Undo-Redo stack
|
||||
SHCommandManager::RegisterCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCLICommand>()));
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
/************************************************************************************//*!
|
||||
\file Editor.h++
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Nov 10, 2022
|
||||
\brief Contains the definition of templated functions for 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
|
||||
|
||||
// Primary Include
|
||||
#include "Editor.hxx"
|
||||
// External Dependencies
|
||||
#include "Editor/SHEditorUI.h"
|
||||
// Project Includes
|
||||
#include "Utility/Convert.hxx"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
template<typename NativeType, typename ManagedType>
|
||||
bool Editor::renderSpecificField(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered)
|
||||
{
|
||||
if constexpr (std::is_same_v<ManagedType, System::Enum>)
|
||||
{
|
||||
if (fieldInfo->FieldType->IsSubclassOf(Enum::typeid))
|
||||
{
|
||||
System::Object^ enumObj = fieldInfo->GetValue(object);
|
||||
int oldVal = safe_cast<int>(enumObj);
|
||||
int val = oldVal;
|
||||
if (renderEnumEditor
|
||||
(
|
||||
Convert::ToNative(fieldInfo->Name),
|
||||
enumObj,
|
||||
isHovered
|
||||
))
|
||||
{
|
||||
fieldInfo->SetValue(object, safe_cast<int>(enumObj));
|
||||
registerUndoAction(object, fieldInfo, fieldInfo->GetValue(object), oldVal);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fieldInfo->FieldType == ManagedType::typeid)
|
||||
{
|
||||
RangeAttribute^ rangeAttrib;
|
||||
if constexpr (std::is_arithmetic_v<NativeType> && !std::is_same_v<NativeType, bool>)
|
||||
{
|
||||
rangeAttrib = hasAttribute<RangeAttribute^>(fieldInfo);
|
||||
}
|
||||
|
||||
ManagedType oldVal = safe_cast<ManagedType>(fieldInfo->GetValue(object));
|
||||
ManagedType val = oldVal;
|
||||
if (renderFieldEditorInternal<NativeType, ManagedType>
|
||||
(
|
||||
Convert::ToNative(fieldInfo->Name),
|
||||
&val,
|
||||
fieldEditor,
|
||||
isHovered,
|
||||
rangeAttrib
|
||||
))
|
||||
{
|
||||
fieldInfo->SetValue(object, val);
|
||||
registerUndoAction(object, fieldInfo, fieldInfo->GetValue(object), oldVal);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename NativeType, typename ManagedType>
|
||||
bool Editor::renderFieldEditor(const std::string& fieldName, System::Object^% object, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered, RangeAttribute^ rangeAttrib, bool& modified)
|
||||
{
|
||||
modified = false;
|
||||
|
||||
if constexpr (std::is_same_v<ManagedType, System::Enum>)
|
||||
{
|
||||
if (object->GetType()->IsSubclassOf(Enum::typeid))
|
||||
{
|
||||
modified = renderEnumEditor(fieldName, object, isHovered);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (object->GetType() == ManagedType::typeid)
|
||||
{
|
||||
ManagedType managedVal = safe_cast<ManagedType>(object);
|
||||
cli::interior_ptr<ManagedType> managedValPtr = &managedVal;
|
||||
if (renderFieldEditorInternal<NativeType, ManagedType>(fieldName, managedValPtr, fieldEditor, isHovered, rangeAttrib))
|
||||
{
|
||||
object = managedVal;
|
||||
modified = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template<typename NativeType, typename ManagedType>
|
||||
bool Editor::renderFieldEditorInternal(const std::string& fieldName, interior_ptr<ManagedType> managedValPtr, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered, RangeAttribute^ rangeAttrib)
|
||||
{
|
||||
// Retrieve the native version of the object
|
||||
NativeType val;
|
||||
if constexpr (IsPrimitiveTypeMatches_V<NativeType>)
|
||||
{
|
||||
val = safe_cast<NativeType>(*managedValPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
val = Convert::ToNative(*managedValPtr);
|
||||
}
|
||||
|
||||
// Throw into the SHEditorUI function
|
||||
NativeType oldVal = val;
|
||||
bool changed = false;
|
||||
if (rangeAttrib)
|
||||
{
|
||||
// Do not allow bools for Sliders just in case
|
||||
if constexpr (std::is_arithmetic_v<NativeType> && !std::is_same_v<NativeType, bool>)
|
||||
{
|
||||
changed = SHEditorUI::InputSlider
|
||||
(
|
||||
fieldName,
|
||||
static_cast<NativeType>(rangeAttrib->Min),
|
||||
static_cast<NativeType>(rangeAttrib->Max),
|
||||
val, isHovered
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
changed = fieldEditor(fieldName, val, isHovered);
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
if constexpr (IsPrimitiveTypeMatches_V<NativeType>)
|
||||
{
|
||||
*managedValPtr = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
*managedValPtr = Convert::ToCLI(val);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
template<>
|
||||
bool Editor::renderFieldEditorInternal<std::string, System::String^>(const std::string& fieldName, interior_ptr<System::String^> managedValPtr, EditorFieldFunc<std::string>, bool* isHovered, RangeAttribute^)
|
||||
{
|
||||
// Prevent issues where String^ is null due to being empty
|
||||
if (*managedValPtr == nullptr)
|
||||
*managedValPtr = "";
|
||||
|
||||
// Actual Field
|
||||
std::string val = Convert::ToNative(*managedValPtr);
|
||||
if (SHEditorUI::InputTextField(fieldName, val, isHovered))
|
||||
{
|
||||
*managedValPtr = Convert::ToCLI(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
template<>
|
||||
bool Editor::renderFieldEditorInternal<uint32_t, GameObject>(const std::string& fieldName, interior_ptr<GameObject> managedValPtr, EditorFieldFunc<uint32_t>, bool* isHovered, RangeAttribute^)
|
||||
{
|
||||
uint32_t entityId = managedValPtr->GetEntity();
|
||||
if (SHEditorUI::InputGameObjectField(fieldName, entityId, isHovered, !(*managedValPtr)))
|
||||
{
|
||||
GameObject newVal = GameObject(entityId);
|
||||
if (entityId != MAX_EID)
|
||||
{
|
||||
// Null GameObject set
|
||||
newVal = GameObject(entityId);
|
||||
}
|
||||
*managedValPtr = newVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -17,9 +17,14 @@ of DigiPen Institute of Technology is prohibited.
|
|||
#include "Engine/Entity.hxx"
|
||||
#include "Scripts/Script.hxx"
|
||||
#include "UndoRedoStack.hxx"
|
||||
#include "RangeAttribute.hxx"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
|
||||
template<typename NativeType>
|
||||
using EditorFieldFunc = bool(*)(const std::string& label, NativeType& val, bool* isHovered);
|
||||
|
||||
/// <summary>
|
||||
/// Static class for Editor-related functions
|
||||
/// </summary>
|
||||
|
@ -84,15 +89,114 @@ namespace SHADE
|
|||
/// <param name="object">
|
||||
/// The object that contains the data of the field to render.
|
||||
/// </param>
|
||||
static void renderFieldInInspector(System::Reflection::FieldInfo^ field, Object^ object);
|
||||
static void renderFieldInInspector(System::Reflection::FieldInfo^ field, System::Object^ object);
|
||||
/// <summary>
|
||||
/// Renders a raw editor for a single value.
|
||||
/// </summary>
|
||||
/// <param name="fieldName">The name of the field to render.</param>
|
||||
/// <param name="object">Tracking reference to the object to modify.</param>
|
||||
/// <param name="rangeAttrib">
|
||||
/// If specified, will be used to constrain values.
|
||||
/// </param>
|
||||
/// <returns>True if the value was modified.</returns>
|
||||
static bool renderFieldEditor(const std::string& fieldName, System::Object^% object, RangeAttribute^ rangeAttrib);
|
||||
/// <summary>
|
||||
/// Renders a ImGui field editor based on the type of parameters specified if the
|
||||
/// type matches.
|
||||
/// </summary>
|
||||
/// <typeparam name="NativeType">Native type of the field.</typeparam>
|
||||
/// <typeparam name="ManagedType">Managed type of the field.</typeparam>
|
||||
/// <param name="fieldName">Label to use for the field editor.</param>
|
||||
/// <param name="managedVal">
|
||||
/// Tracking reference for the managed variable to modify.
|
||||
/// </param>
|
||||
/// <param name="fieldEditor">ImGui field editor function to use.</param>
|
||||
/// <param name="isHovered">
|
||||
/// Pointer to a bool that stores if the field editor was hovered over.
|
||||
/// </param>
|
||||
/// <param name="rangeAttrib">
|
||||
/// If provided and the type supports it, the field will be rendered with a
|
||||
/// slider instead.
|
||||
/// </param>
|
||||
/// <param name="modified"></param>
|
||||
/// <returns>True if the field was rendered..</returns>
|
||||
template<typename NativeType, typename ManagedType>
|
||||
static bool renderFieldEditor(const std::string& fieldName, System::Object^% object, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered, RangeAttribute^ rangeAttrib, bool& modified);
|
||||
/// <summary>
|
||||
/// Renders a raw editor for a single enum value.
|
||||
/// </summary>
|
||||
/// <param name="fieldName">The name of the field to render.</param>
|
||||
/// <param name="object">
|
||||
/// Tracking reference to the object to modify. Must be an enum.
|
||||
/// </param>
|
||||
/// <param name="isHovered">
|
||||
/// Pointer to a bool that stores if the field editor was hovered over.
|
||||
/// </param>
|
||||
/// <returns>True if the value was modified.</returns>
|
||||
static bool renderEnumEditor(const std::string& fieldName, System::Object^% object, bool* isHovered);
|
||||
/// <summary>
|
||||
/// Checks if the specified field is of the specified native and managed type
|
||||
/// equivalent and renders a ImGui field editor based on the specified field
|
||||
/// editor function. Also handles fields that contain a RangeAttribute.
|
||||
/// </summary>
|
||||
/// <typeparam name="NativeType">Native type of the field.</typeparam>
|
||||
/// <typeparam name="ManagedType">Managed type of the field.</typeparam>
|
||||
/// <param name="fieldInfo">Describes the field to modify.</param>
|
||||
/// <param name="object">Object to modify that has the specified field.</param>
|
||||
/// <param name="fieldEditor">ImGui field editor function to use.</param>
|
||||
/// <param name="isHovered">
|
||||
/// Pointer to a bool that stores if the field editor was hovered over.
|
||||
/// </param>
|
||||
/// <returns>True if the field is modified.</returns>
|
||||
template<typename NativeType, typename ManagedType>
|
||||
static bool renderSpecificField(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered);
|
||||
/// <summary>
|
||||
/// Renders a ImGui field editor based on the type of parameters specified.
|
||||
/// </summary>
|
||||
/// <typeparam name="NativeType">Native type of the field.</typeparam>
|
||||
/// <typeparam name="ManagedType">Managed type of the field.</typeparam>
|
||||
/// <param name="fieldName">Label to use for the field editor.</param>
|
||||
/// <param name="managedVal">
|
||||
/// Tracking reference for the managed variable to modify.
|
||||
/// </param>
|
||||
/// <param name="fieldEditor">ImGui field editor function to use.</param>
|
||||
/// <param name="isHovered">
|
||||
/// Pointer to a bool that stores if the field editor was hovered over.
|
||||
/// </param>
|
||||
/// <param name="rangeAttrib">
|
||||
/// If provided and the type supports it, the field will be rendered with a
|
||||
/// slider instead.
|
||||
/// </param>
|
||||
/// <returns>True if the field is modified.</returns>
|
||||
template<typename NativeType, typename ManagedType>
|
||||
static bool renderFieldEditorInternal(const std::string& fieldName, interior_ptr<ManagedType> managedValPtr, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered, RangeAttribute^ rangeAttrib);
|
||||
|
||||
/// <summary>
|
||||
/// Renders a context menu when right clicked for the scripts
|
||||
/// </summary>
|
||||
/// <param name="entity">The Entity to render the Scripts of.</param>
|
||||
/// <param name="script">The Script to render the inspector for.</param>
|
||||
static void renderScriptContextMenu(Entity entity, Script^ script);
|
||||
/// <summary>
|
||||
/// Adds changes to a variable as an undo-able/redo-able action on the Undo-Redo
|
||||
/// stack.
|
||||
/// </summary>
|
||||
/// <param name="object">The object that changes are applied to.</param>
|
||||
/// <param name="field">The field that was changed.</param>
|
||||
/// <param name="newData">New data to set.</param>
|
||||
/// <param name="oldData">Data that was overriden.</param>
|
||||
static void registerUndoAction(System::Object^ object, System::Reflection::FieldInfo^ field, System::Object^ newData, System::Object^ oldData);
|
||||
static void registerUndoListChangeAction(System::Type^ type, System::Collections::IList^ list, int index, System::Object^ newData, System::Object^ oldData);
|
||||
static void registerUndoListAddAction(System::Type^ type, System::Collections::IList^ list, int index, System::Object^ data);
|
||||
static void registerUndoListRemoveAction(System::Type^ type, System::Collections::IList^ list, int index, System::Object^ data);
|
||||
/// <summary>
|
||||
/// Checks if a specific field has the specified attribute
|
||||
/// </summary>
|
||||
/// <typeparam name="Attribute">Type of Attribute to check for.</typeparam>
|
||||
/// <param name="field">The field to check.</param>
|
||||
/// <returns>The attribute to check for if it exists. Null otherwise.</returns>
|
||||
generic<typename Attribute> where Attribute : System::Attribute
|
||||
static Attribute hasAttribute(System::Reflection::FieldInfo^ field);
|
||||
};
|
||||
}
|
||||
#include "Editor.h++"
|
||||
|
|
|
@ -19,9 +19,14 @@ of DigiPen Institute of Technology is prohibited.
|
|||
// External Dependencies
|
||||
#include "Editor/SHEditorUI.h"
|
||||
// Project Headers
|
||||
#include "Utility/Debug.hxx"
|
||||
#include "Utility/Convert.hxx"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* UndoRedoStack - Properties */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
bool UndoRedoStack::UndoActionPresent::get()
|
||||
{
|
||||
return commandStack->Count > 0 && latestActionIndex >= 0;
|
||||
|
@ -32,7 +37,10 @@ namespace SHADE
|
|||
return latestActionIndex >= 0 && latestActionIndex < commandStack->Count - 1;
|
||||
}
|
||||
|
||||
void UndoRedoStack::Add(Command command)
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* UndoRedoStack - Usage Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
void UndoRedoStack::Add(ICommand^ command)
|
||||
{
|
||||
// Erase any other actions ahead of the current action
|
||||
if (latestActionIndex >= 0 && latestActionIndex < commandStack->Count - 1)
|
||||
|
@ -52,8 +60,8 @@ namespace SHADE
|
|||
if (!UndoActionPresent)
|
||||
return;
|
||||
|
||||
Command cmd = commandStack[latestActionIndex];
|
||||
cmd.Field->SetValue(cmd.Object, cmd.OldData);
|
||||
ICommand^ cmd = commandStack[latestActionIndex];
|
||||
cmd->Unexceute();
|
||||
--latestActionIndex;
|
||||
}
|
||||
|
||||
|
@ -62,8 +70,192 @@ namespace SHADE
|
|||
if (!RedoActionPresent)
|
||||
return;
|
||||
|
||||
Command cmd = commandStack[latestActionIndex];
|
||||
cmd.Field->SetValue(cmd.Object, cmd.NewData);
|
||||
ICommand^ cmd = commandStack[latestActionIndex];
|
||||
cmd->Execute();
|
||||
++latestActionIndex;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* FieldChangeCommand - Constructor */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
FieldChangeCommand::FieldChangeCommand(System::Object^ obj, System::Reflection::FieldInfo^ field, System::Object^ newData, System::Object^ oldData)
|
||||
: objectToChange { obj }
|
||||
, field { field }
|
||||
, newData { newData }
|
||||
, oldData { oldData }
|
||||
{}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* FieldChangeCommand - ICommand Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
bool FieldChangeCommand::Execute()
|
||||
{
|
||||
if (field && objectToChange)
|
||||
{
|
||||
field->SetValue(objectToChange, newData);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FieldChangeCommand::Unexceute()
|
||||
{
|
||||
if (field && objectToChange)
|
||||
{
|
||||
field->SetValue(objectToChange, oldData);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FieldChangeCommand::Merge(ICommand^ command)
|
||||
{
|
||||
FieldChangeCommand^ otherCommand = safe_cast<FieldChangeCommand^>(command);
|
||||
if (otherCommand == nullptr)
|
||||
{
|
||||
Debug::LogWarning("[Field Change Command] Attempted to merge two incompatible commands!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only merge if they are workng on the same object and field
|
||||
if (field == otherCommand->field && objectToChange == otherCommand->objectToChange)
|
||||
{
|
||||
newData = otherCommand->newData;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* ListElementChangeCommand - Constructor */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
ListElementChangeCommand::ListElementChangeCommand(System::Collections::IList^ list, int index, System::Object^ newData, System::Object^ oldData)
|
||||
: list { list }
|
||||
, index { index }
|
||||
, newData { newData }
|
||||
, oldData { oldData }
|
||||
{}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* ListElementChangeCommand - ICommand Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
bool ListElementChangeCommand::Execute()
|
||||
{
|
||||
if (list && index < list->Count)
|
||||
{
|
||||
list[index] = newData;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ListElementChangeCommand::Unexceute()
|
||||
{
|
||||
if (list && index < list->Count)
|
||||
{
|
||||
list[index] = oldData;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool ListElementChangeCommand::Merge(ICommand^ command)
|
||||
{
|
||||
ListElementChangeCommand^ otherCommand = safe_cast<ListElementChangeCommand^>(command);
|
||||
if (otherCommand == nullptr)
|
||||
{
|
||||
Debug::LogWarning("[Field Change Command] Attempted to merge two incompatible commands!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (command && list == otherCommand->list && index == otherCommand->index)
|
||||
{
|
||||
newData = otherCommand->newData;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* ListElementAddCommand - ICommand Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
ListElementAddCommand::ListElementAddCommand(System::Collections::IList^ list, int addIndex, System::Object^ data)
|
||||
: list { list }
|
||||
, addIndex { addIndex }
|
||||
, data { data }
|
||||
{}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* ListElementAddCommand - ICommand Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
bool ListElementAddCommand::Execute()
|
||||
{
|
||||
if (list)
|
||||
{
|
||||
list->Insert(addIndex, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ListElementAddCommand::Unexceute()
|
||||
{
|
||||
if (list && addIndex < list->Count)
|
||||
{
|
||||
list->RemoveAt(addIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ListElementAddCommand::Merge(ICommand^)
|
||||
{
|
||||
// Not allowed
|
||||
return false;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* ListElementRemoveCommand - ICommand Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
ListElementRemoveCommand::ListElementRemoveCommand(System::Collections::IList^ list, int removeIndex, System::Object^ data)
|
||||
: list { list }
|
||||
, removeIndex { removeIndex }
|
||||
, data { data }
|
||||
{}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* ListElementRemoveCommand - ICommand Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
bool ListElementRemoveCommand::Execute()
|
||||
{
|
||||
if (list && removeIndex < list->Count)
|
||||
{
|
||||
list->RemoveAt(removeIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ListElementRemoveCommand::Unexceute()
|
||||
{
|
||||
if (list)
|
||||
{
|
||||
list->Insert(removeIndex, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ListElementRemoveCommand::Merge(ICommand^)
|
||||
{
|
||||
// Not allowed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,27 +15,99 @@ of DigiPen Institute of Technology is prohibited.
|
|||
|
||||
namespace SHADE
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for command that fits into the UndoRedoStack which can perform
|
||||
/// undo-able and redo-able operations.
|
||||
/// </summary>
|
||||
private interface class ICommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes an action. This is called when a "Redo" is performed.
|
||||
/// </summary>
|
||||
/// <returns>Whether the action was successful or not.</returns>
|
||||
bool Execute();
|
||||
/// <summary>
|
||||
/// Undoes an action. This is called when an "Undo" is performed.
|
||||
/// </summary>
|
||||
/// <returns>Whether the action was successful or not.</returns>
|
||||
bool Unexceute();
|
||||
/// <summary>
|
||||
/// Merges this command with another command.
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <returns>Whether the merge was successful or not.</returns>
|
||||
bool Merge(ICommand^ command);
|
||||
};
|
||||
|
||||
private ref class FieldChangeCommand sealed : public ICommand
|
||||
{
|
||||
public:
|
||||
FieldChangeCommand(System::Object^ obj, System::Reflection::FieldInfo^ field, System::Object^ newData, System::Object^ oldData);
|
||||
|
||||
bool Execute() override;
|
||||
bool Unexceute() override;
|
||||
bool Merge(ICommand^ command) override;
|
||||
|
||||
private:
|
||||
System::Object^ objectToChange;
|
||||
System::Reflection::FieldInfo^ field;
|
||||
System::Object^ newData;
|
||||
System::Object^ oldData;
|
||||
};
|
||||
|
||||
|
||||
private ref class ListElementChangeCommand sealed : public ICommand
|
||||
{
|
||||
public:
|
||||
ListElementChangeCommand(System::Collections::IList^ list, int index, System::Object^ newData, System::Object^ oldData);
|
||||
|
||||
bool Execute() override;
|
||||
bool Unexceute() override;
|
||||
bool Merge(ICommand^ command) override;
|
||||
|
||||
private:
|
||||
System::Collections::IList^ list;
|
||||
int index;
|
||||
System::Object^ newData;
|
||||
System::Object^ oldData;
|
||||
};
|
||||
|
||||
private ref class ListElementAddCommand sealed : public ICommand
|
||||
{
|
||||
public:
|
||||
ListElementAddCommand(System::Collections::IList^ list, int addIndex, System::Object^ data);
|
||||
|
||||
bool Execute() override;
|
||||
bool Unexceute() override;
|
||||
bool Merge(ICommand^ command) override;
|
||||
|
||||
private:
|
||||
System::Collections::IList^ list;
|
||||
int addIndex; // New index of the added element
|
||||
System::Object^ data;
|
||||
};
|
||||
|
||||
private ref class ListElementRemoveCommand sealed : public ICommand
|
||||
{
|
||||
public:
|
||||
ListElementRemoveCommand(System::Collections::IList^ list, int removeIndex, System::Object^ data);
|
||||
|
||||
bool Execute() override;
|
||||
bool Unexceute() override;
|
||||
bool Merge(ICommand^ command) override;
|
||||
|
||||
private:
|
||||
System::Collections::IList^ list;
|
||||
int removeIndex; // Index of the element to remove at
|
||||
System::Object^ data;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Class that is able to store a stack of actions that can be done and redone.
|
||||
/// </summary>
|
||||
private ref class UndoRedoStack sealed
|
||||
{
|
||||
public:
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Type Definitions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Command for the stack that represents a data modification.
|
||||
/// </summary>
|
||||
value struct Command
|
||||
{
|
||||
public:
|
||||
System::Object^ Object;
|
||||
System::Reflection::FieldInfo^ Field;
|
||||
System::Object^ NewData;
|
||||
System::Object^ OldData;
|
||||
};
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Properties */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
@ -55,7 +127,7 @@ namespace SHADE
|
|||
/// Adds a command onto the stack.
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
void Add(Command command);
|
||||
void Add(ICommand^ command);
|
||||
/// <summary>
|
||||
/// Undos the last added command if it exists.
|
||||
/// </summary>
|
||||
|
@ -70,6 +142,6 @@ namespace SHADE
|
|||
/* Data Members */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
int latestActionIndex = -1;
|
||||
System::Collections::Generic::List<Command>^ commandStack = gcnew System::Collections::Generic::List<Command>();
|
||||
System::Collections::Generic::List<ICommand^>^ commandStack = gcnew System::Collections::Generic::List<ICommand^>();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ of DigiPen Institute of Technology is prohibited.
|
|||
#include "Utility/Convert.hxx"
|
||||
#include "Script.hxx"
|
||||
#include "Engine/Entity.hxx"
|
||||
#include "Serialisation/ReflectionUtilities.hxx"
|
||||
#include "Serialisation/SerialisationUtilities.hxx"
|
||||
#include "Engine/Application.hxx"
|
||||
#include "Physics/SHPhysicsSystemInterface.h"
|
||||
#include "Physics/SHPhysicsUtils.h"
|
||||
|
@ -613,7 +613,7 @@ namespace SHADE
|
|||
System::Collections::Generic::List<Script^>^ scriptList = scripts[entity];
|
||||
for each (Script^ script in scriptList)
|
||||
{
|
||||
ReflectionUtilities::Serialise(script, *yamlNode);
|
||||
SerialisationUtilities::Serialise(script, *yamlNode);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -658,7 +658,7 @@ namespace SHADE
|
|||
if (AddScriptViaNameWithRef(entity, typeName, script))
|
||||
{
|
||||
// Copy the data in
|
||||
ReflectionUtilities::Deserialise(script, node);
|
||||
SerialisationUtilities::Deserialise(script, node);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -18,31 +18,6 @@ of DigiPen Institute of Technology is prohibited.
|
|||
#include "Serialisation/ReflectionUtilities.hxx"
|
||||
// Project Includes
|
||||
#include "SerializeFieldAttribute.hxx"
|
||||
#include "Utility/Convert.hxx"
|
||||
#include "Math/Vector2.hxx"
|
||||
#include "Math/Vector3.hxx"
|
||||
#include "Utility/Debug.hxx"
|
||||
#include "Engine/GameObject.hxx"
|
||||
|
||||
/*-------------------------------------------------------------------------------------*/
|
||||
/* Macro Functions */
|
||||
/*-------------------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Macro expansion that is used in RapidJsonValueToField() to retrieve the specified
|
||||
/// member of a Vector type that is stored into a Vector named "vec".
|
||||
/// </summary>
|
||||
/// <param name="MEMBER">The name of the member to retrieve.</param>
|
||||
#define PRIMITIVE_VECTOR_FIELD_ASSIGN(MEMBER) \
|
||||
iter = jsonValue.FindMember(#MEMBER); \
|
||||
if (iter != jsonValue.MemberEnd()) \
|
||||
{ \
|
||||
vec.MEMBER = iter->value.GetDouble(); \
|
||||
} \
|
||||
|
||||
/*-------------------------------------------------------------------------------------*/
|
||||
/* File-Level Constants */
|
||||
/*-------------------------------------------------------------------------------------*/
|
||||
static const std::string_view SCRIPT_TYPE_YAMLTAG = "Type";
|
||||
|
||||
/*-------------------------------------------------------------------------------------*/
|
||||
/* Function Definitions */
|
||||
|
@ -64,202 +39,14 @@ namespace SHADE
|
|||
return fieldInfo->IsPublic || fieldInfo->GetCustomAttributes(SerializeField::typeid, true)->Length > 0;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Serialisation Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
void ReflectionUtilities::Serialise(System::Object^ object, YAML::Node& scriptListNode)
|
||||
bool ReflectionUtilities::FieldIsList(System::Reflection::FieldInfo^ fieldInfo)
|
||||
{
|
||||
using namespace System::Reflection;
|
||||
|
||||
// Create YAML object
|
||||
YAML::Node scriptNode;
|
||||
scriptNode.SetStyle(YAML::EmitterStyle::Block);
|
||||
scriptNode[SCRIPT_TYPE_YAMLTAG.data()] = Convert::ToNative(object->GetType()->FullName);
|
||||
|
||||
// Get all fields
|
||||
System::Collections::Generic::IEnumerable<FieldInfo^>^ fields = GetInstanceFields(object);
|
||||
for each (FieldInfo^ field in fields)
|
||||
{
|
||||
// Ignore private and non-SerialiseField
|
||||
if (!FieldIsSerialisable(field))
|
||||
continue;
|
||||
|
||||
// Serialise
|
||||
writeFieldIntoYaml(field, object, scriptNode);
|
||||
return IsList(fieldInfo->FieldType);
|
||||
}
|
||||
|
||||
scriptListNode.push_back(scriptNode);
|
||||
}
|
||||
void ReflectionUtilities::Deserialise(Object^ object, YAML::Node& yamlNode)
|
||||
bool ReflectionUtilities::IsList(System::Type^ type)
|
||||
{
|
||||
using namespace System::Reflection;
|
||||
|
||||
// Load the YAML
|
||||
if (!yamlNode.IsMap())
|
||||
{
|
||||
// Invalid
|
||||
Debug::LogError
|
||||
(
|
||||
System::String::Format("[ReflectionUtilities] Invalid YAML Node provided for deserialization of \"{0}\" script.",
|
||||
object->GetType()->FullName)
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Get all fields
|
||||
System::Collections::Generic::IEnumerable<FieldInfo^>^ fields = GetInstanceFields(object);
|
||||
for each (FieldInfo^ field in fields)
|
||||
{
|
||||
// Ignore private and non-SerialiseField
|
||||
if (!FieldIsSerialisable(field))
|
||||
continue;
|
||||
|
||||
// Deserialise
|
||||
const std::string FIELD_NAME = Convert::ToNative(field->Name);
|
||||
if (yamlNode[FIELD_NAME])
|
||||
{
|
||||
writeYamlIntoField(field, object, yamlNode[FIELD_NAME]);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Serialization Helper Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
void ReflectionUtilities::writeFieldIntoYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& yamlNode)
|
||||
{
|
||||
// Field YAML Node
|
||||
YAML::Node fieldNode;
|
||||
|
||||
// Retrieve string for the YAML
|
||||
const bool PRIMITIVE_SERIALIZED = fieldInsertYaml<System::Int16>(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<System::Int32>(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<System::Int64>(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<System::UInt16>(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<System::UInt32>(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<System::UInt64>(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<System::Byte>(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<bool>(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<float>(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<double>(fieldInfo, object, fieldNode);
|
||||
|
||||
// Serialization of more complex types
|
||||
if (!PRIMITIVE_SERIALIZED)
|
||||
{
|
||||
if (fieldInfo->FieldType->IsSubclassOf(System::Enum::typeid))
|
||||
{
|
||||
fieldNode = std::to_string(safe_cast<int>(fieldInfo->GetValue(object)));
|
||||
}
|
||||
else if (fieldInfo->FieldType == System::String::typeid)
|
||||
{
|
||||
System::String^ str = safe_cast<System::String^>(fieldInfo->GetValue(object));
|
||||
fieldNode = Convert::ToNative(str);
|
||||
}
|
||||
else if (fieldInfo->FieldType == Vector2::typeid)
|
||||
{
|
||||
Vector2 vec = safe_cast<Vector2>(fieldInfo->GetValue(object));
|
||||
fieldNode.SetStyle(YAML::EmitterStyle::Flow);
|
||||
fieldNode.push_back(vec.x);
|
||||
fieldNode.push_back(vec.y);
|
||||
}
|
||||
else if (fieldInfo->FieldType == Vector3::typeid)
|
||||
{
|
||||
Vector3 vec = safe_cast<Vector3>(fieldInfo->GetValue(object));
|
||||
fieldNode.SetStyle(YAML::EmitterStyle::Flow);
|
||||
fieldNode.push_back(vec.x);
|
||||
fieldNode.push_back(vec.y);
|
||||
fieldNode.push_back(vec.z);
|
||||
}
|
||||
else if (fieldInfo->FieldType == GameObject::typeid)
|
||||
{
|
||||
GameObject gameObj = safe_cast<GameObject>(fieldInfo->GetValue(object));
|
||||
fieldNode = gameObj ? gameObj.GetEntity() : MAX_EID;
|
||||
}
|
||||
else // Not any of the supported types
|
||||
{
|
||||
Debug::LogWarning(Convert::ToNative(System::String::Format
|
||||
(
|
||||
"[ReflectionUtilities] Failed to parse \"{0}\" of \"{1}\" type for serialization.",
|
||||
fieldInfo->Name, fieldInfo->FieldType)
|
||||
));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the field into YAML
|
||||
yamlNode[Convert::ToNative(fieldInfo->Name)] = fieldNode;
|
||||
}
|
||||
|
||||
void ReflectionUtilities::writeYamlIntoField(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node)
|
||||
{
|
||||
if (fieldAssignYaml<System::Int16> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<System::Int32> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<System::Int64> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<System::UInt16>(fieldInfo, object, node) ||
|
||||
fieldAssignYaml<System::UInt32>(fieldInfo, object, node) ||
|
||||
fieldAssignYaml<System::UInt64>(fieldInfo, object, node) ||
|
||||
fieldAssignYaml<System::Byte> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<bool> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<float> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<double> (fieldInfo, object, node))
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (fieldInfo->FieldType->IsSubclassOf(System::Enum::typeid))
|
||||
{
|
||||
fieldInfo->SetValue(object, node.as<int>());
|
||||
}
|
||||
else if (fieldInfo->FieldType == System::String::typeid)
|
||||
{
|
||||
fieldInfo->SetValue(object, Convert::ToCLI(node.as<std::string>()));
|
||||
}
|
||||
else if (fieldInfo->FieldType == Vector2::typeid)
|
||||
{
|
||||
if (node.IsSequence() && node.size() == 2)
|
||||
{
|
||||
Vector2 vec;
|
||||
vec.x = node[0].as<float>();
|
||||
vec.y = node[1].as<float>();
|
||||
fieldInfo->SetValue(object, vec);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug::LogWarning
|
||||
(
|
||||
System::String::Format("[ReflectionUtilities] Invalid YAML Node provided for deserialization of a Vector2 \"{0}\" field in \"{1}\" script.",
|
||||
fieldInfo->Name, object->GetType()->FullName)
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (fieldInfo->FieldType == Vector3::typeid)
|
||||
{
|
||||
if (node.IsSequence() && node.size() == 3)
|
||||
{
|
||||
Vector3 vec;
|
||||
vec.x = node[0].as<float>();
|
||||
vec.y = node[1].as<float>();
|
||||
vec.z = node[2].as<float>();
|
||||
fieldInfo->SetValue(object, vec);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug::LogWarning
|
||||
(
|
||||
System::String::Format("[ReflectionUtilities] Invalid YAML Node provided for deserialization of a Vector3 \"{0}\" field in \"{1}\" script.",
|
||||
fieldInfo->Name, object->GetType()->FullName)
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (fieldInfo->FieldType == GameObject::typeid)
|
||||
{
|
||||
const uint32_t EID = node.as<uint32_t>();
|
||||
fieldInfo->SetValue(object, EID == MAX_EID ? GameObject() : GameObject(EID));
|
||||
}
|
||||
else // Not any of the supported types
|
||||
{
|
||||
Debug::LogWarning(Convert::ToNative(System::String::Format
|
||||
(
|
||||
"[ReflectionUtilities] Failed to parse \"{0}\" of \"{1}\" type for deserialisation.",
|
||||
fieldInfo->Name, fieldInfo->FieldType)
|
||||
));
|
||||
}
|
||||
return type->IsGenericType
|
||||
&& type->GetGenericTypeDefinition() == System::Collections::Generic::List<int>::typeid->GetGenericTypeDefinition();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
/************************************************************************************//*!
|
||||
\file ReflectionUtilities.h++
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Sep 16, 2022
|
||||
\brief Contains the definition of the template functions of the managed
|
||||
ReflectionUtilities 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
|
||||
|
||||
// Primary Header
|
||||
#include "ReflectionUtilities.hxx"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Serialization Helper Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
template<typename FieldType>
|
||||
bool ReflectionUtilities::fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode)
|
||||
{
|
||||
if (fieldInfo->FieldType == FieldType::typeid)
|
||||
{
|
||||
const FieldType VALUE = safe_cast<FieldType>(fieldInfo->GetValue(object));
|
||||
fieldNode = static_cast<FieldType>(VALUE);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename FieldType>
|
||||
bool ReflectionUtilities::fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node)
|
||||
{
|
||||
return fieldAssignYaml<FieldType, ToNativeType_T<FieldType>>(fieldInfo, object, node);
|
||||
}
|
||||
|
||||
template<typename FieldType, typename CastType>
|
||||
bool ReflectionUtilities::fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node)
|
||||
{
|
||||
if (fieldInfo->FieldType == FieldType::typeid)
|
||||
{
|
||||
fieldInfo->SetValue(object, node.as<CastType>());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -13,9 +13,6 @@ of DigiPen Institute of Technology is prohibited.
|
|||
*//*************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
// External Dependencies
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -42,40 +39,17 @@ namespace SHADE
|
|||
/// True if the specified field is a candidate for serialisation.
|
||||
/// </returns>
|
||||
static bool FieldIsSerialisable(System::Reflection::FieldInfo^ fieldInfo);
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Serialisation Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Creates a JSON node that represents the specified object and its associated
|
||||
/// serialisable fields. Public fields and fields marked with the SerialiseField
|
||||
/// attribute will be serialised.
|
||||
/// Checks if the specified field is a generic List.
|
||||
/// </summary>
|
||||
/// <param name="object">The object to serialise.</param>
|
||||
static void Serialise(System::Object^ object, YAML::Node& yamlNode);
|
||||
/// <param name="fieldInfo">The field to check.</param>
|
||||
/// <returns>True if fieldInfo is describing a generic List.</returns>
|
||||
static bool FieldIsList(System::Reflection::FieldInfo^ fieldInfo);
|
||||
/// <summary>
|
||||
/// Deserialises a YAML node that contains a map of Scripts and copies the
|
||||
/// deserialised data into the specified object if there are matching fields.
|
||||
/// Checks if the specified type is a generic List type.
|
||||
/// </summary>
|
||||
/// <param name="yamlNode">
|
||||
/// The JSON string that contains the data to copy into this Script object.
|
||||
/// </param>
|
||||
/// <param name="object">The object to copy deserialised data into.</param>
|
||||
static void Deserialise(System::Object^ object, YAML::Node& yamlNode);
|
||||
|
||||
private:
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Serialization Helper Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
static void writeFieldIntoYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& yamlNode);
|
||||
template<typename FieldType>
|
||||
static bool fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode);
|
||||
static void writeYamlIntoField(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node);
|
||||
template<typename FieldType>
|
||||
static bool fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node);
|
||||
template<typename FieldType, typename CastType>
|
||||
static bool fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node);
|
||||
/// <param name="type">The type to check.</param>
|
||||
/// <returns>True if type is a generic List.</returns>
|
||||
static bool IsList(System::Type^ type);
|
||||
};
|
||||
}
|
||||
|
||||
#include "ReflectionUtilities.h++"
|
|
@ -0,0 +1,263 @@
|
|||
/************************************************************************************//*!
|
||||
\file SerialisationUtilities.cxx
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Nov 6, 2021
|
||||
\brief Contains the definition of the functions for the SerialisationUtilities
|
||||
managed static class.
|
||||
|
||||
Note: This file is written in C++17/CLI.
|
||||
|
||||
Copyright (C) 2021 DigiPen Institute of Technology.
|
||||
Reproduction or disclosure of this file or its contents without the prior written consent
|
||||
of DigiPen Institute of Technology is prohibited.
|
||||
*//*************************************************************************************/
|
||||
// Precompiled Headers
|
||||
#include "SHpch.h"
|
||||
// Primary Header
|
||||
#include "Serialisation/SerialisationUtilities.hxx"
|
||||
// Project Includes
|
||||
#include "ReflectionUtilities.hxx"
|
||||
|
||||
/*-------------------------------------------------------------------------------------*/
|
||||
/* File-Level Constants */
|
||||
/*-------------------------------------------------------------------------------------*/
|
||||
static const std::string_view SCRIPT_TYPE_YAMLTAG = "Type";
|
||||
|
||||
/*-------------------------------------------------------------------------------------*/
|
||||
/* Function Definitions */
|
||||
/*-------------------------------------------------------------------------------------*/
|
||||
namespace SHADE
|
||||
{
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Serialisation Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
void SerialisationUtilities::Serialise(System::Object^ object, YAML::Node& scriptListNode)
|
||||
{
|
||||
using namespace System::Reflection;
|
||||
|
||||
// Create YAML object
|
||||
YAML::Node scriptNode;
|
||||
scriptNode.SetStyle(YAML::EmitterStyle::Block);
|
||||
scriptNode[SCRIPT_TYPE_YAMLTAG.data()] = Convert::ToNative(object->GetType()->FullName);
|
||||
|
||||
// Get all fields
|
||||
System::Collections::Generic::IEnumerable<FieldInfo^>^ fields = ReflectionUtilities::GetInstanceFields(object);
|
||||
for each (FieldInfo^ field in fields)
|
||||
{
|
||||
// Ignore private and non-SerialiseField
|
||||
if (!ReflectionUtilities::FieldIsSerialisable(field))
|
||||
continue;
|
||||
|
||||
// Serialise
|
||||
writeFieldIntoYaml(field, object, scriptNode);
|
||||
}
|
||||
|
||||
scriptListNode.push_back(scriptNode);
|
||||
}
|
||||
void SerialisationUtilities::Deserialise(Object^ object, YAML::Node& yamlNode)
|
||||
{
|
||||
using namespace System::Reflection;
|
||||
|
||||
// Load the YAML
|
||||
if (!yamlNode.IsMap())
|
||||
{
|
||||
// Invalid
|
||||
Debug::LogError
|
||||
(
|
||||
System::String::Format("[SerialisationUtilities] Invalid YAML Node provided for deserialization of \"{0}\" script.",
|
||||
object->GetType()->FullName)
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Get all fields
|
||||
System::Collections::Generic::IEnumerable<FieldInfo^>^ fields = ReflectionUtilities::GetInstanceFields(object);
|
||||
for each (FieldInfo^ field in fields)
|
||||
{
|
||||
// Ignore private and non-SerialiseField
|
||||
if (!ReflectionUtilities::FieldIsSerialisable(field))
|
||||
continue;
|
||||
|
||||
// Deserialise
|
||||
const std::string FIELD_NAME = Convert::ToNative(field->Name);
|
||||
if (yamlNode[FIELD_NAME])
|
||||
{
|
||||
writeYamlIntoField(field, object, yamlNode[FIELD_NAME]);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Serialization Helper Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
void SerialisationUtilities::writeFieldIntoYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& yamlNode)
|
||||
{
|
||||
// Field YAML Node
|
||||
YAML::Node fieldNode;
|
||||
|
||||
// Retrieve string for the YAML
|
||||
const bool PRIMITIVE_SERIALIZED = fieldInsertYaml<System::Int16 >(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<System::Int32 >(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<System::Int64 >(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<System::UInt16>(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<System::UInt32>(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<System::UInt64>(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<System::Byte >(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<bool >(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<float >(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<double >(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<System::Enum >(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<System::String>(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<Vector2 >(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<Vector3 >(fieldInfo, object, fieldNode) ||
|
||||
fieldInsertYaml<GameObject >(fieldInfo, object, fieldNode);
|
||||
|
||||
// Serialization of more complex types
|
||||
if (!PRIMITIVE_SERIALIZED)
|
||||
{
|
||||
if (ReflectionUtilities::FieldIsList(fieldInfo))
|
||||
{
|
||||
System::Type^ listType = fieldInfo->FieldType->GenericTypeArguments[0];
|
||||
System::Collections::IList^ iList = safe_cast<System::Collections::IList^>(fieldInfo->GetValue(object));
|
||||
|
||||
|
||||
fieldNode.SetStyle(YAML::EmitterStyle::Block);
|
||||
for (int i = 0; i < iList->Count; ++i)
|
||||
{
|
||||
YAML::Node elemNode;
|
||||
if (varInsertYaml(iList[i], elemNode))
|
||||
{
|
||||
fieldNode.push_back(elemNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug::LogWarning(Convert::ToNative(System::String::Format
|
||||
(
|
||||
"[SerialisationUtilities] Failed to parse element # {2} of \"{0}\" of \"{1}\" type for serialization.",
|
||||
fieldInfo->Name, fieldInfo->FieldType, i)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Not any of the supported types
|
||||
{
|
||||
Debug::LogWarning(Convert::ToNative(System::String::Format
|
||||
(
|
||||
"[SerialisationUtilities] Failed to parse \"{0}\" of \"{1}\" type for serialization.",
|
||||
fieldInfo->Name, fieldInfo->FieldType)
|
||||
));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the field into YAML
|
||||
yamlNode[Convert::ToNative(fieldInfo->Name)] = fieldNode;
|
||||
}
|
||||
|
||||
bool SerialisationUtilities::varInsertYaml(System::Object^ object, YAML::Node& fieldNode)
|
||||
{
|
||||
const bool INSERTED =
|
||||
varInsertYamlInternal<System::Int16 >(object, fieldNode) ||
|
||||
varInsertYamlInternal<System::Int32 >(object, fieldNode) ||
|
||||
varInsertYamlInternal<System::Int64 >(object, fieldNode) ||
|
||||
varInsertYamlInternal<System::UInt16>(object, fieldNode) ||
|
||||
varInsertYamlInternal<System::UInt32>(object, fieldNode) ||
|
||||
varInsertYamlInternal<System::UInt64>(object, fieldNode) ||
|
||||
varInsertYamlInternal<System::Byte >(object, fieldNode) ||
|
||||
varInsertYamlInternal<bool >(object, fieldNode) ||
|
||||
varInsertYamlInternal<float >(object, fieldNode) ||
|
||||
varInsertYamlInternal<double >(object, fieldNode) ||
|
||||
varInsertYamlInternal<System::Enum >(object, fieldNode) ||
|
||||
varInsertYamlInternal<System::String>(object, fieldNode) ||
|
||||
varInsertYamlInternal<Vector2 >(object, fieldNode) ||
|
||||
varInsertYamlInternal<Vector3 >(object, fieldNode) ||
|
||||
varInsertYamlInternal<GameObject >(object, fieldNode);
|
||||
return INSERTED;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Deserialization Helper Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
bool SerialisationUtilities::writeYamlIntoField(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node)
|
||||
{
|
||||
const bool ASSIGNED =
|
||||
fieldAssignYaml<System::Int16> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<System::Int32> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<System::Int64> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<System::UInt16>(fieldInfo, object, node) ||
|
||||
fieldAssignYaml<System::UInt32>(fieldInfo, object, node) ||
|
||||
fieldAssignYaml<System::UInt64>(fieldInfo, object, node) ||
|
||||
fieldAssignYaml<System::Byte> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<bool> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<float> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<double> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<System::Enum> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<System::String>(fieldInfo, object, node) ||
|
||||
fieldAssignYaml<Vector2> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<Vector3> (fieldInfo, object, node) ||
|
||||
fieldAssignYaml<GameObject> (fieldInfo, object, node);
|
||||
if (!ASSIGNED)
|
||||
{
|
||||
if (ReflectionUtilities::FieldIsList(fieldInfo))
|
||||
{
|
||||
System::Type^ elemType = fieldInfo->FieldType->GenericTypeArguments[0];
|
||||
System::Collections::IList^ iList = safe_cast<System::Collections::IList^>(fieldInfo->GetValue(object));
|
||||
if (node.IsSequence())
|
||||
{
|
||||
// Get list size
|
||||
const int LIST_SIZE = static_cast<int>(node.size());
|
||||
if (LIST_SIZE > 0)
|
||||
{
|
||||
// Get list type
|
||||
array<System::Type^>^ typeList = gcnew array<System::Type^>{ elemType };
|
||||
System::Type^ listType = System::Collections::Generic::List<int>::typeid->GetGenericTypeDefinition()->MakeGenericType(typeList);
|
||||
// Create a list of the specified type
|
||||
array<int>^ params = gcnew array<int>{ node.size() };
|
||||
System::Collections::IList^ list = safe_cast<System::Collections::IList^>
|
||||
(
|
||||
System::Activator::CreateInstance(listType, params)
|
||||
);
|
||||
|
||||
// Populate the list
|
||||
for (int i = 0; i < LIST_SIZE; ++i)
|
||||
{
|
||||
// Create the object
|
||||
System::Object^ obj = System::Activator::CreateInstance(elemType);
|
||||
|
||||
// Set it's value
|
||||
if (varAssignYaml(obj, node[i]))
|
||||
{
|
||||
list->Add(obj);
|
||||
}
|
||||
}
|
||||
fieldInfo->SetValue(object, list);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return ASSIGNED;
|
||||
}
|
||||
|
||||
bool SerialisationUtilities::varAssignYaml(System::Object^% object, YAML::Node& node)
|
||||
{
|
||||
const bool DESERIALISED =
|
||||
varAssignYamlInternal<System::Int16> (object, node) ||
|
||||
varAssignYamlInternal<System::Int32> (object, node) ||
|
||||
varAssignYamlInternal<System::Int64> (object, node) ||
|
||||
varAssignYamlInternal<System::UInt16>(object, node) ||
|
||||
varAssignYamlInternal<System::UInt32>(object, node) ||
|
||||
varAssignYamlInternal<System::UInt64>(object, node) ||
|
||||
varAssignYamlInternal<System::Byte> (object, node) ||
|
||||
varAssignYamlInternal<bool> (object, node) ||
|
||||
varAssignYamlInternal<float> (object, node) ||
|
||||
varAssignYamlInternal<double> (object, node) ||
|
||||
varAssignYamlInternal<System::Enum> (object, node) ||
|
||||
varAssignYamlInternal<System::String>(object, node) ||
|
||||
varAssignYamlInternal<Vector2> (object, node) ||
|
||||
varAssignYamlInternal<Vector3> (object, node) ||
|
||||
varAssignYamlInternal<GameObject> (object, node);
|
||||
return DESERIALISED;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
/************************************************************************************//*!
|
||||
\file SerialisationUtilities.h++
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Sep 16, 2022
|
||||
\brief Contains the definition of the template functions of the managed
|
||||
ReflectionUtilities 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
|
||||
|
||||
// Primary Header
|
||||
#include "SerialisationUtilities.hxx"
|
||||
// Project Includes
|
||||
#include "Utility/Convert.hxx"
|
||||
#include "Utility/Debug.hxx"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Serialization Helper Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
template<typename FieldType>
|
||||
bool SerialisationUtilities::fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode)
|
||||
{
|
||||
Debug::Log(FieldType::typeid->Name);
|
||||
return varInsertYamlInternal<FieldType>(fieldInfo->GetValue(object), fieldNode);
|
||||
}
|
||||
template<typename FieldType>
|
||||
bool SerialisationUtilities::varInsertYamlInternal(System::Object^ object, YAML::Node& fieldNode)
|
||||
{
|
||||
if constexpr (std::is_same_v<FieldType, System::Enum>)
|
||||
{
|
||||
if (object->GetType()->IsSubclassOf(System::Enum::typeid))
|
||||
{
|
||||
fieldNode = std::to_string(safe_cast<int>(object));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<FieldType, System::String>)
|
||||
{
|
||||
if (object->GetType() == System::String::typeid)
|
||||
{
|
||||
System::String^ str = safe_cast<System::String^>(object);
|
||||
fieldNode = Convert::ToNative(str);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<FieldType, Vector2>)
|
||||
{
|
||||
if (object->GetType() == Vector2::typeid)
|
||||
{
|
||||
Vector2 vec = safe_cast<Vector2>(object);
|
||||
fieldNode.SetStyle(YAML::EmitterStyle::Flow);
|
||||
fieldNode.push_back(vec.x);
|
||||
fieldNode.push_back(vec.y);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<FieldType, Vector3>)
|
||||
{
|
||||
if (object->GetType() == Vector3::typeid)
|
||||
{
|
||||
Vector3 vec = safe_cast<Vector3>(object);
|
||||
fieldNode.SetStyle(YAML::EmitterStyle::Flow);
|
||||
fieldNode.push_back(vec.x);
|
||||
fieldNode.push_back(vec.y);
|
||||
fieldNode.push_back(vec.z);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<FieldType, GameObject>)
|
||||
{
|
||||
if (object->GetType() == GameObject::typeid)
|
||||
{
|
||||
GameObject gameObj = safe_cast<GameObject>(object);
|
||||
fieldNode = gameObj ? gameObj.GetEntity() : MAX_EID;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (object->GetType() == FieldType::typeid)
|
||||
{
|
||||
FieldType value = safe_cast<FieldType>(object);
|
||||
fieldNode = static_cast<FieldType>(value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Deserialization Helper Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
template<typename FieldType>
|
||||
bool SerialisationUtilities::fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node)
|
||||
{
|
||||
System::Object^ valueObj = fieldInfo->GetValue(object);
|
||||
if (varAssignYamlInternal<FieldType>(valueObj, node))
|
||||
{
|
||||
fieldInfo->SetValue(object, valueObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename FieldType, typename CastType>
|
||||
bool SerialisationUtilities::varAssignYamlInternal(System::Object^% object, YAML::Node& node)
|
||||
{
|
||||
if constexpr (std::is_same_v<FieldType, System::Enum>)
|
||||
{
|
||||
if (object->GetType()->IsSubclassOf(System::Enum::typeid))
|
||||
{
|
||||
object = node.as<int>();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<FieldType, System::Collections::IList>)
|
||||
{
|
||||
if (ReflectionUtilities::FieldIsList(fieldInfo))
|
||||
{
|
||||
System::Collections::IList^ iList = safe_cast<System::Collections::IList^>(object);
|
||||
object = gcnew
|
||||
if (node.IsSequence() )
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (object->GetType() == FieldType::typeid)
|
||||
{
|
||||
if constexpr (std::is_same_v<FieldType, System::String>)
|
||||
{
|
||||
object = Convert::ToCLI(node.as<std::string>());
|
||||
}
|
||||
else if constexpr (std::is_same_v<FieldType, Vector2>)
|
||||
{
|
||||
if (node.IsSequence() && node.size() == 2)
|
||||
{
|
||||
Vector2 vec;
|
||||
vec.x = node[0].as<float>();
|
||||
vec.y = node[1].as<float>();
|
||||
object = vec;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<FieldType, Vector3>)
|
||||
{
|
||||
if (node.IsSequence() && node.size() == 3)
|
||||
{
|
||||
Vector3 vec;
|
||||
vec.x = node[0].as<float>();
|
||||
vec.y = node[1].as<float>();
|
||||
vec.z = node[2].as<float>();
|
||||
object = vec;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<FieldType, GameObject>)
|
||||
{
|
||||
const uint32_t EID = node.as<uint32_t>();
|
||||
object = (EID == MAX_EID ? GameObject() : GameObject(EID));
|
||||
}
|
||||
else
|
||||
{
|
||||
object = node.as<CastType>();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/************************************************************************************//*!
|
||||
\file SerialisationUtilities.hxx
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Nov 6, 2021
|
||||
\brief Contains the definition of the managed SerialisationUtilities static
|
||||
class.
|
||||
|
||||
Note: This file is written in C++17/CLI.
|
||||
|
||||
Copyright (C) 2021 DigiPen Institute of Technology.
|
||||
Reproduction or disclosure of this file or its contents without the prior written consent
|
||||
of DigiPen Institute of Technology is prohibited.
|
||||
*//*************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
// External Dependencies
|
||||
#include <yaml-cpp/yaml.h>
|
||||
// Project Includes
|
||||
#include "Math/Vector2.hxx"
|
||||
#include "Math/Vector3.hxx"
|
||||
#include "Engine/GameObject.hxx"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains useful static functions for working with Serialisation of Managed data.
|
||||
/// </summary>
|
||||
private ref class SerialisationUtilities abstract sealed
|
||||
{
|
||||
public:
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Serialisation Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Creates a JSON node that represents the specified object and its associated
|
||||
/// serialisable fields. Public fields and fields marked with the SerialiseField
|
||||
/// attribute will be serialised.
|
||||
/// </summary>
|
||||
/// <param name="object">The object to serialise.</param>
|
||||
static void Serialise(System::Object^ object, YAML::Node& yamlNode);
|
||||
/// <summary>
|
||||
/// Deserialises a YAML node that contains a map of Scripts and copies the
|
||||
/// deserialised data into the specified object if there are matching fields.
|
||||
/// </summary>
|
||||
/// <param name="yamlNode">
|
||||
/// The JSON string that contains the data to copy into this Script object.
|
||||
/// </param>
|
||||
/// <param name="object">The object to copy deserialised data into.</param>
|
||||
static void Deserialise(System::Object^ object, YAML::Node& yamlNode);
|
||||
|
||||
private:
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Serialization Helper Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
static void writeFieldIntoYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& yamlNode);
|
||||
template<typename FieldType>
|
||||
static bool fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode);
|
||||
static bool varInsertYaml(System::Object^ object, YAML::Node& fieldNode);
|
||||
template<typename FieldType>
|
||||
static bool varInsertYamlInternal(System::Object^ object, YAML::Node& fieldNode);
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Deserialization Helper Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
static bool writeYamlIntoField(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node);
|
||||
template<typename FieldType>
|
||||
static bool fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node);
|
||||
static bool varAssignYaml(System::Object^% object, YAML::Node& node);
|
||||
template<typename FieldType, typename CastType = ToNativeType_T<FieldType>>
|
||||
static bool varAssignYamlInternal(System::Object^% object, YAML::Node& node);
|
||||
};
|
||||
}
|
||||
|
||||
#include "SerialisationUtilities.h++"
|
|
@ -152,6 +152,40 @@ namespace SHADE
|
|||
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified type is matching between native C++ and the managed type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type to check.</typeparam>
|
||||
template<typename T>
|
||||
struct IsPrimitiveTypeMatches : public std::integral_constant
|
||||
<
|
||||
bool,
|
||||
std::is_same_v<System::Int16 , typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<System::Int32 , typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<System::Int64 , typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<System::UInt16, typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<System::UInt32, typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<System::UInt64, typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<System::Byte , typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<bool , typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<double , typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<float , typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<int8_t , typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<int16_t , typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<int32_t , typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<int64_t , typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<uint16_t , typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<uint32_t , typename std::remove_cv_t<T>> ||
|
||||
std::is_same_v<uint64_t , typename std::remove_cv_t<T>>
|
||||
>
|
||||
{};
|
||||
/// <summary>
|
||||
/// Short hand for IsPrimitiveTypeMatches::value
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type to check.</typeparam>
|
||||
template<typename T>
|
||||
inline constexpr bool IsPrimitiveTypeMatches_V = IsPrimitiveTypeMatches<T>::value;
|
||||
|
||||
/// <summary>
|
||||
/// Type Transformer for managed types to native types.
|
||||
/// </summary>
|
||||
|
@ -163,6 +197,7 @@ namespace SHADE
|
|||
{
|
||||
public:
|
||||
using Value = void;
|
||||
static bool IsDefined() { return is_same_v<ManagedType, Value>; }
|
||||
};
|
||||
template<> struct ToNativeType<System::Int16> { using Value = int16_t; };
|
||||
template<> struct ToNativeType<System::Int32> { using Value = int32_t; };
|
||||
|
@ -195,17 +230,18 @@ namespace SHADE
|
|||
{
|
||||
public:
|
||||
using Value = void;
|
||||
static bool IsDefined() { return is_same_v<NativeType, Value>; }
|
||||
};
|
||||
template<> struct ToManagedType<int8_t> { using Value = System::Byte; };
|
||||
template<> struct ToManagedType<int16_t> { using Value = System::Int16; };
|
||||
template<> struct ToManagedType<int32_t> { using Value = System::Int32; };
|
||||
template<> struct ToManagedType<int64_t> { using Value = System::Int64; };
|
||||
template<> struct ToManagedType<int8_t > { using Value = System::Byte; };
|
||||
template<> struct ToManagedType<int16_t > { using Value = System::Int16; };
|
||||
template<> struct ToManagedType<int32_t > { using Value = System::Int32; };
|
||||
template<> struct ToManagedType<int64_t > { using Value = System::Int64; };
|
||||
template<> struct ToManagedType<uint16_t> { using Value = System::UInt16; };
|
||||
template<> struct ToManagedType<uint32_t> { using Value = System::UInt32; };
|
||||
template<> struct ToManagedType<uint64_t> { using Value = System::UInt64; };
|
||||
template<> struct ToManagedType<bool> { using Value = bool; };
|
||||
template<> struct ToManagedType<double> { using Value = double; };
|
||||
template<> struct ToManagedType<float> { using Value = float; };
|
||||
template<> struct ToManagedType<bool > { using Value = bool; };
|
||||
template<> struct ToManagedType<double > { using Value = double; };
|
||||
template<> struct ToManagedType<float > { using Value = float; };
|
||||
|
||||
/// <summary>
|
||||
/// Alias for ToManagedType::Value
|
||||
|
|
Loading…
Reference in New Issue