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
112 changes: 73 additions & 39 deletions Tetragrama/Controllers/EditorCameraController.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,78 @@
#include <Tetragrama/Controllers/EditorCameraController.h>
#include <Tetragrama/EditorScene.h>
#include <ZEngine/Core/Maths/MathUtils.h>
#include <ZEngine/Rendering/Scenes/RenderScene.h>
#include <ZEngine/ECS/Components/MeshComponent.h>
#include <ZEngine/Engine.h>
#include <ZEngine/Rendering/Scenes/SceneRayQuery.h>

using namespace ZEngine::Rendering::Cameras;
using namespace ZEngine::Core::Maths;

namespace
{
ZEngine::Rendering::Scenes::SceneRaycastHit RaycastEditorScene(void* context, Vec3f origin, Vec3f direction, float max_distance)
{
ZEngine::Rendering::Scenes::SceneRaycastHit miss = {};
miss.Distance = max_distance;

auto* app = static_cast<ZEngine::Applications::GameApplication*>(context);
if (!app || !app->CurrentScene)
return miss;

return ZEngine::Rendering::Scenes::RaycastSceneBounds(*app->CurrentScene, origin, direction, max_distance);
}

bool GetEditorSelectionBounds(void* context, Vec3f& out_center, float& out_radius)
{
using namespace ZEngine::Rendering::Scenes;

auto* app = static_cast<ZEngine::Applications::GameApplication*>(context);
if (!app || !app->CurrentScene)
return false;

auto* scene = static_cast<Tetragrama::EditorScene*>(app->CurrentScene);
if (!scene || !scene->SelectedActorHandle.Valid())
return false;

auto* engine = ZEngine::Engine::GetContext();
if (!engine || !engine->ActorManager)
return false;

auto* actor = engine->ActorManager->Access(scene->SelectedActorHandle);
if (!actor)
return false;

const auto* mesh = actor->GetComponent<ZEngine::ECS::Components::MeshComponent>();
if (!mesh || mesh->RenderInstanceId == UINT32_MAX)
return false;

SceneRaycastBounds bounds = {};
if (!TryGetSceneInstanceBounds(*scene, mesh->RenderInstanceId, bounds))
return false;

out_center = bounds.Center;
out_radius = bounds.Radius;
return true;
}

bool GetEditorSceneBounds(void* context, Vec3f& out_center, float& out_radius)
{
using namespace ZEngine::Rendering::Scenes;

auto* app = static_cast<ZEngine::Applications::GameApplication*>(context);
if (!app || !app->CurrentScene)
return false;

SceneRaycastBounds bounds = {};
if (!TryGetSceneBounds(*app->CurrentScene, bounds))
return false;

out_center = bounds.Center;
out_radius = bounds.Radius;
return true;
}
} // namespace

namespace Tetragrama::Controllers
{
void EditorCameraController::Initialize(ZEngine::Core::Memory::ArenaAllocator* arena, ZEngine::Windows::CoreWindow* window, ZEngine::Input::InputManager* input_manager, ZEngine::Applications::GameApplication* app)
Expand Down Expand Up @@ -35,44 +103,10 @@ namespace Tetragrama::Controllers
m_camera = ZPushStructCtorArgs(arena, FlyCamera, logicalW / logicalH, settings);
m_camera->SetViewportSize(logicalW, logicalH);

m_camera->Hooks.Raycast = [](Vec3f, Vec3f, float maxDist) { return maxDist; };

m_camera->Hooks.GetSelectionBounds = [app]() -> std::pair<Vec3f, float> {
using namespace ZEngine::Rendering::Scenes;
static const std::pair<Vec3f, float> kFallback = {
Vec3f{0.0f, 0.0f, 0.0f},
5.0f
};

if (!app || !app->CurrentScene)
return kFallback;

auto* scene = app->CurrentScene;
int32_t sel_id = scene->SelectedInstanceId.value.load(std::memory_order_acquire);
if (sel_id <= 0)
return kFallback;

// Seqlock read — abort if writer is active; this is best-effort for a focus op.
uint64_t seq1 = scene->m_seq.value.load(std::memory_order_acquire);
if (seq1 & 1)
return kFallback;

for (uint32_t i = 0; i < scene->Instances.size(); ++i)
{
if ((int32_t) scene->Instances[i].Id == sel_id)
{
const auto& t = scene->Instances[i].Transform;
Vec3f center = {t[3][0], t[3][1], t[3][2]};

uint64_t seq2 = scene->m_seq.value.load(std::memory_order_acquire);
if (seq1 != seq2)
return kFallback;

return {center, 2.0f};
}
}
return kFallback;
};
m_camera->Hooks.Context = app;
m_camera->Hooks.Raycast = &RaycastEditorScene;
m_camera->Hooks.GetSelectionBounds = &GetEditorSelectionBounds;
m_camera->Hooks.GetSceneBounds = &GetEditorSceneBounds;

FlyCameraController::Initialize(input_manager, arena);
}
Expand Down
27 changes: 11 additions & 16 deletions Tetragrama/Editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,32 +152,27 @@ namespace Tetragrama
ZUIUILayer->OnEvent(e);
}

// Gate camera-controller mouse routing on viewport focus (Gap 3)
bool is_mouse_event = (e.GetType() == ZEngine::Core::EventType::MouseButtonPressed || e.GetType() == ZEngine::Core::EventType::MouseButtonReleased || e.GetType() == ZEngine::Core::EventType::MouseMoved || e.GetType() == ZEngine::Core::EventType::MouseWheel);

bool viewport_active = RenderPipeline && RenderPipeline->ZUICtx && RenderPipeline->ZUICtx->ViewportHovered;

if (CameraController && (!is_mouse_event || viewport_active))
{
CameraController->OnEvent(e);
}

OnEvent(e);
}

void Editor::OnUpdate(float dt)
{
CHECK_AND_ESCAPE_NULL(ZUIUILayer)
if (CameraController)
{
ZEngine::UI::ZUIInputCapture capture = {};
if (RenderPipeline && RenderPipeline->ZUICtx)
capture = ZEngine::UI::ZUIGetInputCapture(RenderPipeline->ZUICtx);
CameraController->SetInputCapture(capture.Pointer, capture.Keyboard);
}

// Camera controller is self-gating — it reads cursor position vs viewport rect
// set each frame by ViewportPanel::BuildContent. No external activation needed.
ZUIUILayer->Update(dt);
if (ZUIUILayer)
ZUIUILayer->Update(dt);
}

void Editor::OnEvent(Core::CoreEvent& /*e*/)
{
// Event routing is handled in ProcessEvent (which also gates camera input
// on viewport focus). Nothing extra needed here.
// Window and UI events are handled in ProcessEvent. Camera state is sampled
// from InputManager during the application update.
}

void Editor::OnPreRender() {}
Expand Down
1 change: 0 additions & 1 deletion Tetragrama/Editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ namespace Tetragrama
virtual void OnUpdate(float dt) override;
virtual void OnEvent(ZEngine::Core::CoreEvent&) override;

// Gates camera-controller routing on viewport hover (Gap 3)
void ProcessEvent(ZEngine::Core::CoreEvent&) override;

virtual void OnPreRender() override;
Expand Down
7 changes: 6 additions & 1 deletion Tetragrama/Panels/ViewportPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,19 @@ namespace Tetragrama::Panels
img->Size[1] = ZFill();
img->TextureIndex = m_scene_texture.Valid() ? (uint32_t) m_scene_texture.Index : 0xFFFFFFFFu;
ZUIBoxSetColor(img, 1.f, 1.f, 1.f, m_scene_texture.Valid() ? 1.f : 0.f);
ctx->ViewportInputKey = img->Key;

ZUISignal img_sig = ZUISignalFromBox(ctx, img);
ZUISignal img_sig = ZUISignalFromBox(ctx, img);
ZUIPopBox(ctx);

// Feed the viewport rect to the camera controller every frame so it can
// self-gate on cursor position without depending on the ZUI hit-test chain.
if (app->CameraController)
{
app->CameraController->SetViewportRect(rect[0], rect[1], rect[2], rect[3]);
if (sw > 0.0f && sh > 0.0f)
app->CameraController->SetViewport(sw, sh);
}

// Keep ViewportHovered for ZUI-level concerns (drag-drop, scroll routing).
ctx->ViewportHovered = (img_sig.Flags & ZUI_SignalHovered) != 0;
Expand Down
Loading
Loading