feat: add new fancy reflection
This commit is contained in:
@@ -27,6 +27,7 @@ public:
|
||||
|
||||
std::string GetTypeName() const override { return "AudioSource"; }
|
||||
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
@@ -17,8 +17,7 @@ public:
|
||||
|
||||
std::string GetTypeName() const override { return "CameraComponent"; }
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
// Places the camera at this object's world transform, looking down its local -Z.
|
||||
|
||||
@@ -19,9 +19,7 @@ public:
|
||||
|
||||
std::string GetTypeName() const override { return "LightComponent"; }
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
void SetType(LightType type) { m_Type = type; }
|
||||
[[nodiscard]] LightType GetType() const { return m_Type; }
|
||||
|
||||
@@ -26,9 +26,10 @@ public:
|
||||
|
||||
std::string GetTypeName() const override { return "OrbitAndSpin"; }
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
void OnFieldsEdited() override;
|
||||
|
||||
// optional setters
|
||||
void SetRadius(float r) { m_Radius = r; }
|
||||
@@ -36,6 +37,7 @@ public:
|
||||
|
||||
private:
|
||||
void BuildOrbitBasis(); // builds m_U/m_V from m_OrbitAxis
|
||||
void Sanitize();
|
||||
|
||||
float m_Radius = 5.0f;
|
||||
glm::vec3 m_Center{0.0f};
|
||||
|
||||
@@ -11,16 +11,13 @@ public:
|
||||
|
||||
void Update(float) override {}
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
std::string GetTypeName() const override
|
||||
{
|
||||
return "BoxCollider";
|
||||
}
|
||||
|
||||
|
||||
void DrawDebugLines(const glm::vec4& color) const override;
|
||||
|
||||
[[nodiscard]] PhysicsShapeDesc BuildPhysicsShape() const override {
|
||||
|
||||
@@ -17,24 +17,7 @@ public:
|
||||
return "CapsuleCollider";
|
||||
}
|
||||
|
||||
void ImGuiInspector() override;
|
||||
|
||||
nlohmann::json Serialize() const override {
|
||||
auto data = SerializeCollider();
|
||||
data["radius"] = m_Radius;
|
||||
data["height"] = m_Height;
|
||||
return data;
|
||||
}
|
||||
|
||||
void Deserialize(const nlohmann::json& data) override {
|
||||
DeserializeCollider(data);
|
||||
if (data.contains("radius")) {
|
||||
SetRadius(data.at("radius").get<float>());
|
||||
}
|
||||
if (data.contains("height")) {
|
||||
SetHeight(data.at("height").get<float>());
|
||||
}
|
||||
}
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
void DrawDebugLines(const glm::vec4& color) const override;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
|
||||
[[nodiscard]] bool AffectsPhysics() const override { return true; }
|
||||
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
[[nodiscard]] virtual PhysicsShapeDesc BuildPhysicsShape() const = 0;
|
||||
virtual void DrawDebugLines(const glm::vec4& color) const = 0;
|
||||
@@ -30,18 +30,6 @@ public:
|
||||
void SetCenterOffset(const glm::vec3& offset) { m_CenterOffset = offset; NotifyPhysicsChanged(); }
|
||||
|
||||
protected:
|
||||
[[nodiscard]] nlohmann::json SerializeCollider() const {
|
||||
return {
|
||||
{"centerOffset", m_CenterOffset},
|
||||
{"isTrigger", m_IsTrigger}
|
||||
};
|
||||
}
|
||||
|
||||
void DeserializeCollider(const nlohmann::json& data) {
|
||||
m_CenterOffset = data.value("centerOffset", m_CenterOffset);
|
||||
m_IsTrigger = data.value("isTrigger", m_IsTrigger);
|
||||
}
|
||||
|
||||
bool m_IsTrigger{false};
|
||||
glm::vec3 m_CenterOffset{0.0f};
|
||||
};
|
||||
|
||||
@@ -17,9 +17,7 @@ public:
|
||||
return "ConvexHullCollider";
|
||||
}
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
void DrawDebugLines(const glm::vec4& color) const override;
|
||||
|
||||
|
||||
@@ -17,24 +17,7 @@ public:
|
||||
return "CylinderCollider";
|
||||
}
|
||||
|
||||
void ImGuiInspector() override;
|
||||
|
||||
nlohmann::json Serialize() const override {
|
||||
auto data = SerializeCollider();
|
||||
data["radius"] = m_Radius;
|
||||
data["height"] = m_Height;
|
||||
return data;
|
||||
}
|
||||
|
||||
void Deserialize(const nlohmann::json& data) override {
|
||||
DeserializeCollider(data);
|
||||
if (data.contains("radius")) {
|
||||
SetRadius(data.at("radius").get<float>());
|
||||
}
|
||||
if (data.contains("height")) {
|
||||
SetHeight(data.at("height").get<float>());
|
||||
}
|
||||
}
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
void DrawDebugLines(const glm::vec4& color) const override;
|
||||
|
||||
|
||||
@@ -14,14 +14,6 @@ public:
|
||||
return "MeshCollider";
|
||||
}
|
||||
|
||||
nlohmann::json Serialize() const override {
|
||||
return SerializeCollider();
|
||||
}
|
||||
|
||||
void Deserialize(const nlohmann::json& data) override {
|
||||
DeserializeCollider(data);
|
||||
}
|
||||
|
||||
void ImGuiInspector() override;
|
||||
|
||||
void DrawDebugLines(const glm::vec4& color) const override;
|
||||
|
||||
@@ -21,8 +21,7 @@ public:
|
||||
void Start() override;
|
||||
void Destroy() override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
std::string GetTypeName() const override
|
||||
|
||||
@@ -12,9 +12,7 @@ public:
|
||||
|
||||
void Update(float) override {}
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
std::string GetTypeName() const override
|
||||
{
|
||||
return "SphereCollider";
|
||||
|
||||
@@ -20,9 +20,7 @@ public:
|
||||
|
||||
std::string GetTypeName() const override { return "Rotator"; }
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
void SetDistance(float distance);
|
||||
void SetSpeed(float speed) { m_Speed = speed; }
|
||||
|
||||
@@ -18,9 +18,7 @@ public:
|
||||
|
||||
std::string GetTypeName() const override { return "Spinner"; }
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
void SetAxis(const glm::vec3& axis) {
|
||||
m_Axis = glm::dot(axis, axis) > 0.000001f
|
||||
|
||||
@@ -23,9 +23,7 @@ public:
|
||||
void Render(const RenderContext& ctx) override;
|
||||
void Draw(const glm::vec2& screenSize);
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
[[nodiscard]] float GetScale(const glm::vec2& screenSize) const;
|
||||
|
||||
|
||||
@@ -22,8 +22,7 @@ public:
|
||||
|
||||
std::string GetTypeName() const override { return "RectTransform"; }
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
[[nodiscard]] UIRect ComputeRect(const UIRect& parentRect, float scale) const;
|
||||
|
||||
@@ -20,9 +20,7 @@ public:
|
||||
|
||||
void DrawUI(const UIRect& rect, float scale) override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
void SetShape(UIImageShape shape) { m_Shape = shape; }
|
||||
void SetOutline(float thickness) { m_Outline = thickness; }
|
||||
|
||||
@@ -21,9 +21,7 @@ public:
|
||||
|
||||
void DrawUI(const UIRect& rect, float scale) override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
void SetText(std::string text) { m_Text = std::move(text); }
|
||||
void SetFont(std::string assetPath) { m_FontPath = std::move(assetPath); }
|
||||
|
||||
@@ -29,10 +29,14 @@ struct FieldOptions {
|
||||
std::optional<double> max;
|
||||
const char* format = nullptr;
|
||||
bool color = false;
|
||||
bool angle = false; // radians, shown in degrees
|
||||
bool multiline = false;
|
||||
bool acceptAssets = false; // strings take a virtual path dropped from the asset browser
|
||||
bool readOnly = false;
|
||||
bool hidden = false;
|
||||
bool transient = false;
|
||||
bool hidden = false; // saved, not shown
|
||||
bool transient = false; // shown, not saved
|
||||
const char* label = nullptr;
|
||||
const char* tooltip = nullptr;
|
||||
std::span<const char* const> enumNames;
|
||||
std::function<bool(const GameObject&)> filter;
|
||||
};
|
||||
@@ -66,6 +70,7 @@ namespace reflect_detail {
|
||||
}
|
||||
|
||||
bool DrawScalar(const char* label, Scalar type, void* value, int components, const void* min, const void* max, const FieldOptions& options);
|
||||
bool DrawAngle(const char* label, float& radians, const FieldOptions& options);
|
||||
bool DrawEnum(const char* label, std::int64_t& value, const FieldOptions& options);
|
||||
|
||||
void BeginList(const char* label);
|
||||
@@ -77,6 +82,9 @@ namespace reflect_detail {
|
||||
|
||||
template <class T>
|
||||
concept Numeric = std::is_arithmetic_v<T> && !std::same_as<T, bool>;
|
||||
|
||||
template <class T>
|
||||
concept FloatVec = std::same_as<T, glm::vec2> || std::same_as<T, glm::vec3> || std::same_as<T, glm::vec4>;
|
||||
}
|
||||
|
||||
bool DrawField(const char* label, bool& value, const FieldOptions& options, const Scene* scene);
|
||||
@@ -90,6 +98,11 @@ bool DrawField(const char* label, std::vector<ObjectRef>& value, const FieldOpti
|
||||
template <reflect_detail::Numeric T>
|
||||
bool DrawField(const char* label, T& value, const FieldOptions& options, const Scene*)
|
||||
{
|
||||
if constexpr (std::same_as<T, float>) {
|
||||
if (options.angle) {
|
||||
return reflect_detail::DrawAngle(label, value, options);
|
||||
}
|
||||
}
|
||||
const T min = options.min ? static_cast<T>(*options.min) : T{};
|
||||
const T max = options.max ? static_cast<T>(*options.max) : T{};
|
||||
return reflect_detail::DrawScalar(label, reflect_detail::ScalarOf<T>(), &value, 1,
|
||||
@@ -181,6 +194,9 @@ struct FieldCodec {
|
||||
if constexpr (reflect_detail::Numeric<T>) {
|
||||
if (options.min && typed < static_cast<T>(*options.min)) typed = static_cast<T>(*options.min);
|
||||
if (options.max && typed > static_cast<T>(*options.max)) typed = static_cast<T>(*options.max);
|
||||
} else if constexpr (reflect_detail::FloatVec<T>) {
|
||||
if (options.min) typed = glm::max(typed, T{static_cast<float>(*options.min)});
|
||||
if (options.max) typed = glm::min(typed, T{static_cast<float>(*options.max)});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,14 +219,26 @@ class FieldVisitor {
|
||||
public:
|
||||
virtual ~FieldVisitor() = default;
|
||||
|
||||
using Commit = std::function<void(void* value)>;
|
||||
|
||||
template <Reflectable T>
|
||||
void Field(std::string_view key, T& value, const FieldOptions& options = {})
|
||||
{
|
||||
Visit(key, &value, options, FieldCodec<T>::Ops);
|
||||
Visit(key, &value, options, FieldCodec<T>::Ops, {});
|
||||
}
|
||||
|
||||
// Loads and edits go through the setter, so it can validate and react
|
||||
template <Reflectable T, std::invocable<const T&> Setter>
|
||||
void Property(std::string_view key, const T& value, Setter&& setter, const FieldOptions& options = {})
|
||||
{
|
||||
T copy = value;
|
||||
Visit(key, ©, options, FieldCodec<T>::Ops, [&setter](void* changed) {
|
||||
setter(*static_cast<const T*>(changed));
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops) = 0;
|
||||
virtual void Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops, const Commit& commit) = 0;
|
||||
};
|
||||
|
||||
class JsonWriter final : public FieldVisitor {
|
||||
@@ -218,7 +246,7 @@ public:
|
||||
[[nodiscard]] nlohmann::json& Result() { return m_Result; }
|
||||
|
||||
protected:
|
||||
void Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops) override;
|
||||
void Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops, const Commit& commit) override;
|
||||
|
||||
private:
|
||||
nlohmann::json m_Result = nlohmann::json::object();
|
||||
@@ -229,7 +257,7 @@ public:
|
||||
explicit JsonReader(const nlohmann::json& data) : m_Data(data) {}
|
||||
|
||||
protected:
|
||||
void Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops) override;
|
||||
void Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops, const Commit& commit) override;
|
||||
|
||||
private:
|
||||
const nlohmann::json& m_Data;
|
||||
@@ -240,7 +268,7 @@ public:
|
||||
ReferenceResolver(const Component& owner, const ObjectRef::ObjectMap& objects) : m_Owner(owner), m_Objects(objects) {}
|
||||
|
||||
protected:
|
||||
void Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops) override;
|
||||
void Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops, const Commit& commit) override;
|
||||
|
||||
private:
|
||||
const Component& m_Owner;
|
||||
@@ -254,7 +282,7 @@ public:
|
||||
[[nodiscard]] bool Changed() const { return m_Changed; }
|
||||
|
||||
protected:
|
||||
void Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops) override;
|
||||
void Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops, const Commit& commit) override;
|
||||
|
||||
private:
|
||||
const Scene* m_Scene;
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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", ¤t) && 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();
|
||||
}
|
||||
|
||||
@@ -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::Reflect(FieldVisitor& v) {
|
||||
static constexpr std::array<const char*, 3> TypeNames{"Directional", "Point", "Spot"};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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"});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"},
|
||||
};
|
||||
}
|
||||
static constexpr std::array<const char*, 2> ShapeNames{"rect", "ellipse"};
|
||||
static constexpr std::array<const char*, 2> FilterNames{"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;
|
||||
}
|
||||
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});
|
||||
}
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
|
||||
@@ -65,6 +65,26 @@ namespace reflect_detail {
|
||||
return ImGui::DragScalarN(label, ToImGui(type), value, components, options.speed, min, max, options.format, ClampFlags(options));
|
||||
}
|
||||
|
||||
bool DrawAngle(const char* label, float& radians, const FieldOptions& options)
|
||||
{
|
||||
float degrees = glm::degrees(radians);
|
||||
const char* format = options.format != nullptr ? options.format : "%.1f deg";
|
||||
bool changed;
|
||||
if (options.min && options.max) {
|
||||
changed = ImGui::SliderFloat(label, °rees, glm::degrees(static_cast<float>(*options.min)),
|
||||
glm::degrees(static_cast<float>(*options.max)), format, ImGuiSliderFlags_AlwaysClamp);
|
||||
} else {
|
||||
const float min = options.min ? glm::degrees(static_cast<float>(*options.min)) : 0.0f;
|
||||
const float max = options.max ? glm::degrees(static_cast<float>(*options.max)) : 0.0f;
|
||||
changed = ImGui::DragScalar(label, ImGuiDataType_Float, °rees, options.speed,
|
||||
options.min ? &min : nullptr, options.max ? &max : nullptr, format, ClampFlags(options));
|
||||
}
|
||||
if (changed) {
|
||||
radians = glm::radians(degrees);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool DrawEnum(const char* label, std::int64_t& value, const FieldOptions& options)
|
||||
{
|
||||
if (options.enumNames.empty()) {
|
||||
@@ -72,11 +92,11 @@ namespace reflect_detail {
|
||||
}
|
||||
|
||||
const auto count = static_cast<std::int64_t>(options.enumNames.size());
|
||||
const char* preview = value >= 0 && value < count ? options.enumNames[static_cast<std::size_t>(value)] : "?";
|
||||
const std::string preview = value >= 0 && value < count ? PrettifyFieldName(options.enumNames[static_cast<std::size_t>(value)]) : "?";
|
||||
bool changed = false;
|
||||
if (ImGui::BeginCombo(label, preview)) {
|
||||
if (ImGui::BeginCombo(label, preview.c_str())) {
|
||||
for (std::int64_t i = 0; i < count; ++i) {
|
||||
if (ImGui::Selectable(options.enumNames[static_cast<std::size_t>(i)], i == value)) {
|
||||
if (ImGui::Selectable(PrettifyFieldName(options.enumNames[static_cast<std::size_t>(i)]).c_str(), i == value)) {
|
||||
value = i;
|
||||
changed = true;
|
||||
}
|
||||
@@ -134,9 +154,17 @@ bool DrawField(const char* label, bool& value, const FieldOptions&, const Scene*
|
||||
return ImGui::Checkbox(label, &value);
|
||||
}
|
||||
|
||||
bool DrawField(const char* label, std::string& value, const FieldOptions&, const Scene*)
|
||||
bool DrawField(const char* label, std::string& value, const FieldOptions& options, const Scene*)
|
||||
{
|
||||
return ImGuiUtils::InputString(label, value);
|
||||
bool changed = options.multiline ? ImGuiUtils::InputStringMultiline(label, value) : ImGuiUtils::InputString(label, value);
|
||||
if (options.acceptAssets && ImGui::BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(ImGuiUtils::AssetPayload)) {
|
||||
value = static_cast<const char*>(payload->Data);
|
||||
changed = true;
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool DrawField(const char* label, glm::vec2& value, const FieldOptions& options, const Scene*)
|
||||
@@ -241,14 +269,14 @@ void ResolveField(std::vector<ObjectRef>& value, const ObjectRef::ObjectMap& obj
|
||||
});
|
||||
}
|
||||
|
||||
void JsonWriter::Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops)
|
||||
void JsonWriter::Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops, const Commit&)
|
||||
{
|
||||
if (!options.transient) {
|
||||
ops.save(value, m_Result[std::string{key}], options);
|
||||
}
|
||||
}
|
||||
|
||||
void JsonReader::Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops)
|
||||
void JsonReader::Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops, const Commit& commit)
|
||||
{
|
||||
if (options.transient || !m_Data.is_object()) {
|
||||
return;
|
||||
@@ -262,15 +290,21 @@ void JsonReader::Visit(std::string_view key, void* value, const FieldOptions& op
|
||||
} catch (const std::exception& exception) {
|
||||
throw std::runtime_error("\"" + std::string{key} + "\": " + exception.what());
|
||||
}
|
||||
if (commit) {
|
||||
commit(value);
|
||||
}
|
||||
}
|
||||
|
||||
void ReferenceResolver::Visit(std::string_view key, void* value, const FieldOptions&, const FieldOps& ops)
|
||||
void ReferenceResolver::Visit(std::string_view key, void* value, const FieldOptions&, const FieldOps& ops, const Commit& commit)
|
||||
{
|
||||
if (ops.resolve == nullptr) {
|
||||
return;
|
||||
}
|
||||
std::vector<ObjectId> dropped;
|
||||
ops.resolve(value, m_Objects, dropped);
|
||||
if (commit) {
|
||||
commit(value);
|
||||
}
|
||||
const GameObject* owner = m_Owner.GetGameObject();
|
||||
for (const ObjectId id : dropped) {
|
||||
spdlog::warn("{} on {}: {} references object {} which is not in the scene file, dropping it",
|
||||
@@ -278,7 +312,7 @@ void ReferenceResolver::Visit(std::string_view key, void* value, const FieldOpti
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDrawer::Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops)
|
||||
void InspectorDrawer::Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops, const Commit& commit)
|
||||
{
|
||||
if (options.hidden) {
|
||||
return;
|
||||
@@ -286,7 +320,14 @@ void InspectorDrawer::Visit(std::string_view key, void* value, const FieldOption
|
||||
const std::string label = options.label != nullptr ? options.label : reflect_detail::PrettifyFieldName(key);
|
||||
ImGui::PushID(key.data(), key.data() + key.size());
|
||||
ImGui::BeginDisabled(options.readOnly);
|
||||
m_Changed |= ops.draw(label.c_str(), value, options, m_Scene);
|
||||
const bool changed = ops.draw(label.c_str(), value, options, m_Scene);
|
||||
if (options.tooltip != nullptr) {
|
||||
ImGui::SetItemTooltip("%s", options.tooltip);
|
||||
}
|
||||
if (changed && commit) {
|
||||
commit(value);
|
||||
}
|
||||
m_Changed |= changed;
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
@@ -13,9 +13,7 @@ public:
|
||||
|
||||
void OnTriggerEnter(GameObject* other) override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
void SetSpawnOffset(const glm::vec3& offset) { m_SpawnOffset = offset; }
|
||||
[[nodiscard]] glm::vec3 GetSpawnPoint() const;
|
||||
|
||||
@@ -16,10 +16,7 @@ public:
|
||||
|
||||
void LateUpdate(float dt) override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ResolveReferences(const ObjectMap& objects) override;
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
void SetPlayer(const GameObject& player) { m_Player = ObjectRef(player); }
|
||||
void SetCrosshair(const GameObject& crosshair) { m_Crosshair = ObjectRef(crosshair); }
|
||||
|
||||
@@ -20,8 +20,8 @@ public:
|
||||
void Start() override;
|
||||
void Update(float dt) override;
|
||||
void FixedUpdate(float fixedDt) override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
void ImGuiInspector() override;
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
|
||||
[[nodiscard]] float GetYaw() const { return m_Yaw; }
|
||||
|
||||
@@ -11,9 +11,8 @@ public:
|
||||
|
||||
void Start() override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
void OnFieldsEdited() override;
|
||||
|
||||
void SetOn(bool on);
|
||||
[[nodiscard]] bool IsOn() const { return m_On; }
|
||||
|
||||
@@ -14,8 +14,7 @@ public:
|
||||
void Start() override;
|
||||
void Update(float dt) override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
void SetTravelOffset(const glm::vec3& offset) { m_TravelOffset = offset; }
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
void Start() override;
|
||||
void FixedUpdate(float fixedDt) override;
|
||||
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
@@ -10,9 +10,7 @@ public:
|
||||
std::string GetTypeName() const override { return "PrintComponent"; }
|
||||
void Start() override;
|
||||
void Update(float dt) override;
|
||||
void ImGuiInspector() override;
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json&) override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
void SetMessage(std::string_view message)
|
||||
{
|
||||
|
||||
@@ -11,8 +11,7 @@ public:
|
||||
void OnTriggerEnter(GameObject* other) override;
|
||||
void OnTriggerExit(GameObject* other) override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json&) override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
int triggerEnterCount{0};
|
||||
int triggerExitCount{0};
|
||||
|
||||
@@ -9,9 +9,7 @@ public:
|
||||
|
||||
std::string GetTypeName() const override { return "WinGoal"; }
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
[[nodiscard]] bool IsReached() const { return m_Reached; }
|
||||
void Reset() { m_Reached = false; }
|
||||
|
||||
@@ -13,10 +13,7 @@ public:
|
||||
|
||||
void LateUpdate(float dt) override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ResolveReferences(const ObjectMap& objects) override;
|
||||
void ImGuiInspector() override;
|
||||
void Reflect(FieldVisitor& v) override;
|
||||
|
||||
void SetGoal(const GameObject& goal) { m_Goal = ObjectRef(goal); }
|
||||
void SetScreen(const GameObject& screen) { m_Screen = ObjectRef(screen); }
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#include <components/Checkpoint.h>
|
||||
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/Serialization/GlmJson.h>
|
||||
#include <destrum/Serialization/Reflect.h>
|
||||
|
||||
#include <components/PlayerRespawn.h>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
Checkpoint::Checkpoint(GameObject& parent)
|
||||
@@ -35,17 +33,7 @@ void Checkpoint::OnTriggerEnter(GameObject* other)
|
||||
spdlog::info("{} reached checkpoint {}", other->GetName(), GetGameObject()->GetName());
|
||||
}
|
||||
|
||||
nlohmann::json Checkpoint::Serialize() const
|
||||
void Checkpoint::Reflect(FieldVisitor& v)
|
||||
{
|
||||
return {{"spawnOffset", m_SpawnOffset}};
|
||||
}
|
||||
|
||||
void Checkpoint::Deserialize(const nlohmann::json& data)
|
||||
{
|
||||
m_SpawnOffset = data.value("spawnOffset", m_SpawnOffset);
|
||||
}
|
||||
|
||||
void Checkpoint::ImGuiInspector()
|
||||
{
|
||||
ImGui::DragFloat3("Spawn offset", glm::value_ptr(m_SpawnOffset), 0.05f);
|
||||
v.Field("spawnOffset", m_SpawnOffset, {.speed = 0.05f});
|
||||
}
|
||||
|
||||
@@ -6,10 +6,7 @@
|
||||
#include <destrum/Components/UI/UIGraphic.h>
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/Scene/Scene.h>
|
||||
#include <destrum/Serialization/GlmJson.h>
|
||||
#include <destrum/Util/ImGuiUtils.h>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <destrum/Serialization/Reflect.h>
|
||||
|
||||
namespace {
|
||||
void TintHierarchy(GameObject& object, const glm::vec4& color)
|
||||
@@ -59,49 +56,13 @@ void CrosshairUI::LateUpdate(float)
|
||||
TintHierarchy(*crosshair, focused ? m_FocusedColor : m_Color);
|
||||
}
|
||||
|
||||
nlohmann::json CrosshairUI::Serialize() const
|
||||
void CrosshairUI::Reflect(FieldVisitor& v)
|
||||
{
|
||||
return {
|
||||
{"player", m_Player},
|
||||
{"crosshair", m_Crosshair},
|
||||
{"focusIndicator", m_FocusIndicator},
|
||||
{"color", m_Color},
|
||||
{"focusedColor", m_FocusedColor},
|
||||
{"size", m_Size},
|
||||
{"focusedSize", m_FocusedSize},
|
||||
};
|
||||
}
|
||||
|
||||
void CrosshairUI::Deserialize(const nlohmann::json& data)
|
||||
{
|
||||
m_Player = data.value("player", ObjectRef{});
|
||||
m_Crosshair = data.value("crosshair", ObjectRef{});
|
||||
m_FocusIndicator = data.value("focusIndicator", ObjectRef{});
|
||||
m_Color = data.value("color", m_Color);
|
||||
m_FocusedColor = data.value("focusedColor", m_FocusedColor);
|
||||
m_Size = data.value("size", m_Size);
|
||||
m_FocusedSize = data.value("focusedSize", m_FocusedSize);
|
||||
}
|
||||
|
||||
void CrosshairUI::ResolveReferences(const ObjectMap& objects)
|
||||
{
|
||||
m_Player.Resolve(objects);
|
||||
m_Crosshair.Resolve(objects);
|
||||
m_FocusIndicator.Resolve(objects);
|
||||
}
|
||||
|
||||
void CrosshairUI::ImGuiInspector()
|
||||
{
|
||||
if (const Scene* scene = GetGameObject()->GetScene()) {
|
||||
ImGuiUtils::DrawObjectRefPicker("Player", m_Player, *scene, [](const GameObject& object) {
|
||||
return object.GetComponent<FirstPersonController>() != nullptr;
|
||||
});
|
||||
ImGuiUtils::DrawObjectRefPicker("Crosshair", m_Crosshair, *scene);
|
||||
ImGuiUtils::DrawObjectRefPicker("Focus Indicator", m_FocusIndicator, *scene);
|
||||
}
|
||||
|
||||
ImGui::ColorEdit4("Color", &m_Color.x);
|
||||
ImGui::ColorEdit4("Focused Color", &m_FocusedColor.x);
|
||||
ImGui::DragFloat("Size", &m_Size, 0.5f, 0.0f, 500.0f);
|
||||
ImGui::DragFloat("Focused Size", &m_FocusedSize, 0.5f, 0.0f, 500.0f);
|
||||
v.Field("player", m_Player, {.filter = [](const GameObject& object) { return object.GetComponent<FirstPersonController>() != nullptr; }});
|
||||
v.Field("crosshair", m_Crosshair);
|
||||
v.Field("focusIndicator", m_FocusIndicator);
|
||||
v.Field("color", m_Color, {.color = true});
|
||||
v.Field("focusedColor", m_FocusedColor, {.color = true});
|
||||
v.Field("size", m_Size, {.speed = 0.5f, .min = 0.0, .max = 500.0});
|
||||
v.Field("focusedSize", m_FocusedSize, {.speed = 0.5f, .min = 0.0, .max = 500.0});
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/ObjectModel/Transform.h>
|
||||
#include <destrum/Physics/PhysicsWorld.h>
|
||||
#include <destrum/Serialization/Reflect.h>
|
||||
#include <destrum/Util/DeltaTime.h>
|
||||
|
||||
#include <array>
|
||||
@@ -274,70 +275,40 @@ glm::vec3 FirstPersonController::GetEyePosition() const
|
||||
return position + glm::vec3{0.0f, m_EyeHeight, 0.0f};
|
||||
}
|
||||
|
||||
void FirstPersonController::Reflect(FieldVisitor& v)
|
||||
{
|
||||
v.Field("walkSpeed", m_WalkSpeed, {.speed = 0.1f, .min = 0.0, .max = 100.0});
|
||||
v.Field("sprintSpeed", m_SprintSpeed, {.speed = 0.1f, .min = 0.0, .max = 100.0});
|
||||
v.Field("groundAcceleration", m_GroundAcceleration, {.speed = 0.5f, .min = 0.0, .max = 500.0});
|
||||
v.Field("airAcceleration", m_AirAcceleration, {.speed = 0.5f, .min = 0.0, .max = 500.0});
|
||||
v.Field("jumpHeight", m_JumpHeight, {.speed = 0.01f, .min = 0.0, .max = 10.0});
|
||||
v.Field("gravity", m_Gravity, {.speed = 0.1f, .min = 0.0, .max = 100.0});
|
||||
v.Field("fallGravityMultiplier", m_FallGravityMultiplier, {.speed = 0.01f, .min = 1.0, .max = 5.0});
|
||||
v.Field("coyoteTime", m_CoyoteTime, {.speed = 0.005f, .min = 0.0, .max = 1.0});
|
||||
v.Field("jumpBufferTime", m_JumpBufferTime, {.speed = 0.005f, .min = 0.0, .max = 1.0});
|
||||
v.Field("interactDistance", m_InteractDistance, {.speed = 0.05f, .min = 0.0, .max = 20.0});
|
||||
v.Field("maxSlopeAngle", m_MaxSlopeAngle, {.min = 0.0, .max = glm::radians(89.0), .angle = true});
|
||||
v.Field("groundStickSpeed", m_GroundStickSpeed, {.speed = 0.05f, .min = 0.0, .max = 20.0});
|
||||
v.Field("mouseSensitivity", m_MouseSensitivity, {.speed = 0.0001f, .min = 0.0, .max = 0.1, .format = "%.4f"});
|
||||
v.Field("eyeHeight", m_EyeHeight, {.speed = 0.01f, .min = -2.0, .max = 2.0});
|
||||
v.Field("yaw", m_Yaw, {.angle = true, .readOnly = true});
|
||||
v.Field("pitch", m_Pitch, {.angle = true, .readOnly = true});
|
||||
}
|
||||
|
||||
void FirstPersonController::ImGuiInspector()
|
||||
{
|
||||
Component::ImGuiInspector();
|
||||
ImGui::DragFloat("Walk Speed", &m_WalkSpeed, 0.1f, 0.0f, 100.0f);
|
||||
ImGui::DragFloat("Sprint Speed", &m_SprintSpeed, 0.1f, 0.0f, 100.0f);
|
||||
ImGui::DragFloat("Ground Acceleration", &m_GroundAcceleration, 0.5f, 0.0f, 500.0f);
|
||||
ImGui::DragFloat("Air Acceleration", &m_AirAcceleration, 0.5f, 0.0f, 500.0f);
|
||||
ImGui::DragFloat("Jump Height", &m_JumpHeight, 0.01f, 0.0f, 10.0f);
|
||||
ImGui::DragFloat("Gravity", &m_Gravity, 0.1f, 0.0f, 100.0f);
|
||||
ImGui::DragFloat("Fall Gravity Multiplier", &m_FallGravityMultiplier, 0.01f, 1.0f, 5.0f);
|
||||
ImGui::DragFloat("Coyote Time", &m_CoyoteTime, 0.005f, 0.0f, 1.0f);
|
||||
ImGui::DragFloat("Jump Buffer Time", &m_JumpBufferTime, 0.005f, 0.0f, 1.0f);
|
||||
ImGui::DragFloat("Interact Distance", &m_InteractDistance, 0.05f, 0.0f, 20.0f);
|
||||
ImGui::SliderAngle("Max Slope Angle", &m_MaxSlopeAngle, 0.0f, 89.0f);
|
||||
ImGui::DragFloat("Ground Stick Speed", &m_GroundStickSpeed, 0.05f, 0.0f, 20.0f);
|
||||
ImGui::DragFloat("Mouse Sensitivity", &m_MouseSensitivity, 0.0001f, 0.0f, 0.1f);
|
||||
ImGui::DragFloat("Eye Height", &m_EyeHeight, 0.01f, -2.0f, 2.0f);
|
||||
ImGui::Text("Grounded: %s", m_Grounded ? "yes" : "no");
|
||||
ImGui::Text("Ground slope: %.1f deg", glm::degrees(std::acos(glm::clamp(m_GroundNormal.y, -1.0f, 1.0f))));
|
||||
ImGui::Text("Yaw: %.1f deg", glm::degrees(m_Yaw));
|
||||
ImGui::Text("Pitch: %.1f deg", glm::degrees(m_Pitch));
|
||||
if (bool possessed = m_Possessed; ImGui::Checkbox("Possessed", &possessed)) {
|
||||
SetPossessed(possessed);
|
||||
}
|
||||
}
|
||||
|
||||
nlohmann::json FirstPersonController::Serialize() const
|
||||
{
|
||||
return {
|
||||
{"walkSpeed", m_WalkSpeed},
|
||||
{"sprintSpeed", m_SprintSpeed},
|
||||
{"groundAcceleration", m_GroundAcceleration},
|
||||
{"airAcceleration", m_AirAcceleration},
|
||||
{"jumpHeight", m_JumpHeight},
|
||||
{"gravity", m_Gravity},
|
||||
{"fallGravityMultiplier", m_FallGravityMultiplier},
|
||||
{"coyoteTime", m_CoyoteTime},
|
||||
{"jumpBufferTime", m_JumpBufferTime},
|
||||
{"interactDistance", m_InteractDistance},
|
||||
{"maxSlopeAngle", m_MaxSlopeAngle},
|
||||
{"groundStickSpeed", m_GroundStickSpeed},
|
||||
{"mouseSensitivity", m_MouseSensitivity},
|
||||
{"eyeHeight", m_EyeHeight},
|
||||
{"yaw", m_Yaw},
|
||||
{"pitch", m_Pitch},
|
||||
};
|
||||
}
|
||||
|
||||
void FirstPersonController::Deserialize(const nlohmann::json& data)
|
||||
{
|
||||
m_WalkSpeed = data.value("walkSpeed", data.value("movementSpeed", m_WalkSpeed));
|
||||
m_SprintSpeed = data.value("sprintSpeed", m_SprintSpeed);
|
||||
m_GroundAcceleration = data.value("groundAcceleration", m_GroundAcceleration);
|
||||
m_AirAcceleration = data.value("airAcceleration", m_AirAcceleration);
|
||||
m_JumpHeight = data.value("jumpHeight", m_JumpHeight);
|
||||
m_Gravity = data.value("gravity", m_Gravity);
|
||||
m_FallGravityMultiplier = data.value("fallGravityMultiplier", m_FallGravityMultiplier);
|
||||
m_CoyoteTime = data.value("coyoteTime", m_CoyoteTime);
|
||||
m_JumpBufferTime = data.value("jumpBufferTime", m_JumpBufferTime);
|
||||
m_InteractDistance = data.value("interactDistance", m_InteractDistance);
|
||||
m_MaxSlopeAngle = data.value("maxSlopeAngle", m_MaxSlopeAngle);
|
||||
m_GroundStickSpeed = data.value("groundStickSpeed", m_GroundStickSpeed);
|
||||
m_MouseSensitivity = data.value("mouseSensitivity", m_MouseSensitivity);
|
||||
m_EyeHeight = data.value("eyeHeight", m_EyeHeight);
|
||||
m_Yaw = data.value("yaw", m_Yaw);
|
||||
m_Pitch = data.value("pitch", m_Pitch);
|
||||
Component::Deserialize(data);
|
||||
if (!data.contains("walkSpeed") && data.contains("movementSpeed")) {
|
||||
m_WalkSpeed = data.at("movementSpeed").get<float>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
#include <destrum/Components/MeshRendererComponent.h>
|
||||
#include <destrum/Graphics/Renderer.h>
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/Serialization/Reflect.h>
|
||||
#include <destrum/Util/GameState.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
LightToggle::LightToggle(GameObject& parent)
|
||||
: Activatable(parent, "LightToggle")
|
||||
{
|
||||
@@ -50,19 +49,12 @@ void LightToggle::Apply()
|
||||
renderer.updateMaterialGPU(meshRenderer->GetMaterialID());
|
||||
}
|
||||
|
||||
nlohmann::json LightToggle::Serialize() const
|
||||
void LightToggle::Reflect(FieldVisitor& v)
|
||||
{
|
||||
return {{"on", m_On}};
|
||||
v.Field("on", m_On);
|
||||
}
|
||||
|
||||
void LightToggle::Deserialize(const nlohmann::json& data)
|
||||
void LightToggle::OnFieldsEdited()
|
||||
{
|
||||
m_On = data.value("on", m_On);
|
||||
}
|
||||
|
||||
void LightToggle::ImGuiInspector()
|
||||
{
|
||||
if (bool on = m_On; ImGui::Checkbox("On", &on)) {
|
||||
SetOn(on);
|
||||
}
|
||||
Apply();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <cmath>
|
||||
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/Serialization/GlmJson.h>
|
||||
#include <destrum/Serialization/Reflect.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
@@ -50,28 +50,16 @@ glm::vec3 MovingPlatform::GetOffsetAt(float progress) const
|
||||
return m_TravelOffset * eased;
|
||||
}
|
||||
|
||||
nlohmann::json MovingPlatform::Serialize() const
|
||||
void MovingPlatform::Reflect(FieldVisitor& v)
|
||||
{
|
||||
return {
|
||||
{"travelOffset", m_TravelOffset},
|
||||
{"travelDuration", m_TravelDuration},
|
||||
{"pauseDuration", m_PauseDuration},
|
||||
{"elapsedTime", m_ElapsedTime},
|
||||
};
|
||||
}
|
||||
|
||||
void MovingPlatform::Deserialize(const nlohmann::json& data)
|
||||
{
|
||||
m_TravelOffset = data.value("travelOffset", m_TravelOffset);
|
||||
m_TravelDuration = std::max(data.value("travelDuration", m_TravelDuration), 0.01f);
|
||||
m_PauseDuration = std::max(data.value("pauseDuration", m_PauseDuration), 0.0f);
|
||||
m_ElapsedTime = std::max(data.value("elapsedTime", m_ElapsedTime), 0.0f);
|
||||
v.Field("travelOffset", m_TravelOffset, {.speed = 0.05f});
|
||||
v.Field("travelDuration", m_TravelDuration, {.speed = 0.05f, .min = 0.01, .format = "%.2f s"});
|
||||
v.Field("pauseDuration", m_PauseDuration, {.speed = 0.05f, .min = 0.0, .format = "%.2f s"});
|
||||
v.Field("elapsedTime", m_ElapsedTime, {.min = 0.0, .hidden = true});
|
||||
}
|
||||
|
||||
void MovingPlatform::ImGuiInspector()
|
||||
{
|
||||
ImGui::Text("Progress: %.0f%%", GetTravelProgress() * 100.0f);
|
||||
ImGui::DragFloat3("Travel Offset", &m_TravelOffset.x, 0.05f);
|
||||
ImGui::DragFloat("Travel Duration", &m_TravelDuration, 0.05f, 0.01f, 60.0f, "%.2f s");
|
||||
ImGui::DragFloat("Pause Duration", &m_PauseDuration, 0.05f, 0.0f, 60.0f, "%.2f s");
|
||||
Component::ImGuiInspector();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <destrum/Components/Physics/Rigidbody.h>
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/Serialization/GlmJson.h>
|
||||
#include <destrum/Serialization/Reflect.h>
|
||||
|
||||
#include <components/FirstPersonController.h>
|
||||
|
||||
@@ -46,9 +47,15 @@ void PlayerRespawn::Respawn()
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerRespawn::Reflect(FieldVisitor& v)
|
||||
{
|
||||
v.Field("killHeight", m_KillHeight);
|
||||
}
|
||||
|
||||
// The respawn point is only saved once set, until then Start uses the spawn position
|
||||
nlohmann::json PlayerRespawn::Serialize() const
|
||||
{
|
||||
nlohmann::json data{{"killHeight", m_KillHeight}};
|
||||
nlohmann::json data = Component::Serialize();
|
||||
if (m_HasRespawnPoint) {
|
||||
data["respawnPoint"] = m_RespawnPoint;
|
||||
}
|
||||
@@ -57,7 +64,7 @@ nlohmann::json PlayerRespawn::Serialize() const
|
||||
|
||||
void PlayerRespawn::Deserialize(const nlohmann::json& data)
|
||||
{
|
||||
m_KillHeight = data.value("killHeight", m_KillHeight);
|
||||
Component::Deserialize(data);
|
||||
if (data.contains("respawnPoint")) {
|
||||
SetRespawnPoint(data.at("respawnPoint").get<glm::vec3>());
|
||||
}
|
||||
@@ -65,8 +72,10 @@ void PlayerRespawn::Deserialize(const nlohmann::json& data)
|
||||
|
||||
void PlayerRespawn::ImGuiInspector()
|
||||
{
|
||||
ImGui::DragFloat("Kill height", &m_KillHeight, 0.1f);
|
||||
ImGui::DragFloat3("Respawn point", glm::value_ptr(m_RespawnPoint), 0.1f);
|
||||
Component::ImGuiInspector();
|
||||
if (ImGui::DragFloat3("Respawn Point", glm::value_ptr(m_RespawnPoint), 0.1f)) {
|
||||
m_HasRespawnPoint = true;
|
||||
}
|
||||
if (ImGui::Button("Respawn now")) {
|
||||
Respawn();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
#include <destrum/Serialization/Reflect.h>
|
||||
|
||||
PrintComponent::PrintComponent(GameObject& parent) : Component(parent, "PrintComponent")
|
||||
{
|
||||
}
|
||||
@@ -15,20 +17,7 @@ void PrintComponent::Update(float dt)
|
||||
spdlog::debug(m_message);
|
||||
}
|
||||
|
||||
void PrintComponent::ImGuiInspector()
|
||||
void PrintComponent::Reflect(FieldVisitor& v)
|
||||
{
|
||||
Component::ImGuiInspector();
|
||||
}
|
||||
|
||||
nlohmann::json PrintComponent::Serialize() const
|
||||
{
|
||||
return {{"message", m_message}};
|
||||
}
|
||||
|
||||
void PrintComponent::Deserialize(const nlohmann::json& basic_jsons)
|
||||
{
|
||||
if (basic_jsons.contains("message"))
|
||||
{
|
||||
m_message = basic_jsons.at("message").get<std::string>();
|
||||
}
|
||||
v.Field("message", m_message);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/Serialization/Reflect.h>
|
||||
|
||||
TriggerLoggerComponent::TriggerLoggerComponent(GameObject& parent)
|
||||
: Component(parent, "TriggerLoggerComponent") {}
|
||||
@@ -28,18 +29,8 @@ void TriggerLoggerComponent::OnTriggerExit(GameObject* other) {
|
||||
triggerExitCount);
|
||||
}
|
||||
|
||||
nlohmann::json TriggerLoggerComponent::Serialize() const {
|
||||
return {
|
||||
{"triggerEnterCount", triggerEnterCount},
|
||||
{"triggerExitCount", triggerExitCount}
|
||||
};
|
||||
}
|
||||
|
||||
void TriggerLoggerComponent::Deserialize(const nlohmann::json& data) {
|
||||
if (data.contains("triggerEnterCount")) {
|
||||
triggerEnterCount = data.at("triggerEnterCount").get<int>();
|
||||
}
|
||||
if (data.contains("triggerExitCount")) {
|
||||
triggerExitCount = data.at("triggerExitCount").get<int>();
|
||||
}
|
||||
void TriggerLoggerComponent::Reflect(FieldVisitor& v) {
|
||||
v.Field("triggerEnterCount", triggerEnterCount, {.readOnly = true});
|
||||
v.Field("triggerExitCount", triggerExitCount, {.readOnly = true});
|
||||
v.Field("lastOtherName", lastOtherName, {.readOnly = true, .transient = true});
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
#include <components/WinGoal.h>
|
||||
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/Serialization/Reflect.h>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
WinGoal::WinGoal(GameObject& parent)
|
||||
@@ -18,17 +18,7 @@ void WinGoal::OnActivate(GameObject& instigator)
|
||||
m_Reached = true;
|
||||
}
|
||||
|
||||
nlohmann::json WinGoal::Serialize() const
|
||||
void WinGoal::Reflect(FieldVisitor& v)
|
||||
{
|
||||
return {{"reached", m_Reached}};
|
||||
}
|
||||
|
||||
void WinGoal::Deserialize(const nlohmann::json& data)
|
||||
{
|
||||
m_Reached = data.value("reached", m_Reached);
|
||||
}
|
||||
|
||||
void WinGoal::ImGuiInspector()
|
||||
{
|
||||
ImGui::Checkbox("Reached", &m_Reached);
|
||||
v.Field("reached", m_Reached);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/Scene/Scene.h>
|
||||
#include <destrum/Util/ImGuiUtils.h>
|
||||
#include <destrum/Serialization/Reflect.h>
|
||||
|
||||
WinScreenUI::WinScreenUI(GameObject& parent)
|
||||
: Component(parent, "WinScreenUI")
|
||||
@@ -28,35 +28,8 @@ void WinScreenUI::LateUpdate(float)
|
||||
screen->SetActive(winGoal != nullptr && winGoal->IsReached());
|
||||
}
|
||||
|
||||
nlohmann::json WinScreenUI::Serialize() const
|
||||
void WinScreenUI::Reflect(FieldVisitor& v)
|
||||
{
|
||||
return {
|
||||
{"goal", m_Goal},
|
||||
{"screen", m_Screen},
|
||||
};
|
||||
}
|
||||
|
||||
void WinScreenUI::Deserialize(const nlohmann::json& data)
|
||||
{
|
||||
m_Goal = data.value("goal", ObjectRef{});
|
||||
m_Screen = data.value("screen", ObjectRef{});
|
||||
}
|
||||
|
||||
void WinScreenUI::ResolveReferences(const ObjectMap& objects)
|
||||
{
|
||||
m_Goal.Resolve(objects);
|
||||
m_Screen.Resolve(objects);
|
||||
}
|
||||
|
||||
void WinScreenUI::ImGuiInspector()
|
||||
{
|
||||
const Scene* scene = GetGameObject()->GetScene();
|
||||
if (scene == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGuiUtils::DrawObjectRefPicker("Goal", m_Goal, *scene, [](const GameObject& object) {
|
||||
return object.GetComponent<WinGoal>() != nullptr;
|
||||
});
|
||||
ImGuiUtils::DrawObjectRefPicker("Screen", m_Screen, *scene);
|
||||
v.Field("goal", m_Goal, {.filter = [](const GameObject& object) { return object.GetComponent<WinGoal>() != nullptr; }});
|
||||
v.Field("screen", m_Screen);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
@@ -415,6 +416,11 @@ namespace {
|
||||
v.Field("target", target);
|
||||
v.Field("targets", targets);
|
||||
v.Field("cache", cache, {.transient = true});
|
||||
v.Field("extents", extents, {.min = 0.5});
|
||||
v.Property("limited", limited, [this](int value) {
|
||||
limited = std::min(value, 5);
|
||||
++setterCalls;
|
||||
});
|
||||
}
|
||||
|
||||
bool flag{false};
|
||||
@@ -431,6 +437,9 @@ namespace {
|
||||
ObjectRef target;
|
||||
std::vector<ObjectRef> targets;
|
||||
int cache{0};
|
||||
glm::vec3 extents{1.0f};
|
||||
int limited{0};
|
||||
int setterCalls{0};
|
||||
};
|
||||
|
||||
[[nodiscard]] bool throwsMentioning(ReflectedComponent& component, const nlohmann::json& data, const char* key)
|
||||
@@ -493,10 +502,25 @@ namespace {
|
||||
component->Deserialize({{"mode", 2}});
|
||||
check(component->mode == TestMode::Fast, "named enums must still load from a number");
|
||||
|
||||
component->Deserialize({{"extents", {2.0f, 0.1f, -1.0f}}});
|
||||
check(component->extents == glm::vec3{2.0f, 0.5f, 0.5f}, "loading must clamp every component of a vector");
|
||||
|
||||
check(throwsMentioning(*component, {{"mode", "Turbo"}}, "\"mode\""), "an unknown enum name must fail and name the field");
|
||||
check(throwsMentioning(*component, {{"offset", "up"}}, "\"offset\""), "a wrong type must fail and name the field");
|
||||
}
|
||||
|
||||
void testReflectedProperties()
|
||||
{
|
||||
GameObject object{"reflected"};
|
||||
auto* component = object.AddComponent<ReflectedComponent>();
|
||||
component->Deserialize({{"flag", true}});
|
||||
check(component->setterCalls == 0, "a property setter must not run when its key is missing");
|
||||
|
||||
component->Deserialize({{"limited", 9}});
|
||||
check(component->limited == 5 && component->setterCalls == 1, "loading a property must go through its setter");
|
||||
check(component->Serialize().at("limited") == 5, "a property must save its current value");
|
||||
}
|
||||
|
||||
void testReflectedReferencesResolve()
|
||||
{
|
||||
ComponentFactory::Register<ReflectedComponent>("ReflectedComponent");
|
||||
@@ -538,6 +562,7 @@ void RunSerializationTests()
|
||||
{
|
||||
testReflectedRoundTrip();
|
||||
testReflectedLoadClampsAndKeepsDefaults();
|
||||
testReflectedProperties();
|
||||
testReflectedReferencesResolve();
|
||||
testFieldLabels();
|
||||
testTonemapperNames();
|
||||
|
||||
Reference in New Issue
Block a user