Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
628 changes: 522 additions & 106 deletions Tetragrama/Components/HierarchyViewUIComponent.cpp

Large diffs are not rendered by default.

24 changes: 21 additions & 3 deletions Tetragrama/Components/HierarchyViewUIComponent.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#pragma once
#include <Tetragrama/Components/UIComponent.h>
#include <Tetragrama/Editor.h>
#include <ZEngine/Core/Containers/Array.h>
#include <ZEngine/Core/Memory/Allocator.h>
#include <ZEngine/ECS/ActorManager.h>
#include <ZEngine/ECS/EntityID.h>
#include <imgui.h>

namespace Tetragrama::Components
Expand All @@ -16,9 +20,23 @@ namespace Tetragrama::Components
virtual void Render(ZEngine::Rendering::Renderers::GraphicRenderer* const renderer, ZEngine::Hardwares::CommandBuffer* const command_buffer) override;

private:
void RenderGuizmo(EditorPtr app, EditorScenePtr scene);
void RenderGuizmo(EditorPtr app, EditorScenePtr scene);

int m_gizmo_operation{-1};
char m_filter_buf[128] = {};
static void DrawArrow(ImDrawList* dl, ImVec2 pos, float row_h, bool expanded, ImU32 color);
static void DrawTypeIcon(ImDrawList* dl, ImVec2 pos, float sz, const char* type, bool is_collection);

int m_gizmo_operation{-1};
char m_filter_buf[128] = {};

ZEngine::ECS::ActorHandle m_renaming_handle = {};
char m_rename_buf[128] = {};

// Collapsed entity IDs — everything NOT in this list is expanded by default.
ZEngine::Core::Memory::ArenaAllocator m_outliner_arena = {};
ZEngine::Core::Containers::Array<ZEngine::ECS::EntityID> m_collapsed = {};
bool m_scene_root_collapsed = false;

bool IsCollapsed(ZEngine::ECS::EntityID eid) const;
void ToggleCollapsed(ZEngine::ECS::EntityID eid);
};
} // namespace Tetragrama::Components
16 changes: 16 additions & 0 deletions ZEngine/ZEngine/ECS/Components/ParentComponent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once
#include <ZEngine/ECS/EntityID.h>
#include <ZEngine/ZEngineDef.h>

namespace ZEngine::ECS::Components
{
// Links an entity to its parent in the scene hierarchy.
// Entities without this component are roots — their WorldTransform equals their local transform.
// HierarchySystem propagates parent WorldTransform × local matrix each frame.
struct ParentComponent
{
EntityID Parent = INVALID_ENTITY;
};

static_assert(sizeof(ParentComponent) <= 8, "ParentComponent exceeds expected size");
} // namespace ZEngine::ECS::Components
10 changes: 5 additions & 5 deletions ZEngine/ZEngine/ECS/Components/TransformComponent.h
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
#pragma once
#include <ZEngine/Core/Maths/Matrix.h>
#include <ZEngine/Core/Maths/Vec.h>
#include <ZEngine/ZEngineDef.h>

namespace ZEngine::ECS::Components
{
// Plain-data transform. No methods, no computed matrix.
// Separate from Rendering::Components::TransformComponent which has methods and a Mat4f.
// Position, Rotation, Scale are LOCAL-space values edited by the user/gizmo.
// WorldTransform is computed each frame by HierarchySystem and consumed by
// TransformSyncSystem and LightSyncSystem — never set directly.
struct TransformComponent
{
Core::Maths::Vec3f Position = {0.f, 0.f, 0.f};
Core::Maths::Vec3f Rotation = {0.f, 0.f, 0.f}; // radians (pitch, yaw, roll)
Core::Maths::Vec3f Scale = {1.f, 1.f, 1.f};

// Previous-frame position for fixed-timestep interpolation (game-loop.md)
Core::Maths::Vec3f PreviousPosition = {0.f, 0.f, 0.f};
Core::Maths::Mat4f WorldTransform = Core::Maths::Identity<Core::Maths::Mat4f>();
};

static_assert(sizeof(TransformComponent) <= 64, "TransformComponent exceeds cache line");
static_assert(alignof(TransformComponent) <= 16, "TransformComponent misaligned");
} // namespace ZEngine::ECS::Components
42 changes: 42 additions & 0 deletions ZEngine/ZEngine/ECS/Systems/HierarchySystem.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include <ZEngine/Core/Maths/Matrix.h>
#include <ZEngine/ECS/Components/ParentComponent.h>
#include <ZEngine/ECS/Components/TransformComponent.h>
#include <ZEngine/ECS/Systems/HierarchySystem.h>

using namespace ZEngine::Core::Maths;
using namespace ZEngine::ECS::Components;

namespace ZEngine::ECS::Systems
{
void SyncHierarchy(ECS::Scene& scene)
{
// Pass 1 — roots: WorldTransform = local matrix
scene.ForEach<TransformComponent>([&](EntityID id, TransformComponent& tc) {
if (scene.HasComponent<ParentComponent>(id))
return;
tc.WorldTransform = ComposeTransformMatrix(tc.Position, tc.Rotation, tc.Scale);
});

// Pass 2+ — children: WorldTransform = parent.World × local
// Repeat until no change to handle arbitrary nesting depth.
bool changed = true;
while (changed)
{
changed = false;
scene.ForEach<TransformComponent, ParentComponent>([&](EntityID, TransformComponent& tc, ParentComponent& pc) {
if (pc.Parent == INVALID_ENTITY)
return;
TransformComponent* parent_tc = scene.GetComponent<TransformComponent>(pc.Parent);
if (!parent_tc)
return;
Mat4f local = ComposeTransformMatrix(tc.Position, tc.Rotation, tc.Scale);
Mat4f world = parent_tc->WorldTransform * local;
if (world(0, 0) != tc.WorldTransform(0, 0) || world(0, 3) != tc.WorldTransform(0, 3) || world(1, 3) != tc.WorldTransform(1, 3) || world(2, 3) != tc.WorldTransform(2, 3))
{
tc.WorldTransform = world;
changed = true;
}
});
}
}
} // namespace ZEngine::ECS::Systems
15 changes: 15 additions & 0 deletions ZEngine/ZEngine/ECS/Systems/HierarchySystem.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once
#include <ZEngine/ECS/Scene.h>

namespace ZEngine::ECS::Systems
{
// Propagates local→world transforms top-down each frame.
//
// Two passes per frame:
// Pass 1 — roots (no ParentComponent): WorldTransform = ComposeTransformMatrix(local)
// Pass 2 — children: WorldTransform = parent.WorldTransform × ComposeTransformMatrix(local)
// Repeated until stable to handle arbitrary hierarchy depth.
//
// Must run before SyncECSToRenderScene and SyncECSToLights.
void SyncHierarchy(Scene& scene);
} // namespace ZEngine::ECS::Systems
18 changes: 8 additions & 10 deletions ZEngine/ZEngine/ECS/Systems/LightSyncSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,11 @@ namespace ZEngine::ECS::Systems
scene.ForEach<TransformComponent, LightComponent>([&](EntityID, TransformComponent& tc, LightComponent& lc) {
if (lc.LightType == LightComponent::Type::Directional && lights.DirectionalCount < 4)
{
// Build rotation-only matrix and extract forward direction (column 2).
// ComposeTransformMatrix uses YXZ Euler order; column 2 = (-sy, sx*cy, cx*cy).
Mat4f rot = ComposeTransformMatrix(Vec3f(0.f, 0.f, 0.f), tc.Rotation, Vec3f(1.f, 1.f, 1.f));

// Forward direction = column 2 of WorldTransform (already includes parent rotation).
auto& dir = lights.DirectionalLights[lights.DirectionalCount++];
dir.Direction.x = rot(0, 2);
dir.Direction.y = rot(1, 2);
dir.Direction.z = rot(2, 2);
dir.Direction.x = tc.WorldTransform(0, 2);
dir.Direction.y = tc.WorldTransform(1, 2);
dir.Direction.z = tc.WorldTransform(2, 2);
dir.Direction.w = 0.f;
dir.Color.x = lc.Color[0];
dir.Color.y = lc.Color[1];
Expand All @@ -33,10 +30,11 @@ namespace ZEngine::ECS::Systems
}
else if (lc.LightType == LightComponent::Type::Point && lights.PointCount < 8)
{
// World position = translation column of WorldTransform.
auto& pt = lights.PointLights[lights.PointCount++];
pt.Position.x = tc.Position.x;
pt.Position.y = tc.Position.y;
pt.Position.z = tc.Position.z;
pt.Position.x = tc.WorldTransform(0, 3);
pt.Position.y = tc.WorldTransform(1, 3);
pt.Position.z = tc.WorldTransform(2, 3);
pt.Position.w = 1.f;
pt.Color.x = lc.Color[0];
pt.Color.y = lc.Color[1];
Expand Down
6 changes: 1 addition & 5 deletions ZEngine/ZEngine/ECS/Systems/TransformSyncSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,10 @@ namespace ZEngine::ECS::Systems
using namespace Components;
using namespace Core::Maths;

// Use Position directly — interpolation between PreviousPosition and Position
// is deferred until fixed-timestep physics/simulation systems are active.
// Applying alpha here conflicts with immediate gizmo-driven position updates.
scene.ForEach<TransformComponent, MeshComponent>([&](EntityID, TransformComponent& tc, MeshComponent& mc) {
if (mc.RenderInstanceId == UINT32_MAX)
return;
Mat4f mat = ComposeTransformMatrix(tc.Position, tc.Rotation, tc.Scale);
render_scene.SetInstanceTransform(mc.RenderInstanceId, mat);
render_scene.SetInstanceTransform(mc.RenderInstanceId, tc.WorldTransform);
});
}
} // namespace ZEngine::ECS::Systems
2 changes: 2 additions & 0 deletions ZEngine/ZEngine/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <ZEngine/Core/VFS/VFSPath.h>
#include <ZEngine/ECS/Reflection/BuiltInComponentReflection.h>
#include <ZEngine/ECS/Reflection/ComponentReflectionRegistry.h>
#include <ZEngine/ECS/Systems/HierarchySystem.h>
#include <ZEngine/ECS/Systems/LightSyncSystem.h>
#include <ZEngine/ECS/Systems/TransformSyncSystem.h>
#include <ZEngine/Engine.h>
Expand Down Expand Up @@ -291,6 +292,7 @@ namespace ZEngine

if (g_engine_ctx->Scene && g_app->CurrentScene)
{
ECS::Systems::SyncHierarchy(*g_engine_ctx->Scene);
ECS::Systems::SyncECSToRenderScene(*g_engine_ctx->Scene, alpha, *g_app->CurrentScene);
ECS::Systems::SyncECSToLights(*g_engine_ctx->Scene, *g_app->CurrentScene);
}
Expand Down
Loading