feat: add reflection

This commit is contained in:
2026-10-30 17:09:53 +01:00
parent 0fbc023018
commit 08acd84404
10 changed files with 763 additions and 110 deletions
+1 -2
View File
@@ -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 Open() { m_IsOpen = true; }
@@ -17,9 +17,8 @@ public:
void Start() override;
void Interact(GameObject& interactor) override;
nlohmann::json Serialize() const override;
void Deserialize(const nlohmann::json& data) override;
void ResolveReferences(const ObjectMap& objects) override;
void Reflect(FieldVisitor& v) override;
void OnFieldsEdited() override;
void ImGuiInspector() override;
[[nodiscard]] std::size_t GetColorIndex() const { return m_ColorIndex; }
@@ -35,7 +34,6 @@ public:
private:
void ApplyColor();
void ActivateTargets(GameObject& interactor);
void DrawTargetsInspector();
std::size_t m_ColorIndex{0};
int m_PressCount{0};
+8 -21
View File
@@ -3,7 +3,7 @@
#include <algorithm>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/Serialization/GlmJson.h>
#include <destrum/Serialization/Reflect.h>
#include <imgui.h>
@@ -58,24 +58,13 @@ void Door::ApplyPosition()
GetGameObject()->GetTransform().SetLocalPosition(m_ClosedPosition + GetOffsetFor(m_OpenAmount));
}
nlohmann::json Door::Serialize() const
void Door::Reflect(FieldVisitor& v)
{
return {
{"openOffset", m_OpenOffset},
{"openDuration", m_OpenDuration},
{"toggleable", m_Toggleable},
{"open", m_IsOpen},
{"openAmount", m_OpenAmount},
};
}
void Door::Deserialize(const nlohmann::json& data)
{
m_OpenOffset = data.value("openOffset", m_OpenOffset);
m_OpenDuration = std::max(data.value("openDuration", m_OpenDuration), 0.0f);
m_Toggleable = data.value("toggleable", m_Toggleable);
m_IsOpen = data.value("open", m_IsOpen);
m_OpenAmount = std::clamp(data.value("openAmount", m_OpenAmount), 0.0f, 1.0f);
v.Field("openOffset", m_OpenOffset, {.speed = 0.05f});
v.Field("openDuration", m_OpenDuration, {.speed = 0.05f, .min = 0.0, .format = "%.2f s"});
v.Field("toggleable", m_Toggleable);
v.Field("open", m_IsOpen, {.hidden = true});
v.Field("openAmount", m_OpenAmount, {.min = 0.0, .max = 1.0, .hidden = true});
}
void Door::ImGuiInspector()
@@ -85,7 +74,5 @@ void Door::ImGuiInspector()
Toggle();
}
ImGui::DragFloat3("Open Offset", &m_OpenOffset.x, 0.05f);
ImGui::DragFloat("Open Duration", &m_OpenDuration, 0.05f, 0.0f, 10.0f, "%.2f s");
ImGui::Checkbox("Toggleable", &m_Toggleable);
Activatable::ImGuiInspector();
}
@@ -6,13 +6,13 @@
#include <destrum/Graphics/Renderer.h>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/Scene/Scene.h>
#include <destrum/Serialization/Reflect.h>
#include <destrum/Util/GameState.h>
#include <spdlog/spdlog.h>
#include <algorithm>
#include <array>
#include <optional>
#include <imgui.h>
@@ -108,93 +108,28 @@ void InteractableButton::ApplyColor()
renderer.updateMaterialGPU(meshRenderer->GetMaterialID());
}
nlohmann::json InteractableButton::Serialize() const
void InteractableButton::Reflect(FieldVisitor& v)
{
return {
{"colorIndex", m_ColorIndex},
{"cycleColors", m_CycleColors},
{"pressCount", m_PressCount},
{"targets", m_Targets},
};
v.Field("colorIndex", m_ColorIndex, {.max = static_cast<double>(ButtonColors.size() - 1)});
v.Field("cycleColors", m_CycleColors);
v.Field("pressCount", m_PressCount, {.readOnly = true});
v.Field("targets", m_Targets, {.filter = [this](const GameObject& object) {
return &object != GetGameObject() && object.GetComponent<Activatable>() != nullptr;
}});
}
void InteractableButton::Deserialize(const nlohmann::json& data)
void InteractableButton::OnFieldsEdited()
{
m_ColorIndex = data.value("colorIndex", m_ColorIndex) % ButtonColors.size();
m_CycleColors = data.value("cycleColors", m_CycleColors);
m_PressCount = data.value("pressCount", m_PressCount);
m_Targets = data.value("targets", std::vector<ObjectRef>{});
}
void InteractableButton::ResolveReferences(const ObjectMap& objects)
{
std::erase_if(m_Targets, [&](ObjectRef& target) {
const ObjectId fileId = target.GetId();
if (!target.Resolve(objects)) {
spdlog::warn("{} targets object {} which is not in the scene file, dropping it", GetGameObject()->GetName(), fileId);
return true;
}
return !target.IsSet();
});
if (m_CycleColors) {
ApplyColor();
}
}
void InteractableButton::ImGuiInspector()
{
ImGui::Text("Presses: %d", m_PressCount);
if (ImGui::Button("Press")) {
Interact(*GetGameObject());
}
if (ImGui::Checkbox("Cycle Colors", &m_CycleColors) && m_CycleColors) {
ApplyColor();
}
DrawTargetsInspector();
}
void InteractableButton::DrawTargetsInspector()
{
ImGui::SeparatorText("Targets");
const Scene* scene = GetGameObject()->GetScene();
if (scene == nullptr) {
ImGui::TextDisabled("Not in a scene");
return;
}
std::optional<ObjectId> targetToRemove;
for (const ObjectRef& targetRef : m_Targets) {
const ObjectId targetId = targetRef.GetId();
const GameObject* target = targetRef.Get(*scene);
ImGui::PushID(static_cast<int>(targetId));
if (ImGui::SmallButton("x")) {
targetToRemove = targetId;
}
ImGui::SameLine();
if (target != nullptr) {
ImGui::TextUnformatted(target->GetName().c_str());
} else {
ImGui::TextDisabled("Missing object %llu", static_cast<unsigned long long>(targetId));
}
ImGui::PopID();
}
if (targetToRemove) {
RemoveTarget(*targetToRemove);
}
if (ImGui::BeginCombo("##AddTarget", "Add target...")) {
for (const auto& object : scene->GetObjects()) {
const bool alreadyTargeted = object != nullptr &&
std::ranges::find(m_Targets, ObjectRef(*object)) != m_Targets.end();
if (object == nullptr || object.get() == GetGameObject() || alreadyTargeted ||
object->GetComponent<Activatable>() == nullptr) {
continue;
}
ImGui::PushID(static_cast<int>(object->GetId()));
if (ImGui::Selectable(object->GetName().c_str())) {
AddTarget(*object);
}
ImGui::PopID();
}
ImGui::EndCombo();
}
Interactable::ImGuiInspector();
}