feat: add new fancy reflection

This commit is contained in:
2026-11-01 23:12:00 +01:00
parent 08acd84404
commit 6a85d17899
58 changed files with 380 additions and 1055 deletions
+20 -47
View File
@@ -4,6 +4,7 @@
#include <destrum/Assets/AssetTypes.h>
#include <destrum/Audio/AudioSystem.h>
#include <destrum/ObjectModel/Transform.h>
#include <destrum/Serialization/Reflect.h>
#include <destrum/Util/ImGuiUtils.h>
#include <AL/al.h>
@@ -158,18 +159,25 @@ void AudioSource::SetDistances(float minDistance, float maxDistance)
ApplyProperties();
}
void AudioSource::Reflect(FieldVisitor& v)
{
v.Property("volume", m_Volume, [this](float volume) { SetVolume(volume); }, {.speed = 0.01f, .min = 0.0, .max = 2.0});
v.Property("pitch", m_Pitch, [this](float pitch) { SetPitch(pitch); }, {.speed = 0.01f, .min = 0.1, .max = 3.0});
v.Property("loop", m_Loop, [this](bool loop) { SetLoop(loop); });
v.Field("playOnStart", m_PlayOnStart);
v.Property("spatial", m_Spatial, [this](bool spatial) { SetSpatial(spatial); });
v.Property("minDistance", m_MinDistance, [this](float minDistance) { SetDistances(minDistance, m_MaxDistance); },
{.speed = 0.05f, .min = 0.01, .format = "%.2f m", .hidden = !m_Spatial, .tooltip = "Full volume up to this distance"});
v.Property("maxDistance", m_MaxDistance, [this](float maxDistance) { SetDistances(m_MinDistance, maxDistance); },
{.speed = 0.1f, .min = 0.01, .format = "%.2f m", .hidden = !m_Spatial,
.tooltip = "The sound stops getting quieter past this distance"});
}
nlohmann::json AudioSource::Serialize() const
{
return {
{"clip", m_ClipPath.empty() ? nlohmann::json(nullptr) : AssetDatabase::GetInstance().WriteReference(m_ClipPath)},
{"volume", m_Volume},
{"pitch", m_Pitch},
{"loop", m_Loop},
{"playOnStart", m_PlayOnStart},
{"spatial", m_Spatial},
{"minDistance", m_MinDistance},
{"maxDistance", m_MaxDistance},
};
nlohmann::json data = Component::Serialize();
data["clip"] = m_ClipPath.empty() ? nlohmann::json(nullptr) : AssetDatabase::GetInstance().WriteReference(m_ClipPath);
return data;
}
void AudioSource::Deserialize(const nlohmann::json& data)
@@ -183,13 +191,7 @@ void AudioSource::Deserialize(const nlohmann::json& data)
SetClip({});
}
}
m_Loop = data.value("loop", m_Loop);
m_PlayOnStart = data.value("playOnStart", m_PlayOnStart);
m_Spatial = data.value("spatial", m_Spatial);
SetVolume(data.value("volume", m_Volume));
SetPitch(data.value("pitch", m_Pitch));
SetDistances(data.value("minDistance", m_MinDistance), data.value("maxDistance", m_MaxDistance));
Component::Deserialize(data);
}
void AudioSource::ImGuiInspector()
@@ -218,36 +220,7 @@ void AudioSource::ImGuiInspector()
m_Clip->GetSampleRate(), m_Clip->GetDuration());
}
float volume = m_Volume;
if (ImGui::SliderFloat("Volume", &volume, 0.0f, 2.0f)) {
SetVolume(volume);
}
float pitch = m_Pitch;
if (ImGui::SliderFloat("Pitch", &pitch, 0.1f, 3.0f)) {
SetPitch(pitch);
}
bool loop = m_Loop;
if (ImGui::Checkbox("Loop", &loop)) {
SetLoop(loop);
}
ImGui::Checkbox("Play On Start", &m_PlayOnStart);
bool spatial = m_Spatial;
if (ImGui::Checkbox("Spatial", &spatial)) {
SetSpatial(spatial);
}
if (m_Spatial) {
float minDistance = m_MinDistance;
float maxDistance = m_MaxDistance;
bool changed = ImGui::DragFloat("Min Distance", &minDistance, 0.05f, 0.01f, 1000.0f, "%.2f m");
ImGui::SetItemTooltip("Full volume up to this distance");
changed |= ImGui::DragFloat("Max Distance", &maxDistance, 0.1f, 0.01f, 10000.0f, "%.2f m");
ImGui::SetItemTooltip("The sound stops getting quieter past this distance");
if (changed) {
SetDistances(minDistance, maxDistance);
}
}
Component::ImGuiInspector();
ImGui::BeginDisabled(m_ClipPath.empty());
if (ImGui::Button(IsPlaying() ? "Restart" : "Play")) {
+6 -26
View File
@@ -3,6 +3,7 @@
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/ObjectModel/Transform.h>
#include <destrum/Scene/Scene.h>
#include <destrum/Serialization/Reflect.h>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
@@ -54,19 +55,10 @@ bool CameraComponent::IsCurrent() const
return scene != nullptr && scene->GetCurrentCamera() == this;
}
nlohmann::json CameraComponent::Serialize() const {
return {
{"fieldOfView", m_FieldOfView},
{"zNear", m_ZNear},
{"zFar", m_ZFar}
};
}
void CameraComponent::Deserialize(const nlohmann::json& data) {
if (data.contains("fieldOfView")) {
SetFieldOfView(data.at("fieldOfView").get<float>());
}
SetClipPlanes(data.value("zNear", m_ZNear), data.value("zFar", m_ZFar));
void CameraComponent::Reflect(FieldVisitor& v) {
v.Property("fieldOfView", m_FieldOfView, [this](float fov) { SetFieldOfView(fov); }, {.speed = 0.5f, .min = 1.0, .max = 179.0});
v.Property("zNear", m_ZNear, [this](float zNear) { SetClipPlanes(zNear, m_ZFar); }, {.speed = 0.01f, .min = 0.0001, .label = "Near"});
v.Property("zFar", m_ZFar, [this](float zFar) { SetClipPlanes(m_ZNear, zFar); }, {.speed = 1.0f, .min = 0.1, .label = "Far"});
}
void CameraComponent::ImGuiInspector() {
@@ -74,17 +66,5 @@ void CameraComponent::ImGuiInspector() {
if (ImGui::Checkbox("Current Camera", &current) && current) {
MakeCurrent();
}
float fieldOfView = m_FieldOfView;
if (ImGui::DragFloat("Field Of View", &fieldOfView, 0.5f, 1.0f, 179.0f)) {
SetFieldOfView(fieldOfView);
}
float zNear = m_ZNear;
float zFar = m_ZFar;
const bool nearChanged = ImGui::DragFloat("Near", &zNear, 0.01f, 0.0001f, 100.0f);
const bool farChanged = ImGui::DragFloat("Far", &zFar, 1.0f, 0.1f, 100000.0f);
if (nearChanged || farChanged) {
SetClipPlanes(zNear, zFar);
}
Component::ImGuiInspector();
}
+16 -85
View File
@@ -2,7 +2,7 @@
#include <destrum/Graphics/RenderContext.h>
#include <destrum/ObjectModel/Transform.h>
#include <destrum/Serialization/GlmJson.h>
#include <destrum/Serialization/Reflect.h>
#include <glm/gtc/constants.hpp>
#include <glm/gtc/quaternion.hpp>
@@ -11,8 +11,6 @@
#include <algorithm>
#include <array>
#include <imgui.h>
LightComponent::LightComponent(GameObject& parent, LightType type)
: Component(parent, "LightComponent")
, m_Type(type)
@@ -52,86 +50,19 @@ void LightComponent::Render(const RenderContext& ctx)
});
}
nlohmann::json LightComponent::Serialize() const {
return {
{"type", static_cast<std::uint32_t>(m_Type)},
{"color", m_Color},
{"intensity", m_Intensity},
{"range", m_Range},
{"innerConeAngle", m_InnerConeAngle},
{"outerConeAngle", m_OuterConeAngle},
{"castShadows", m_CastShadows}
};
}
void LightComponent::Deserialize(const nlohmann::json& data) {
if (data.contains("type")) {
const auto type = data.at("type").get<std::uint32_t>();
if (type <= static_cast<std::uint32_t>(LightType::Spot)) {
m_Type = static_cast<LightType>(type);
}
}
m_Color = data.value("color", m_Color);
if (data.contains("intensity")) {
SetIntensity(data.at("intensity").get<float>());
}
if (data.contains("range")) {
SetRange(data.at("range").get<float>());
}
float innerConeAngle = m_InnerConeAngle;
float outerConeAngle = m_OuterConeAngle;
if (data.contains("innerConeAngle")) {
innerConeAngle = data.at("innerConeAngle").get<float>();
}
if (data.contains("outerConeAngle")) {
outerConeAngle = data.at("outerConeAngle").get<float>();
}
SetConeAngles(innerConeAngle, outerConeAngle);
if (data.contains("castShadows")) {
m_CastShadows = data.at("castShadows").get<bool>();
}
}
void LightComponent::ImGuiInspector() {
constexpr std::array typeNames{"Directional", "Point", "Spot"};
int type = static_cast<int>(m_Type);
if (ImGui::Combo("Type", &type, typeNames.data(), static_cast<int>(typeNames.size()))) {
SetType(static_cast<LightType>(type));
}
ImGui::ColorEdit3("Color", &m_Color.x);
float intensity = m_Intensity;
if (ImGui::DragFloat("Intensity", &intensity, 0.05f, 0.0f)) {
SetIntensity(intensity);
}
if (m_Type == LightType::Directional) {
ImGui::Checkbox("Cast Shadows", &m_CastShadows);
ImGui::SameLine();
ImGui::TextDisabled("(first directional light only)");
return;
}
if (m_Type == LightType::Spot) {
ImGui::Checkbox("Cast Shadows", &m_CastShadows);
}
float range = m_Range;
if (ImGui::DragFloat("Range", &range, 0.05f, 0.01f)) {
SetRange(range);
}
if (m_Type == LightType::Spot) {
float innerConeAngle = m_InnerConeAngle;
float outerConeAngle = m_OuterConeAngle;
bool changed = ImGui::SliderAngle("Inner Cone", &innerConeAngle, 0.0f, 90.0f);
changed |= ImGui::SliderAngle("Outer Cone", &outerConeAngle, 0.0f, 90.0f);
if (changed) {
SetConeAngles(innerConeAngle, outerConeAngle);
}
}
void LightComponent::Reflect(FieldVisitor& v) {
static constexpr std::array<const char*, 3> TypeNames{"Directional", "Point", "Spot"};
const bool directional = m_Type == LightType::Directional;
const bool spot = m_Type == LightType::Spot;
v.Field("type", m_Type, {.enumNames = TypeNames});
v.Field("color", m_Color, {.color = true});
v.Property("intensity", m_Intensity, [this](float intensity) { SetIntensity(intensity); }, {.speed = 0.05f, .min = 0.0});
v.Field("castShadows", m_CastShadows, {.hidden = !directional && !spot,
.tooltip = directional ? "Only the first directional light casts shadows" : nullptr});
v.Property("range", m_Range, [this](float range) { SetRange(range); }, {.speed = 0.05f, .min = 0.01, .hidden = directional});
v.Property("innerConeAngle", m_InnerConeAngle, [this](float angle) { SetConeAngles(angle, m_OuterConeAngle); },
{.min = 0.0, .max = glm::half_pi<double>(), .angle = true, .hidden = !spot, .label = "Inner Cone"});
v.Property("outerConeAngle", m_OuterConeAngle, [this](float angle) { SetConeAngles(m_InnerConeAngle, angle); },
{.min = 0.0, .max = glm::half_pi<double>(), .angle = true, .hidden = !spot, .label = "Outer Cone"});
}
+24 -88
View File
@@ -7,10 +7,9 @@
#include "destrum/ObjectModel/GameObject.h"
#include "destrum/ObjectModel/Transform.h"
#include "destrum/Components/MeshRendererComponent.h"
#include "destrum/Serialization/GlmJson.h"
#include "destrum/Serialization/Reflect.h"
#include "destrum/Util/GameState.h"
#include "destrum/Util/DeltaTime.h"
#include "destrum/Util/ImGuiUtils.h"
#include <imgui.h>
@@ -126,40 +125,33 @@ void OrbitAndSpin::Start() {
}
nlohmann::json OrbitAndSpin::Serialize() const {
return {
{"radius", m_Radius},
{"center", m_Center},
{"orbitAxis", m_OrbitAxis},
{"orbitSpeed", m_OrbitSpeed},
{"orbitAngle", m_OrbitAngle},
{"orbitPhase", m_OrbitPhase},
{"growPhase", m_GrowPhase},
{"growSpeed", m_GrowSpeed},
{"growMin", m_GrowMin},
{"growMax", m_GrowMax},
{"spinAxis", m_SpinAxis},
{"spinSpeed", m_SpinSpeed},
{"baseScale", m_BaseScale}
};
void OrbitAndSpin::Reflect(FieldVisitor& v) {
v.Field("radius", m_Radius, {.speed = 0.05f, .min = 0.0});
v.Field("center", m_Center);
v.Field("orbitAxis", m_OrbitAxis);
v.Field("orbitSpeed", m_OrbitSpeed, {.speed = 0.05f});
v.Field("orbitPhase", m_OrbitPhase, {.speed = 0.05f});
v.Field("orbitAngle", m_OrbitAngle, {.format = "%.2f rad", .readOnly = true});
v.Field("growSpeed", m_GrowSpeed, {.speed = 0.05f, .min = 0.0});
v.Field("growMin", m_GrowMin, {.speed = 0.01f, .min = 0.0});
v.Field("growMax", m_GrowMax, {.speed = 0.01f, .min = 0.0});
v.Field("baseScale", m_BaseScale, {.speed = 0.05f});
v.Field("spinAxis", m_SpinAxis);
v.Field("spinSpeed", m_SpinSpeed, {.speed = 0.05f});
v.Field("growPhase", m_GrowPhase, {.format = "%.2f rad", .readOnly = true});
}
void OrbitAndSpin::Deserialize(const nlohmann::json& data) {
m_Radius = data.value("radius", m_Radius);
m_Center = data.value("center", m_Center);
m_OrbitAxis = data.value("orbitAxis", m_OrbitAxis);
m_OrbitSpeed = data.value("orbitSpeed", m_OrbitSpeed);
m_OrbitAngle = data.value("orbitAngle", m_OrbitAngle);
m_OrbitPhase = data.value("orbitPhase", m_OrbitPhase);
m_GrowPhase = data.value("growPhase", m_GrowPhase);
m_GrowSpeed = data.value("growSpeed", m_GrowSpeed);
m_GrowMin = data.value("growMin", m_GrowMin);
m_GrowMax = data.value("growMax", m_GrowMax);
m_SpinAxis = data.value("spinAxis", m_SpinAxis);
m_BaseScale = data.value("baseScale", m_BaseScale);
Component::Deserialize(data);
m_BaseScaleLoaded = data.contains("baseScale");
m_SpinSpeed = data.value("spinSpeed", m_SpinSpeed);
Sanitize();
}
void OrbitAndSpin::OnFieldsEdited() {
Sanitize();
}
void OrbitAndSpin::Sanitize() {
if (m_GrowMin > m_GrowMax) {
std::swap(m_GrowMin, m_GrowMax);
}
@@ -172,62 +164,6 @@ void OrbitAndSpin::Deserialize(const nlohmann::json& data) {
}
void OrbitAndSpin::ImGuiInspector() {
float radius = m_Radius;
if (ImGui::DragFloat("Radius", &radius, 0.05f, 0.0f)) {
SetRadius(radius);
}
glm::vec3 center = m_Center;
if (ImGuiUtils::DrawVec3Control("Center", center)) {
SetCenter(center);
}
glm::vec3 orbitAxis = m_OrbitAxis;
if (ImGuiUtils::DrawVec3Control("Orbit axis", orbitAxis)) {
if (glm::dot(orbitAxis, orbitAxis) >= 1e-8f) {
m_OrbitAxis = glm::normalize(orbitAxis);
BuildOrbitBasis();
}
}
float orbitSpeed = m_OrbitSpeed;
if (ImGui::DragFloat("Orbit speed", &orbitSpeed, 0.05f)) {
m_OrbitSpeed = orbitSpeed;
}
ImGui::DragFloat("Orbit phase", &m_OrbitPhase, 0.05f);
ImGui::Text("Orbit angle: %.2f rad", m_OrbitAngle);
float growSpeed = m_GrowSpeed;
if (ImGui::DragFloat("Grow speed", &growSpeed, 0.05f, 0.0f)) {
m_GrowSpeed = growSpeed;
}
float growMin = m_GrowMin;
if (ImGui::DragFloat("Grow min", &growMin, 0.01f, 0.0f)) {
m_GrowMin = std::min(growMin, m_GrowMax);
}
float growMax = m_GrowMax;
if (ImGui::DragFloat("Grow max", &growMax, 0.01f, 0.0f)) {
m_GrowMax = std::max(growMax, m_GrowMin);
}
glm::vec3 baseScale = m_BaseScale;
if (ImGuiUtils::DrawVec3Control("Base scale", baseScale, 0.05f)) {
m_BaseScale = baseScale;
}
glm::vec3 spinAxis = m_SpinAxis;
if (ImGuiUtils::DrawVec3Control("Spin axis", spinAxis)) {
if (glm::dot(spinAxis, spinAxis) >= 1e-8f) {
m_SpinAxis = glm::normalize(spinAxis);
}
}
float spinSpeed = m_SpinSpeed;
if (ImGui::DragFloat("Spin speed", &spinSpeed, 0.05f)) {
m_SpinSpeed = spinSpeed;
}
ImGui::Text("Grow phase: %.2f rad", m_GrowPhase);
Component::ImGuiInspector();
ImGui::Text("Material ID: %u", m_MaterialID);
}
+4 -23
View File
@@ -1,32 +1,13 @@
#include <destrum/Components/Physics/BoxCollider.h>
#include <destrum/Graphics/DebugDraw.h>
#include <destrum/Util/ImGuiUtils.h>
#include <imgui.h>
#include <destrum/Serialization/Reflect.h>
void BoxCollider::DrawDebugLines(const glm::vec4& color) const {
DebugDraw::Box(GetWorldCenter(), GetWorldRotation(), m_HalfExtents, color);
}
nlohmann::json BoxCollider::Serialize() const {
auto data = SerializeCollider();
data["halfExtents"] = m_HalfExtents;
return data;
}
void BoxCollider::Deserialize(const nlohmann::json& data) {
DeserializeCollider(data);
if (data.contains("halfExtents")) {
SetHalfExtents(data.at("halfExtents").get<glm::vec3>());
}
}
void BoxCollider::ImGuiInspector() {
Collider::ImGuiInspector();
glm::vec3 halfExtents = m_HalfExtents;
if (ImGuiUtils::DrawVec3Control("Half extents", halfExtents)) {
SetHalfExtents(halfExtents);
}
void BoxCollider::Reflect(FieldVisitor& v) {
Collider::Reflect(v);
v.Property("halfExtents", m_HalfExtents, [this](const glm::vec3& halfExtents) { SetHalfExtents(halfExtents); }, {.min = 0.0001});
}
@@ -2,22 +2,14 @@
#include <destrum/Graphics/DebugDraw.h>
#include <imgui.h>
#include <destrum/Serialization/Reflect.h>
void CapsuleCollider::DrawDebugLines(const glm::vec4& color) const {
DebugDraw::Capsule(GetWorldCenter(), GetWorldRotation(), m_Radius, m_Height, color);
}
void CapsuleCollider::ImGuiInspector() {
Collider::ImGuiInspector();
float radius = m_Radius;
if (ImGui::DragFloat("Radius", &radius, 0.05f, 0.0001f)) {
SetRadius(radius);
}
float height = m_Height;
if (ImGui::DragFloat("Height", &height, 0.05f, 0.0001f)) {
SetHeight(height);
}
void CapsuleCollider::Reflect(FieldVisitor& v) {
Collider::Reflect(v);
v.Property("radius", m_Radius, [this](float radius) { SetRadius(radius); }, {.speed = 0.05f, .min = 0.0001});
v.Property("height", m_Height, [this](float height) { SetHeight(height); }, {.speed = 0.05f, .min = 0.0001});
}
+4 -13
View File
@@ -1,9 +1,7 @@
#include <destrum/Components/Physics/Collider.h>
#include <imgui.h>
#include <destrum/ObjectModel/Transform.h>
#include <destrum/Util/ImGuiUtils.h>
#include <destrum/Serialization/Reflect.h>
glm::vec3 Collider::GetWorldCenter() const {
Transform& transform = GetTransform();
@@ -14,14 +12,7 @@ glm::quat Collider::GetWorldRotation() const {
return GetTransform().GetWorldRotation();
}
void Collider::ImGuiInspector() {
bool isTrigger = m_IsTrigger;
if (ImGui::Checkbox("Is trigger", &isTrigger)) {
SetTrigger(isTrigger);
}
glm::vec3 centerOffset = m_CenterOffset;
if (ImGuiUtils::DrawVec3Control("Center offset", centerOffset)) {
SetCenterOffset(centerOffset);
}
void Collider::Reflect(FieldVisitor& v) {
v.Property("isTrigger", m_IsTrigger, [this](bool trigger) { SetTrigger(trigger); });
v.Property("centerOffset", m_CenterOffset, [this](const glm::vec3& offset) { SetCenterOffset(offset); });
}
@@ -1,29 +1,13 @@
#include <destrum/Components/Physics/ConvexHullCollider.h>
#include <imgui.h>
#include <destrum/Serialization/Reflect.h>
nlohmann::json ConvexHullCollider::Serialize() const {
auto data = SerializeCollider();
data["maxVertices"] = m_MaxVertices;
return data;
}
void ConvexHullCollider::Deserialize(const nlohmann::json& data) {
DeserializeCollider(data);
if (data.contains("maxVertices")) {
SetMaxVertices(data.at("maxVertices").get<int>());
}
void ConvexHullCollider::Reflect(FieldVisitor& v) {
Collider::Reflect(v);
v.Property("maxVertices", m_MaxVertices, [this](int maxVertices) { SetMaxVertices(maxVertices); },
{.speed = 1.0f, .min = MinVertices, .max = MaxVertices});
}
void ConvexHullCollider::DrawDebugLines(const glm::vec4& color) const {
DrawConvexHull(m_MaxVertices, color);
}
void ConvexHullCollider::ImGuiInspector() {
MeshShapeCollider::ImGuiInspector();
int maxVertices = m_MaxVertices;
if (ImGui::SliderInt("Max vertices", &maxVertices, MinVertices, MaxVertices)) {
SetMaxVertices(maxVertices);
}
}
@@ -2,22 +2,14 @@
#include <destrum/Graphics/DebugDraw.h>
#include <imgui.h>
#include <destrum/Serialization/Reflect.h>
void CylinderCollider::DrawDebugLines(const glm::vec4& color) const {
DebugDraw::Cylinder(GetWorldCenter(), GetWorldRotation(), m_Radius, m_Height, color);
}
void CylinderCollider::ImGuiInspector() {
Collider::ImGuiInspector();
float radius = m_Radius;
if (ImGui::DragFloat("Radius", &radius, 0.05f, 0.0001f)) {
SetRadius(radius);
}
float height = m_Height;
if (ImGui::DragFloat("Height", &height, 0.05f, 0.0001f)) {
SetHeight(height);
}
void CylinderCollider::Reflect(FieldVisitor& v) {
Collider::Reflect(v);
v.Property("radius", m_Radius, [this](float radius) { SetRadius(radius); }, {.speed = 0.05f, .min = 0.0001});
v.Property("height", m_Height, [this](float height) { SetHeight(height); }, {.speed = 0.05f, .min = 0.0001});
}
+14 -99
View File
@@ -3,40 +3,12 @@
#include <destrum/Physics/PhysicsWorld.h>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/Scene/Scene.h>
#include <destrum/Serialization/Reflect.h>
#include <array>
#include <imgui.h>
namespace {
[[nodiscard]] const char* RigidbodyTypeName(RigidbodyType type) {
switch (type) {
case RigidbodyType::Static:
return "Static";
case RigidbodyType::Kinematic:
return "Kinematic";
case RigidbodyType::Dynamic:
default:
return "Dynamic";
}
}
[[nodiscard]] RigidbodyType ParseRigidbodyType(const nlohmann::json& value) {
if (value.is_string()) {
const std::string type = value.get<std::string>();
if (type == "Static") return RigidbodyType::Static;
if (type == "Kinematic") return RigidbodyType::Kinematic;
return RigidbodyType::Dynamic;
}
const int type = value.get<int>();
switch (type) {
case 0: return RigidbodyType::Static;
case 2: return RigidbodyType::Kinematic;
case 1:
default: return RigidbodyType::Dynamic;
}
}
}
Rigidbody::~Rigidbody() {
if (m_World != nullptr) {
m_World->UnregisterRigidbody(*this);
@@ -110,78 +82,21 @@ glm::vec3 Rigidbody::GetLinearVelocity() const {
return m_World->GetLinearVelocity(m_Body);
}
nlohmann::json Rigidbody::Serialize() const {
return {
{"type", RigidbodyTypeName(m_Type)},
{"mass", m_Mass},
{"friction", m_Friction},
{"restitution", m_Restitution},
{"useGravity", m_UseGravity},
{"allowSleep", m_AllowSleep},
{"freezeRotation", m_FreezeRotation}
};
}
void Rigidbody::Reflect(FieldVisitor& v) {
static constexpr std::array<const char*, 3> TypeNames{"Static", "Dynamic", "Kinematic"};
void Rigidbody::Deserialize(const nlohmann::json& data) {
if (data.contains("type")) {
m_Type = ParseRigidbodyType(data.at("type"));
}
if (data.contains("mass")) {
SetMass(data.at("mass").get<float>());
}
if (data.contains("friction")) {
SetFriction(data.at("friction").get<float>());
}
if (data.contains("restitution")) {
SetRestitution(data.at("restitution").get<float>());
}
if (data.contains("useGravity")) {
m_UseGravity = data.at("useGravity").get<bool>();
}
if (data.contains("allowSleep")) {
m_AllowSleep = data.at("allowSleep").get<bool>();
}
if (data.contains("freezeRotation")) {
m_FreezeRotation = data.at("freezeRotation").get<bool>();
}
v.Property("type", m_Type, [this](RigidbodyType type) { SetType(type); }, {.label = "Body Type", .enumNames = TypeNames});
v.Property("mass", m_Mass, [this](float mass) { SetMass(mass); }, {.min = 0.0001});
v.Property("friction", m_Friction, [this](float friction) { SetFriction(friction); }, {.speed = 0.01f, .min = 0.0});
v.Property("restitution", m_Restitution, [this](float restitution) { SetRestitution(restitution); },
{.speed = 0.01f, .min = 0.0, .max = 1.0});
v.Property("useGravity", m_UseGravity, [this](bool useGravity) { SetUseGravity(useGravity); });
v.Property("allowSleep", m_AllowSleep, [this](bool allowSleep) { SetAllowSleep(allowSleep); });
v.Property("freezeRotation", m_FreezeRotation, [this](bool freezeRotation) { SetFreezeRotation(freezeRotation); });
}
void Rigidbody::ImGuiInspector() {
static const char* typeNames[] = {"Static", "Dynamic", "Kinematic"};
int type = static_cast<int>(m_Type);
if (ImGui::Combo("Body type", &type, typeNames, IM_ARRAYSIZE(typeNames))) {
SetType(static_cast<RigidbodyType>(type));
}
float mass = m_Mass;
if (ImGui::DragFloat("Mass", &mass, 0.1f, 0.0001f)) {
SetMass(mass);
}
float friction = m_Friction;
if (ImGui::DragFloat("Friction", &friction, 0.01f, 0.0f)) {
SetFriction(friction);
}
float restitution = m_Restitution;
if (ImGui::SliderFloat("Restitution", &restitution, 0.0f, 1.0f)) {
SetRestitution(restitution);
}
bool useGravity = m_UseGravity;
if (ImGui::Checkbox("Use gravity", &useGravity)) {
SetUseGravity(useGravity);
}
bool allowSleep = m_AllowSleep;
if (ImGui::Checkbox("Allow sleep", &allowSleep)) {
SetAllowSleep(allowSleep);
}
bool freezeRotation = m_FreezeRotation;
if (ImGui::Checkbox("Freeze rotation", &freezeRotation)) {
SetFreezeRotation(freezeRotation);
}
Component::ImGuiInspector();
if (HasPhysicsBody()) {
const glm::vec3 velocity = GetLinearVelocity();
@@ -2,30 +2,13 @@
#include <destrum/Graphics/DebugDraw.h>
#include <imgui.h>
#include <destrum/Serialization/Reflect.h>
void SphereCollider::DrawDebugLines(const glm::vec4& color) const {
DebugDraw::Sphere(GetWorldCenter(), m_Radius, color);
}
nlohmann::json SphereCollider::Serialize() const {
auto data = SerializeCollider();
data["radius"] = m_Radius;
return data;
}
void SphereCollider::Deserialize(const nlohmann::json& data) {
DeserializeCollider(data);
if (data.contains("radius")) {
SetRadius(data.at("radius").get<float>());
}
}
void SphereCollider::ImGuiInspector() {
Collider::ImGuiInspector();
float radius = m_Radius;
if (ImGui::DragFloat("Radius", &radius, 0.05f, 0.0001f)) {
SetRadius(radius);
}
void SphereCollider::Reflect(FieldVisitor& v) {
Collider::Reflect(v);
v.Property("radius", m_Radius, [this](float radius) { SetRadius(radius); }, {.speed = 0.05f, .min = 0.0001});
}
+10 -48
View File
@@ -5,11 +5,8 @@
#include <cmath>
#include <glm/gtc/quaternion.hpp> // glm::quat, glm::angleAxis
#include <glm/gtx/quaternion.hpp> // operator*(quat, vec3)
#include <destrum/Serialization/GlmJson.h>
#include <destrum/Serialization/Reflect.h>
#include <destrum/Util/DeltaTime.h>
#include <destrum/Util/ImGuiUtils.h>
#include <imgui.h>
glm::vec3 Rotator::MakePerpendicularUnitVector(const glm::vec3& axis)
{
@@ -98,48 +95,13 @@ void Rotator::Update(float dt)
GetTransform().SetLocalPosition(m_Pivot + rotatedOffset);
}
nlohmann::json Rotator::Serialize() const {
return {
{"distance", m_Distance},
{"speed", m_Speed},
{"currentAngle", m_CurrentAngle},
{"pivot", m_Pivot},
{"axis", m_Axis},
{"initialOffset", m_InitialOffset}
};
}
void Rotator::Deserialize(const nlohmann::json& data) {
m_Distance = data.value("distance", m_Distance);
m_Speed = data.value("speed", m_Speed);
m_CurrentAngle = data.value("currentAngle", m_CurrentAngle);
m_Pivot = data.value("pivot", m_Pivot);
if (data.contains("axis")) {
SetAxis(data.at("axis").get<glm::vec3>());
}
m_InitialOffset = data.value("initialOffset", m_InitialOffset);
}
void Rotator::ImGuiInspector() {
float distance = m_Distance;
if (ImGui::DragFloat("Distance", &distance, 0.05f, 0.0f)) {
SetDistance(distance);
}
float speed = m_Speed;
if (ImGui::DragFloat("Speed", &speed, 0.05f)) {
SetSpeed(speed);
}
glm::vec3 pivot = m_Pivot;
if (ImGuiUtils::DrawVec3Control("Pivot", pivot)) {
SetPivotPosition(pivot);
}
glm::vec3 axis = m_Axis;
if (ImGuiUtils::DrawVec3Control("Axis", axis)) {
SetAxis(axis);
}
ImGui::Text("Current angle: %.2f rad", m_CurrentAngle);
// SetPivotPosition derives the offset and distance from the current position, so the pivot is
// loaded first and the saved distance and offset overwrite that
void Rotator::Reflect(FieldVisitor& v) {
v.Property("pivot", m_Pivot, [this](const glm::vec3& pivot) { SetPivotPosition(pivot); });
v.Property("distance", m_Distance, [this](float distance) { SetDistance(distance); }, {.speed = 0.05f, .min = 0.0});
v.Field("speed", m_Speed, {.speed = 0.05f});
v.Property("axis", m_Axis, [this](const glm::vec3& axis) { SetAxis(axis); });
v.Field("currentAngle", m_CurrentAngle, {.format = "%.2f rad", .readOnly = true});
v.Field("initialOffset", m_InitialOffset, {.hidden = true});
}
+5 -32
View File
@@ -3,11 +3,8 @@
#include <glm/gtx/quaternion.hpp>
#include "destrum/ObjectModel/Transform.h"
#include "destrum/Serialization/GlmJson.h"
#include "destrum/Serialization/Reflect.h"
#include "destrum/Util/DeltaTime.h"
#include "destrum/Util/ImGuiUtils.h"
#include <imgui.h>
void Spinner::Update(float dt)
{
@@ -17,32 +14,8 @@ void Spinner::Update(float dt)
GetTransform().SetLocalRotation(q);
}
nlohmann::json Spinner::Serialize() const {
return {
{"axis", m_Axis},
{"speed", m_Speed},
{"angle", m_Angle}
};
}
void Spinner::Deserialize(const nlohmann::json& data) {
if (data.contains("axis")) {
SetAxis(data.at("axis").get<glm::vec3>());
}
m_Speed = data.value("speed", m_Speed);
m_Angle = data.value("angle", m_Angle);
}
void Spinner::ImGuiInspector() {
glm::vec3 axis = m_Axis;
if (ImGuiUtils::DrawVec3Control("Axis", axis)) {
SetAxis(axis);
}
float speed = m_Speed;
if (ImGui::DragFloat("Speed", &speed, 0.05f)) {
SetSpeed(speed);
}
ImGui::Text("Current angle: %.2f rad", m_Angle);
void Spinner::Reflect(FieldVisitor& v) {
v.Property("axis", m_Axis, [this](const glm::vec3& axis) { SetAxis(axis); });
v.Field("speed", m_Speed, {.speed = 0.05f});
v.Field("angle", m_Angle, {.format = "%.2f rad", .readOnly = true});
}
+8 -35
View File
@@ -7,8 +7,7 @@
#include <destrum/Graphics/Managers/Renderer2D.h>
#include <destrum/Graphics/Resources/FontCache.h>
#include <destrum/ObjectModel/GameObject.h>
#include <imgui.h>
#include <destrum/Serialization/Reflect.h>
#include "tracy/Tracy.hpp"
@@ -109,39 +108,13 @@ void Canvas::DrawObject(GameObject& object, const UIRect& rect, float scale, boo
}
}
nlohmann::json Canvas::Serialize() const
void Canvas::Reflect(FieldVisitor& v)
{
return {
{"scaleMode", m_ScaleMode == CanvasScaleMode::ConstantPixelSize ? "constantPixelSize" : "scaleWithScreenHeight"},
{"referenceHeight", m_ReferenceHeight},
{"scaleFactor", m_ScaleFactor},
{"drawDebugRects", m_DrawDebugRects},
};
}
static constexpr std::array<const char*, 2> ScaleModeNames{"constantPixelSize", "scaleWithScreenHeight"};
void Canvas::Deserialize(const nlohmann::json& data)
{
m_ScaleMode = data.value("scaleMode", std::string{"scaleWithScreenHeight"}) == "constantPixelSize"
? CanvasScaleMode::ConstantPixelSize
: CanvasScaleMode::ScaleWithScreenHeight;
m_ReferenceHeight = data.value("referenceHeight", m_ReferenceHeight);
m_ScaleFactor = data.value("scaleFactor", m_ScaleFactor);
m_DrawDebugRects = data.value("drawDebugRects", m_DrawDebugRects);
}
void Canvas::ImGuiInspector()
{
constexpr std::array modeNames{"Constant Pixel Size", "Scale With Screen Height"};
int mode = static_cast<int>(m_ScaleMode);
if (ImGui::Combo("Scale Mode", &mode, modeNames.data(), static_cast<int>(modeNames.size()))) {
m_ScaleMode = static_cast<CanvasScaleMode>(mode);
}
if (m_ScaleMode == CanvasScaleMode::ScaleWithScreenHeight) {
ImGui::DragFloat("Reference Height", &m_ReferenceHeight, 1.0f, 1.0f, 8192.0f);
} else {
ImGui::DragFloat("Scale Factor", &m_ScaleFactor, 0.01f, 0.01f, 10.0f);
}
ImGui::Checkbox("Draw Debug Rects", &m_DrawDebugRects);
const bool scaleWithHeight = m_ScaleMode == CanvasScaleMode::ScaleWithScreenHeight;
v.Field("scaleMode", m_ScaleMode, {.enumNames = ScaleModeNames});
v.Field("referenceHeight", m_ReferenceHeight, {.speed = 1.0f, .min = 1.0, .max = 8192.0, .hidden = !scaleWithHeight});
v.Field("scaleFactor", m_ScaleFactor, {.speed = 0.01f, .min = 0.01, .max = 10.0, .hidden = scaleWithHeight});
v.Field("drawDebugRects", m_DrawDebugRects);
}
+8 -23
View File
@@ -1,6 +1,6 @@
#include <destrum/Components/UI/RectTransform.h>
#include <destrum/Serialization/GlmJson.h>
#include <destrum/Serialization/Reflect.h>
#include <array>
@@ -61,24 +61,13 @@ void RectTransform::Stretch()
m_Size = glm::vec2{0.0f};
}
nlohmann::json RectTransform::Serialize() const
void RectTransform::Reflect(FieldVisitor& v)
{
return {
{"anchorMin", m_AnchorMin},
{"anchorMax", m_AnchorMax},
{"pivot", m_Pivot},
{"position", m_Position},
{"size", m_Size},
};
}
void RectTransform::Deserialize(const nlohmann::json& data)
{
m_AnchorMin = data.value("anchorMin", m_AnchorMin);
m_AnchorMax = data.value("anchorMax", m_AnchorMax);
m_Pivot = data.value("pivot", m_Pivot);
m_Position = data.value("position", m_Position);
m_Size = data.value("size", m_Size);
v.Field("anchorMin", m_AnchorMin, {.speed = 0.01f, .min = 0.0, .max = 1.0});
v.Field("anchorMax", m_AnchorMax, {.speed = 0.01f, .min = 0.0, .max = 1.0});
v.Field("pivot", m_Pivot, {.speed = 0.01f, .min = 0.0, .max = 1.0});
v.Field("position", m_Position, {.speed = 1.0f});
v.Field("size", m_Size, {.speed = 1.0f});
}
void RectTransform::ImGuiInspector()
@@ -118,11 +107,7 @@ void RectTransform::ImGuiInspector()
}
}
ImGui::DragFloat2("Anchor Min", &m_AnchorMin.x, 0.01f, 0.0f, 1.0f);
ImGui::DragFloat2("Anchor Max", &m_AnchorMax.x, 0.01f, 0.0f, 1.0f);
ImGui::DragFloat2("Pivot", &m_Pivot.x, 0.01f, 0.0f, 1.0f);
ImGui::DragFloat2("Position", &m_Position.x, 1.0f);
ImGui::DragFloat2("Size", &m_Size.x, 1.0f);
Component::ImGuiInspector();
ImGui::TextDisabled("Screen: %.0f, %.0f %.0f x %.0f",
m_ScreenRect.min.x, m_ScreenRect.min.y, m_ScreenRect.size.x, m_ScreenRect.size.y);
+11 -43
View File
@@ -5,11 +5,9 @@
#include <destrum/FS/AssetFS.h>
#include <destrum/Graphics/RenderResources.h>
#include <destrum/Graphics/Renderer.h>
#include <destrum/Serialization/GlmJson.h>
#include <destrum/Serialization/Reflect.h>
#include <destrum/Util/GameState.h>
#include <destrum/Util/ImGuiUtils.h>
#include <imgui.h>
#include <spdlog/spdlog.h>
UIImage::UIImage(GameObject& parent, const glm::vec4& color)
@@ -64,45 +62,15 @@ ImageID UIImage::ResolveTexture()
return m_Texture;
}
nlohmann::json UIImage::Serialize() const
void UIImage::Reflect(FieldVisitor& v)
{
return {
{"color", m_Color},
{"shape", m_Shape == UIImageShape::Ellipse ? "ellipse" : "rect"},
{"outline", m_Outline},
{"cornerRadius", m_CornerRadius},
{"texture", m_TexturePath},
{"filter", m_Filter == TextureFilter::Nearest ? "nearest" : "linear"},
};
}
void UIImage::Deserialize(const nlohmann::json& data)
{
m_Color = data.value("color", m_Color);
m_Shape = data.value("shape", std::string{"rect"}) == "ellipse" ? UIImageShape::Ellipse : UIImageShape::Rect;
m_Outline = data.value("outline", m_Outline);
m_CornerRadius = data.value("cornerRadius", m_CornerRadius);
m_TexturePath = data.value("texture", m_TexturePath);
m_Filter = data.value("filter", std::string{"linear"}) == "nearest" ? TextureFilter::Nearest : TextureFilter::Linear;
}
void UIImage::ImGuiInspector()
{
constexpr std::array shapeNames{"Rect", "Ellipse"};
int shape = static_cast<int>(m_Shape);
if (ImGui::Combo("Shape", &shape, shapeNames.data(), static_cast<int>(shapeNames.size()))) {
m_Shape = static_cast<UIImageShape>(shape);
}
ImGui::ColorEdit4("Color", &m_Color.x);
ImGui::DragFloat("Outline", &m_Outline, 0.1f, 0.0f, 100.0f);
if (m_Shape == UIImageShape::Rect) {
ImGui::DragFloat("Corner Radius", &m_CornerRadius, 0.1f, 0.0f, 500.0f);
}
ImGuiUtils::InputString("Texture", m_TexturePath);
bool nearest = m_Filter == TextureFilter::Nearest;
if (ImGui::Checkbox("Nearest Filter", &nearest)) {
m_Filter = nearest ? TextureFilter::Nearest : TextureFilter::Linear;
}
static constexpr std::array<const char*, 2> ShapeNames{"rect", "ellipse"};
static constexpr std::array<const char*, 2> FilterNames{"nearest", "linear"};
v.Field("shape", m_Shape, {.enumNames = ShapeNames});
v.Field("color", m_Color, {.color = true});
v.Field("outline", m_Outline, {.min = 0.0, .max = 100.0});
v.Field("cornerRadius", m_CornerRadius, {.min = 0.0, .max = 500.0, .hidden = m_Shape != UIImageShape::Rect});
v.Field("texture", m_TexturePath, {.acceptAssets = true});
v.Field("filter", m_Filter, {.enumNames = FilterNames});
}
+11 -64
View File
@@ -4,10 +4,7 @@
#include <destrum/Graphics/Managers/Renderer2D.h>
#include <destrum/Graphics/Resources/FontCache.h>
#include <destrum/Serialization/GlmJson.h>
#include <destrum/Util/ImGuiUtils.h>
#include <imgui.h>
#include <destrum/Serialization/Reflect.h>
namespace {
[[nodiscard]] float AlignFactor(UITextAlign align)
@@ -19,24 +16,6 @@ namespace {
}
return 0.0f;
}
[[nodiscard]] const char* AlignName(UITextAlign align)
{
switch (align) {
case UITextAlign::Start: return "start";
case UITextAlign::Center: return "center";
case UITextAlign::End: return "end";
}
return "start";
}
[[nodiscard]] UITextAlign ParseAlign(const std::string& name, UITextAlign fallback)
{
if (name == "start") return UITextAlign::Start;
if (name == "center") return UITextAlign::Center;
if (name == "end") return UITextAlign::End;
return fallback;
}
}
UIText::UIText(GameObject& parent, std::string text, float fontSize)
@@ -86,48 +65,16 @@ void UIText::DrawUI(const UIRect& rect, float scale)
TextStyle{.color = m_Color, .outline = m_Outline * scale, .outlineColor = m_OutlineColor});
}
nlohmann::json UIText::Serialize() const
void UIText::Reflect(FieldVisitor& v)
{
return {
{"text", m_Text},
{"font", m_FontPath},
{"fontSize", m_FontSize},
{"color", m_Color},
{"outline", m_Outline},
{"outlineColor", m_OutlineColor},
{"horizontalAlign", AlignName(m_HorizontalAlign)},
{"verticalAlign", AlignName(m_VerticalAlign)},
};
}
static constexpr std::array<const char*, 3> AlignNames{"start", "center", "end"};
void UIText::Deserialize(const nlohmann::json& data)
{
m_Text = data.value("text", m_Text);
m_FontPath = data.value("font", m_FontPath);
m_FontSize = data.value("fontSize", m_FontSize);
m_Color = data.value("color", m_Color);
m_Outline = data.value("outline", m_Outline);
m_OutlineColor = data.value("outlineColor", m_OutlineColor);
m_HorizontalAlign = ParseAlign(data.value("horizontalAlign", std::string{}), m_HorizontalAlign);
m_VerticalAlign = ParseAlign(data.value("verticalAlign", std::string{}), m_VerticalAlign);
}
void UIText::ImGuiInspector()
{
ImGuiUtils::InputStringMultiline("Text", m_Text);
ImGuiUtils::InputString("Font", m_FontPath);
ImGui::DragFloat("Font Size", &m_FontSize, 0.5f, 1.0f, 1000.0f);
ImGui::ColorEdit4("Color", &m_Color.x);
ImGui::DragFloat("Outline", &m_Outline, 0.1f, 0.0f, 16.0f);
ImGui::ColorEdit4("Outline Color", &m_OutlineColor.x);
constexpr std::array alignNames{"Start", "Center", "End"};
int horizontal = static_cast<int>(m_HorizontalAlign);
if (ImGui::Combo("Horizontal", &horizontal, alignNames.data(), static_cast<int>(alignNames.size()))) {
m_HorizontalAlign = static_cast<UITextAlign>(horizontal);
}
int vertical = static_cast<int>(m_VerticalAlign);
if (ImGui::Combo("Vertical", &vertical, alignNames.data(), static_cast<int>(alignNames.size()))) {
m_VerticalAlign = static_cast<UITextAlign>(vertical);
}
v.Field("text", m_Text, {.multiline = true});
v.Field("font", m_FontPath, {.acceptAssets = true});
v.Field("fontSize", m_FontSize, {.speed = 0.5f, .min = 1.0, .max = 1000.0});
v.Field("color", m_Color, {.color = true});
v.Field("outline", m_Outline, {.min = 0.0, .max = 16.0});
v.Field("outlineColor", m_OutlineColor, {.color = true});
v.Field("horizontalAlign", m_HorizontalAlign, {.label = "Horizontal", .enumNames = AlignNames});
v.Field("verticalAlign", m_VerticalAlign, {.label = "Vertical", .enumNames = AlignNames});
}