From 02430073eccdebe0a1409d99e2c5c8de41c1a9a6 Mon Sep 17 00:00:00 2001 From: Erris Date: Fri, 20 Feb 2026 22:59:30 +0100 Subject: [PATCH] transform guizmos, math and bugfixing file open/save --- editor/include/editor.hpp | 132 +++++++++++++----- editor/include/panels/scene_hierarchy.hpp | 3 + imgui.ini | 2 +- justfile | 33 +++++ open_engine/include/open_engine.hpp | 1 + open_engine/include/open_engine/math/math.hpp | 13 ++ .../include/open_engine/scene/components.hpp | 9 +- .../include/open_engine/scene/scene.hpp | 2 + .../src/open_engine/core/file_dialogs.cpp | 1 - .../src/open_engine/imgui/imgui_layer.cpp | 2 + open_engine/src/open_engine/math/math.cpp | 43 ++++++ open_engine/src/open_engine/scene/scene.cpp | 13 ++ 12 files changed, 210 insertions(+), 44 deletions(-) create mode 100755 justfile create mode 100644 open_engine/include/open_engine/math/math.hpp create mode 100644 open_engine/src/open_engine/math/math.cpp diff --git a/editor/include/editor.hpp b/editor/include/editor.hpp index dd58e9e..862529e 100755 --- a/editor/include/editor.hpp +++ b/editor/include/editor.hpp @@ -3,6 +3,12 @@ #include +#include "open_engine/core.hpp" +#include "open_engine/events/event.hpp" +#include "open_engine/events/key_event.hpp" +#include "open_engine/logging.hpp" +#include "open_engine/math/math.hpp" +#include "open_engine/scene/components.hpp" #include "panels/scene_hierarchy.hpp" //#include @@ -14,6 +20,7 @@ #include #include #include +#include namespace OpenEngine { @@ -68,16 +75,6 @@ namespace OpenEngine { scene = CreateRef(); - //sq_entity = scene->CreateEntity("square"); - - //sq_entity.AddComponents(); - //sq_entity.AddComponents(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); - - //camera_bis = scene->CreateEntity("Main camera"); - //camera_bis.AddComponents(); - //camera_bis.AddComponents(); - //camera_bis.AddComponents().Bind(); - scene_hierarchy.SetContext(scene); } @@ -92,12 +89,10 @@ namespace OpenEngine { (spec.width != viewport_size.x || spec.height != viewport_size.y)) { framebuffer->Resize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y); - //camera.OnResize(viewport_size.x, viewport_size.y); } { OE_PROFILE_SCOPE("Setting up Rendering"); - //camera.OnUpdate(); framebuffer->Bind(); scene->OnViewportResize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y); @@ -107,36 +102,49 @@ namespace OpenEngine { RenderCommand::SetClearColor({0.11f, 0.11f, 0.15f, 1.0f}); RenderCommand::Clear(); - //Renderer2D::BeginScene(camera.GetCamera()); - - /* - for (float y = -5.0f; y < 5.0f; y += 0.5f) { - for (float x = -5.0f; x < 5.0f; x += 0.5f) { - - glm::vec4 gradient_color = {(x + 5.0f) / 10.0f, 0.3f, (y + 5.0f) / 10.0f, 1.0f}; - Renderer2D::DrawQuad({{x, y, 0.0f}, glm::vec3(0.45f, 0.45f, 0.0f)}, gradient_color); - } - } - - Transform tr5 = { - glm::vec3(world_pos_cursor.x, world_pos_cursor.y, 0.9f), - glm::vec3(0.1f, 0.1f, 0.0f), 0.0f}; - - Renderer2D::DrawQuad(tr5, {1, 1, 1, 1}); - */ - scene->OnUpdate(); - //Renderer2D::EndScene(); - framebuffer->Unbind(); } + bool EditorKeyBinds(KeyPressedEvent& event) + { + if (event.GetRepeatCount() > 0) + return false; + bool control = Input::IsKeyPressed(Key::LeftControl) || Input::IsKeyPressed(Key::RightControl); + bool shift = Input::IsKeyPressed(Key::LeftShift) || Input::IsKeyPressed(Key::RightShift); + + switch(event.GetKeyCode()) { + case KeyCode::Q: { + guizmo_operation = -1; + break; + } + case KeyCode::W: { + guizmo_operation = ImGuizmo::OPERATION::TRANSLATE; + break; + } + case KeyCode::E: { + guizmo_operation = ImGuizmo::OPERATION::ROTATE; + break; + } + case KeyCode::R: { + guizmo_operation = ImGuizmo::OPERATION::SCALE; + break; + } + default: + break; + } + + return true; + }; + void OnEvent(Event& event) override { OE_PROFILE_FUNCTION(); - //camera.OnEvent(event); - } + + EventDispatcher dispatcher(event); + dispatcher.Dispatch(BIND_EVENT_FN(EditorLayer::EditorKeyBinds)); + }; float remap(float value, float minInput, float maxInput, float minOutput, float maxOutput) { // 1. Normalize the input to a 0.0 - 1.0 range @@ -144,7 +152,7 @@ namespace OpenEngine { // 2. Use glm::mix to interpolate between the output range return glm::mix(minOutput, maxOutput, t); - } + }; glm::vec3 ScreenToWorld( float screenX, @@ -188,7 +196,7 @@ namespace OpenEngine { ImGui::Begin("Viewport"); bool focused = ImGui::IsWindowFocused(); bool hovered = ImGui::IsWindowHovered(); - Application::Get().GetImGuiLayer()->SetBlockEvents(!focused || !hovered); + Application::Get().GetImGuiLayer()->SetBlockEvents((!focused && !hovered) || ImGui::IsAnyItemActive()); ImVec2 viewport_panel_size = ImGui::GetContentRegionAvail(); @@ -218,6 +226,8 @@ namespace OpenEngine { uint32_t texture_id = framebuffer->GetColorAttachmentRendererID(); ImGui::Image((void*)(uint64_t)texture_id, ImVec2{ viewport_size.x, viewport_size.y }, ImVec2{ 0, 1 }, ImVec2{ 1, 0 }); + DrawGuizmos(); + ImGui::End(); ImGui::PopStyleVar(); }; @@ -266,6 +276,49 @@ namespace OpenEngine { }; */ + void DrawGuizmos() + { + Entity selected_entity = scene_hierarchy.GetSelectedEntity(); + if (selected_entity && selected_entity.HasComponent() && guizmo_operation != -1) { + ImGuizmo::SetOrthographic(false); + ImGuizmo::SetDrawlist(); + + auto window_position = ImGui::GetWindowPos(); + auto window_size = ImGui::GetWindowSize(); + ImGuizmo::SetRect(window_position.x, window_position.y, window_size.x, window_size.y); + + auto camera_entity = scene->GetPrimaryCamera(); + auto camera = camera_entity.GetComponents().camera; + const glm::mat4& projection = camera.GetProjection(); + glm::mat4 camera_view = glm::inverse(camera_entity.GetComponents().GetTransform()); + + auto& transform_comp = selected_entity.GetComponents(); + glm::mat4 transform = transform_comp.GetTransform(); + + bool snap = Input::IsKeyPressed(KeyCode::LeftControl); + float snap_value = 0.1f; + if (guizmo_operation == ImGuizmo::OPERATION::ROTATE) + snap_value = 10.0f; + + float snap_values[] = { snap_value, snap_value, snap_value }; + + ImGuizmo::Manipulate(glm::value_ptr(camera_view), glm::value_ptr(projection), + (ImGuizmo::OPERATION)guizmo_operation, ImGuizmo::LOCAL, + glm::value_ptr(transform), nullptr, + snap ? snap_values : nullptr); + + if (ImGuizmo::IsUsing()) { + glm::vec3 translation, rotation, scale; + Math::DecomposeTransform(transform, translation, rotation, scale); + + glm::vec3 delta_rotation = rotation - transform_comp.rotation; + transform_comp.translation = translation; + transform_comp.rotation += delta_rotation; + transform_comp.scale = scale; + } + } + }; + void OnImGuiRender() override { OE_PROFILE_FUNCTION(); @@ -274,7 +327,7 @@ namespace OpenEngine { // Testing file pickers if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("File")) { - if (ImGui::MenuItem("Save As", "Ctrl+S")) { + if (ImGui::MenuItem("Save Scene As", "Ctrl+S")) { std::string file = FileDialogs::SaveFile("useless"); OE_TRACE("saving to filename: {}", file); if (!file.empty()) { @@ -282,7 +335,7 @@ namespace OpenEngine { serializer.Serialize(file); } } - if (ImGui::MenuItem("Load", "Ctrl+O")) { + if (ImGui::MenuItem("Open Scene", "Ctrl+O")) { std::string file = FileDialogs::OpenFile("useless"); OE_DEBUG("loading filename: {}", file); if (!file.empty()) { @@ -334,10 +387,13 @@ namespace OpenEngine { glm::vec2 viewport_size = { 0.0f, 0.0f }; Ref framebuffer; + SceneHierarchy scene_hierarchy; Entity camera_bis; + int guizmo_operation = -1; + Ref scene; Entity sq_entity; //ImGui::FileBrowser fileDialog; diff --git a/editor/include/panels/scene_hierarchy.hpp b/editor/include/panels/scene_hierarchy.hpp index 11c9576..c99533d 100644 --- a/editor/include/panels/scene_hierarchy.hpp +++ b/editor/include/panels/scene_hierarchy.hpp @@ -15,6 +15,9 @@ namespace OpenEngine { void OnImGuiRender(); + Entity GetSelectedEntity() const { return selected_context; }; + void SetSelectedEntity(Entity entity) { selected_context = entity; }; + private: void DrawEntityNode(Entity& entity); void DrawComponents(Entity& entity); diff --git a/imgui.ini b/imgui.ini index ad4430c..f3921b3 100644 --- a/imgui.ini +++ b/imgui.ini @@ -4,7 +4,7 @@ Size=1272,1362 Collapsed=0 [Window][Debug##Default] -Pos=431,670 +Pos=-32,545 Size=400,400 Collapsed=0 diff --git a/justfile b/justfile new file mode 100755 index 0000000..0787dba --- /dev/null +++ b/justfile @@ -0,0 +1,33 @@ +default: build_and_run + +alias c := config +alias b := build +alias r := run +alias br := build_and_run +alias cln := clean +alias v := valgrind + +run: + @echo Running ${BINARY_NAME} + @./build/${BINARY_NAME} + +build: + time cmake --build build --config ${BUILD_TYPE} + +build_and_run: build + just run + +clean: + rm build -rf + +config: + conan install . --output-folder=build --build=missing -s build_type=${BUILD_TYPE} + @echo Configuring project with build type: ${BUILD_TYPE} + cmake -S . -G Ninja -B build \ + -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=${COMPILE_COMMANDS} \ + -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake +# ; cp build/compile_commands.json . + +valgrind: build + valgrind --track-origins=yes ./build/${BINARY_NAME} diff --git a/open_engine/include/open_engine.hpp b/open_engine/include/open_engine.hpp index f474b24..9b5c7f1 100644 --- a/open_engine/include/open_engine.hpp +++ b/open_engine/include/open_engine.hpp @@ -31,4 +31,5 @@ #include "open_engine/renderer/shader.hpp" #include "open_engine/scene/entity.hpp" #include "open_engine/scene/scene.hpp" + #endif // OPEN_ENGINE_HPP diff --git a/open_engine/include/open_engine/math/math.hpp b/open_engine/include/open_engine/math/math.hpp new file mode 100644 index 0000000..19addf6 --- /dev/null +++ b/open_engine/include/open_engine/math/math.hpp @@ -0,0 +1,13 @@ +#ifndef MATH_HPP +#define MATH_HPP + +#include + +namespace OpenEngine::Math { + + bool DecomposeTransform(const glm::mat4& transform, + glm::vec3& out_translation, glm::vec3& out_rotation, + glm::vec3& out_scale); +} + +#endif // MATH_HPP diff --git a/open_engine/include/open_engine/scene/components.hpp b/open_engine/include/open_engine/scene/components.hpp index 248e330..cfdab9c 100644 --- a/open_engine/include/open_engine/scene/components.hpp +++ b/open_engine/include/open_engine/scene/components.hpp @@ -9,6 +9,10 @@ #include #include #include + +#define GLM_ENABLE_EXPERIMENTAL +#include + #include #include #include @@ -55,10 +59,7 @@ namespace OpenEngine { { glm::mat4 transform = glm::translate(glm::mat4(1.0f), translation); - transform *= glm::rotate(glm::mat4(1.0f), rotation.x, { 1, 0, 0 }) - * glm::rotate(glm::mat4(1.0f), rotation.y, { 0, 1, 0 }) - * glm::rotate(glm::mat4(1.0f), rotation.z, { 0, 0, 1 }); - + transform *= glm::toMat4(glm::quat(rotation)); transform *= glm::scale(glm::mat4(1.0f), scale); return transform; diff --git a/open_engine/include/open_engine/scene/scene.hpp b/open_engine/include/open_engine/scene/scene.hpp index b9b7977..0339809 100644 --- a/open_engine/include/open_engine/scene/scene.hpp +++ b/open_engine/include/open_engine/scene/scene.hpp @@ -22,6 +22,8 @@ namespace OpenEngine { entt::registry& GetRegistry() { return registry; }; + Entity GetPrimaryCamera(); + private: entt::registry registry; diff --git a/open_engine/src/open_engine/core/file_dialogs.cpp b/open_engine/src/open_engine/core/file_dialogs.cpp index 44e3fbc..d467831 100644 --- a/open_engine/src/open_engine/core/file_dialogs.cpp +++ b/open_engine/src/open_engine/core/file_dialogs.cpp @@ -41,7 +41,6 @@ namespace OpenEngine { } } - // TODO: Debug Make work consistently std::string FileDialogs::SaveFile(const char* filters) { NFD_Init(); diff --git a/open_engine/src/open_engine/imgui/imgui_layer.cpp b/open_engine/src/open_engine/imgui/imgui_layer.cpp index b0adfd5..3e2d065 100644 --- a/open_engine/src/open_engine/imgui/imgui_layer.cpp +++ b/open_engine/src/open_engine/imgui/imgui_layer.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "imgui.h" #include #include @@ -309,6 +310,7 @@ namespace OpenEngine { ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); + ImGuizmo::BeginFrame(); } void ImGuiLayer::End() diff --git a/open_engine/src/open_engine/math/math.cpp b/open_engine/src/open_engine/math/math.cpp new file mode 100644 index 0000000..f8b5845 --- /dev/null +++ b/open_engine/src/open_engine/math/math.cpp @@ -0,0 +1,43 @@ +#include + +#include + +#define GLM_ENABLE_EXPERIMENTAL +#include + +namespace OpenEngine::Math { + + bool DecomposeTransform(const glm::mat4& transform, + glm::vec3& out_translation, glm::vec3& out_rotation, + glm::vec3& out_scale) + { + // 1. Extract Translation + out_translation = glm::vec3(transform[3]); + + // 2. Extract Scale and handle Negative Scaling (Reflection) + glm::vec3 vX = glm::vec3(transform[0]); + glm::vec3 vY = glm::vec3(transform[1]); + glm::vec3 vZ = glm::vec3(transform[2]); + + out_scale.x = glm::length(vX); + out_scale.y = glm::length(vY); + out_scale.z = glm::length(vZ); + + // If the determinant is negative, one axis is mirrored + if (glm::determinant(transform) < 0) { + out_scale.x *= -1.0f; + } + + // 3. Extract Rotation (Orthonormalize) + // Avoid division by zero if scale is near-zero + glm::mat3 rotation_matrix; + rotation_matrix[0] = (out_scale.x != 0.0f) ? vX / out_scale.x : glm::vec3(1, 0, 0); + rotation_matrix[1] = (out_scale.y != 0.0f) ? vY / out_scale.y : glm::vec3(0, 1, 0); + rotation_matrix[2] = (out_scale.z != 0.0f) ? vZ / out_scale.z : glm::vec3(0, 0, 1); + + out_rotation = glm::eulerAngles(glm::quat_cast(rotation_matrix)); + + return true; + } + +} diff --git a/open_engine/src/open_engine/scene/scene.cpp b/open_engine/src/open_engine/scene/scene.cpp index f812d87..6692ef4 100644 --- a/open_engine/src/open_engine/scene/scene.cpp +++ b/open_engine/src/open_engine/scene/scene.cpp @@ -80,4 +80,17 @@ namespace OpenEngine { camera_comp.camera.SetViewportSize(width, height); } } + + Entity Scene::GetPrimaryCamera() + { + auto view = registry.view(); + + for (auto entity : view) { + const auto& camera = view.get(entity); + if (camera.primary) + return Entity { entity, this }; + } + + return {}; + } }