#ifndef EDITOR_HPP #define EDITOR_HPP #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 #include #include #include #include #include #include #include #include #include namespace OpenEngine { class CameraController : public NativeScriptableEntity { public: void OnCreate() { }; void OnUpdate() { auto delta = Time::DeltaTime(); if (HasComponent()) { auto& position = GetComponent().translation; if (Input::IsKeyPressed(KeyCode::Up)) position.y += 5.0 * delta; if (Input::IsKeyPressed(KeyCode::Down)) position.y -= 5.0 * delta; if (Input::IsKeyPressed(KeyCode::Right)) position.x += 5.0 * delta; if (Input::IsKeyPressed(KeyCode::Left)) position.x -= 5.0 * delta; } }; void OnDestroy() { }; }; class EditorLayer : public Layer { public: EditorLayer() : Layer("editor_layer") { } ~EditorLayer() {}; void OnAttach() override { OE_PROFILE_FUNCTION(); FramebufferSpecification specs; specs.width = 1280; specs.height = 720; framebuffer = FrameBuffer::Create(specs); scene = CreateRef(); scene_hierarchy.SetContext(scene); } void OnDetach() override { } void OnUpdate() override { FramebufferSpecification spec = framebuffer->GetSpecification(); if (viewport_size.x > 0.0f && viewport_size.y > 0.0f && (spec.width != viewport_size.x || spec.height != viewport_size.y)) { framebuffer->Resize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y); } { OE_PROFILE_SCOPE("Setting up Rendering"); framebuffer->Bind(); scene->OnViewportResize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y); } Renderer2D::ResetStats(); RenderCommand::SetClearColor({0.11f, 0.11f, 0.15f, 1.0f}); RenderCommand::Clear(); scene->OnUpdate(); 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(); 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 float t = (value - minInput) / (maxInput - minInput); // 2. Use glm::mix to interpolate between the output range return glm::mix(minOutput, maxOutput, t); }; glm::vec3 ScreenToWorld( float screenX, float screenY, float screenZ, // depth value (0.0 = near plane, 1.0 = far plane) const glm::mat4& view, const glm::mat4& projection, int viewportWidth, int viewportHeight) { // 1. Convert screen coordinates to normalized device coordinates (NDC) // Screen space: origin at top-left, Y points down // NDC space: origin at center, range [-1, 1] for x and y float x = (2.0f * screenX) / viewportWidth - 1.0f; float y = 1.0f - (2.0f * screenY) / viewportHeight; // Flip Y float z = 2.0f * screenZ - 1.0f; // [0,1] -> [-1,1] glm::vec4 clipCoords(x, y, z, 1.0f); // 2. Transform from clip space to view space glm::mat4 invProjection = glm::inverse(projection); glm::vec4 viewCoords = invProjection * clipCoords; // 3. Transform from view space to world space glm::mat4 invView = glm::inverse(view); glm::vec4 worldCoords = invView * viewCoords; // 4. Perspective divide if (worldCoords.w != 0.0f) { worldCoords /= worldCoords.w; } return glm::vec3(worldCoords); }; void DrawViewport() { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 0, 0 }); ImGui::Begin("Viewport"); bool focused = ImGui::IsWindowFocused(); bool hovered = ImGui::IsWindowHovered(); Application::Get().GetImGuiLayer()->SetBlockEvents((!focused && !hovered) || ImGui::IsAnyItemActive()); ImVec2 viewport_panel_size = ImGui::GetContentRegionAvail(); viewport_size = { viewport_panel_size.x, viewport_panel_size.y }; ImVec2 imgui_cursor_position = ImGui::GetCursorScreenPos(); auto pair_position = Input::GetMousePosition(); glm::vec2 mouse_position = { pair_position.first, pair_position.second }; int max_x = viewport_size.x + imgui_cursor_position.x; int max_y = viewport_size.y + imgui_cursor_position.y; if (focused || hovered) { float x = mouse_position.x - imgui_cursor_position.x; float y = mouse_position.y - imgui_cursor_position.y; /* auto& cam = camera.GetCamera(); world_pos_cursor = ScreenToWorld(x, y, 0.0f, cam.GetViewMatrix(), cam.GetProjectionMatrix(), viewport_size.x, viewport_size.y); */ } 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(); }; void DrawStats() { auto stats = Renderer2D::GetStats(); static float time = 0; time += Time::DeltaTime(); if (time >= 1) { time = 0; } ImGui::Begin("Statistics"); ImGui::Text("FPS: %0.0f", 1 / Time::DeltaTime()); ImGui::Text("Renderer2D:"); ImGui::Text("\t\tDraw calls: %d", stats.draw_calls); ImGui::Text("\t\tQuad count: %d", stats.quad_count); ImGui::Text("\t\tVertices count: %d", stats.GetToralVertexCount()); ImGui::Text("\t\tIndices count: %d", stats.GetToralIndexCount()); /* for (auto& result : profiling_results) { char label[50]; strcpy(label, "%.3fms "); strcat(label, result.name); ImGui::Text(label, result.duration); } profiling_results.clear(); */ ImGui::End(); }; /* void test2() { // (optional) set browser properties fileDialog.SetTitle("title"); fileDialog.SetTypeFilters({ ".oes" }); fileDialog.Open(); }; */ 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(); { // Testing file pickers if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("Save Scene As", "Ctrl+S")) { std::string file = FileDialogs::SaveFile("useless"); OE_TRACE("saving to filename: {}", file); if (!file.empty()) { SceneSerializer serializer(scene); serializer.Serialize(file); } } if (ImGui::MenuItem("Open Scene", "Ctrl+O")) { std::string file = FileDialogs::OpenFile("useless"); OE_DEBUG("loading filename: {}", file); if (!file.empty()) { scene = CreateRef(); scene->OnViewportResize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y); scene_hierarchy.SetContext(scene); SceneSerializer serializer(scene); serializer.Deserialize(file); } } if (ImGui::MenuItem("New Scene", "Ctrl+N")) { scene = CreateRef(); scene->OnViewportResize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y); scene_hierarchy.SetContext(scene); } ImGui::Separator(); if (ImGui::MenuItem("Exit")) Application::Get().Close(); ImGui::EndMenu(); } ImGui::EndMainMenuBar(); } /* fileDialog.Display(); if(fileDialog.HasSelected()) { OE_TRACE("Selected filename {}", fileDialog.GetSelected().string()); fileDialog.ClearSelected(); } */ } ImGui::DockSpaceOverViewport(); DrawStats(); scene_hierarchy.OnImGuiRender(); DrawViewport(); ImGui::ShowDemoWindow(); }; private: std::vector profiling_results; glm::vec2 world_pos_cursor = { 0.0f, 0.0f }; 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; }; } class EditorApp : public OpenEngine::Application { public: EditorApp(); ~EditorApp(); }; #endif // EDITOR_HPP /* #include #include std::string OpenFile() { char buffer[1024]; // This command opens a native GTK file picker and returns the path FILE* pipe = popen("zenity --file-selection", "r"); if (!pipe) return ""; if (fgets(buffer, sizeof(buffer), pipe) != NULL) { std::string path = buffer; path.erase(path.find_last_not_of("\n") + 1); // Clean newline pclose(pipe); return path; } pclose(pipe); return ""; } */