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
@@ -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";
+1 -3
View File
@@ -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; }
+1 -3
View File
@@ -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, &copy, 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;