Dependency position
#647 (GetComponentRaw) ──┐
#649 (Registry) ├──► THIS ISSUE
#650 (Registrations) ──┘
Wait for all three — #647, #649, and #650 — to be merged before starting.
This is the integration point where all Track A and Track B work comes together.
Why
InspectorViewUIComponent.cpp today has a per-component block for every known type:
// current code
auto* tc = actor->GetComponent<TransformComponent>();
if (tc) { ImGui::DragFloat3("Position", &tc->Position.x); ... }
auto* mc = actor->GetComponent<MeshComponent>();
if (mc) { ... }
// ...one block per component — plugin components can never appear
After this issue, all of that is replaced by a single generic loop that works for any registered component, including future game and plugin types.
Work
File: Tetragrama/Components/InspectorViewUIComponent.cpp
Replace the per-component rendering code with:
void InspectorViewUIComponent::DrawSelectedEntity(ZEngine::ECS::EntityID entity_id,
ZEngine::ECS::Scene& scene)
{
if (!scene.IsAlive(entity_id))
return;
const ZEngine::ECS::ArchetypeMask mask = scene.GetMask(entity_id);
ZEngine::ECS::ComponentReflectionRegistry::Get().ForEach(
[&](const ZEngine::ECS::ComponentMeta& meta)
{
if (!ZEngine::ECS::MaskHas(mask, meta.TypeID))
return;
void* raw = scene.GetComponentRaw(entity_id, meta.TypeID);
if (!raw)
return;
if (ImGui::CollapsingHeader(meta.TypeName, ImGuiTreeNodeFlags_DefaultOpen))
DrawComponentFields(meta, raw);
});
}
void InspectorViewUIComponent::DrawComponentFields(const ZEngine::ECS::ComponentMeta& meta,
void* data)
{
ImGui::PushID(static_cast<int>(meta.TypeID));
for (uint32_t i = 0; i < meta.FieldCount; ++i)
{
const auto& field = meta.Fields[i];
if (field.Hidden)
continue;
void* ptr = static_cast<uint8_t*>(data) + field.Offset;
ImGui::BeginDisabled(field.ReadOnly);
DrawField(field, ptr);
if (field.Tooltip && ImGui::IsItemHovered())
ImGui::SetTooltip("%s", field.Tooltip);
ImGui::EndDisabled();
}
ImGui::PopID();
}
void InspectorViewUIComponent::DrawField(const ZEngine::ECS::FieldDescriptor& field,
void* ptr)
{
using FT = ZEngine::ECS::FieldType;
switch (field.Type)
{
case FT::Float: ImGui::DragFloat(field.Name, static_cast<float*>(ptr), 0.05f, field.Min, field.Max); break;
case FT::Vec3f: ImGui::DragFloat3(field.Name, static_cast<float*>(ptr), 0.05f); break;
case FT::Bool: ImGui::Checkbox(field.Name, static_cast<bool*>(ptr)); break;
case FT::Int32: ImGui::DragInt(field.Name, static_cast<int*>(ptr)); break;
case FT::UInt32: { auto v = *static_cast<uint32_t*>(ptr); ImGui::Text("%s: %u", field.Name, v); break; }
case FT::String: ImGui::InputText(field.Name, static_cast<char*>(ptr), field.StringCap); break;
case FT::AssetUUID: {
auto* uuid = static_cast<uuids::uuid*>(ptr);
auto name = ZEngine::Managers::AssetManager::Instance() ?
/* lookup by uuid → asset name if found, else hex string */
uuids::to_string(*uuid) : uuids::to_string(*uuid);
ImGui::Text("%s: %s", field.Name, name.c_str());
break;
}
case FT::Enum: {
auto* val = static_cast<int32_t*>(ptr);
const char* current = "unknown";
for (uint32_t i = 0; i < field.EnumCount; ++i)
if (field.EnumValues[i].Value == *val) { current = field.EnumValues[i].Name; break; }
if (ImGui::BeginCombo(field.Name, current)) {
for (uint32_t i = 0; i < field.EnumCount; ++i) {
bool sel = (field.EnumValues[i].Value == *val);
if (ImGui::Selectable(field.EnumValues[i].Name, sel))
*val = static_cast<int32_t>(field.EnumValues[i].Value);
}
ImGui::EndCombo();
}
break;
}
default: ImGui::Text("%s: (type not rendered)", field.Name); break;
}
}
Connecting to the Actor tier
The inspector currently receives an Actor*. Extract the EntityID and pass the engine's Scene:
if (actor)
{
auto* ctx = ZEngine::Engine::GetContext();
auto* scene = ctx ? ctx->Scene : nullptr;
if (scene)
DrawSelectedEntity(actor->GetEntityID(), *scene);
}
Acceptance criteria
- Inspector shows all fields of all 8 components for any selected actor
- Fields are editable where
ReadOnly = false
NameComponent.Value renders as an editable text input
LightComponent.LightType renders as a dropdown with Directional/Point/Spot
UUIDComponent.Value is read-only text
- No per-component
#include or per-component if block remains in the inspector
- Plugin components (not yet built) would appear automatically once registered
Dependency position
Wait for all three — #647, #649, and #650 — to be merged before starting.
This is the integration point where all Track A and Track B work comes together.
Why
InspectorViewUIComponent.cpptoday has a per-component block for every known type:After this issue, all of that is replaced by a single generic loop that works for any registered component, including future game and plugin types.
Work
File:
Tetragrama/Components/InspectorViewUIComponent.cppReplace the per-component rendering code with:
Connecting to the Actor tier
The inspector currently receives an
Actor*. Extract theEntityIDand pass the engine'sScene:Acceptance criteria
ReadOnly = falseNameComponent.Valuerenders as an editable text inputLightComponent.LightTyperenders as a dropdown with Directional/Point/SpotUUIDComponent.Valueis read-only text#includeor per-componentifblock remains in the inspector