diff --git a/application/editormode.cpp b/application/editormode.cpp index 063f46cb..4e7fb1c2 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -1431,9 +1431,8 @@ void editor_mode::draw_orthophoto() Camera.SetMatrix(viewmatrix); glm::mat4 const view = glm::mat4(glm::mat3(viewmatrix)); ImGuiIO const &io = ImGui::GetIO(); - float const fovy = glm::radians(Global.FieldOfView / Global.ZoomFactor); float const aspect = io.DisplaySize.y > 0.0f ? io.DisplaySize.x / io.DisplaySize.y : 1.0f; - m_orthophoto.draw(glm::perspective(fovy, aspect, 0.1f, 10000.0f) * view, Camera.Pos, io.DisplaySize.x, io.DisplaySize.y); + m_orthophoto.draw(editor_mode::projection_matrix(aspect) * view, Camera.Pos, io.DisplaySize.x, io.DisplaySize.y); } editor_terrain *editor_mode::terrain_at(double X, double Z) @@ -2107,9 +2106,8 @@ void editor_mode::draw_area_fill_outline() const ImGuiIO const &io = ImGui::GetIO(); glm::mat4 const view = GfxRenderer->Camera_View_Matrix(); glm::dvec3 const camerapos = GfxRenderer->Camera_Position(); - float const fovy = glm::radians(Global.FieldOfView / Global.ZoomFactor); float const aspect = io.DisplaySize.y > 0.0f ? io.DisplaySize.x / io.DisplaySize.y : 1.0f; - glm::mat4 const viewprojection = glm::perspective(fovy, aspect, 0.1f, 10000.0f) * view; + glm::mat4 const viewprojection = editor_mode::projection_matrix(aspect) * view; auto const to_clip = [&](glm::dvec3 const &Point) { return viewprojection * glm::vec4(glm::vec3(Point - camerapos), 1.0f); }; auto const to_screen = [&](glm::vec4 const &Clip) { @@ -2198,6 +2196,14 @@ void editor_mode::render_area_fill() void editor_mode::render_gizmo_options() { + if (ImGui::Button(Global.EditorOrtho ? "3D view (O)" : "Top view, orthographic (O)")) + toggle_ortho(); + if (Global.EditorOrtho) + { + ImGui::SameLine(); + ImGui::SetNextItemWidth(120.0f); + ImGui::SliderFloat("Extent (m), wheel", &Global.EditorOrthoExtent, 5.0f, 5000.0f, "%.0f", 3.0f); + } ImGui::Checkbox("Enabled", &m_gizmo_enabled); if (!m_gizmo_enabled) return; @@ -2265,7 +2271,7 @@ void editor_mode::render_gizmo() } ImGuizmo::BeginFrame(); - ImGuizmo::SetOrthographic(false); + ImGuizmo::SetOrthographic(Global.EditorOrtho); ImGuiIO const &io = ImGui::GetIO(); ImGuizmo::SetRect(0.0f, 0.0f, io.DisplaySize.x, io.DisplaySize.y); @@ -2278,9 +2284,8 @@ void editor_mode::render_gizmo() // the engine's own projection bakes in reverse-Z (and screen orientation), which ImGuizmo // doesn't expect; rebuild a clean, standard perspective that matches the rendered view. // for the main viewport the engine uses a symmetric frustum with this exact fov/aspect. - float const fovy = glm::radians(Global.FieldOfView / Global.ZoomFactor); float const aspect = io.DisplaySize.y > 0.0f ? io.DisplaySize.x / io.DisplaySize.y : 1.0f; - glm::mat4 const projection = glm::perspective(fovy, aspect, 0.1f, 10000.0f); + glm::mat4 const projection = editor_mode::projection_matrix(aspect); if (!m_node) { @@ -2462,9 +2467,46 @@ void editor_mode::render_include_gizmo(glm::mat4 const &View, glm::mat4 const &P apply_include(); } +glm::mat4 editor_mode::projection_matrix(float const Aspect) +{ + if (Global.EditorOrtho) + { + auto const height{Global.EditorOrthoExtent}; + return glm::ortho(-height * Aspect, height * Aspect, -height, height, -10000.0f, 10000.0f); + } + return glm::perspective(glm::radians(Global.FieldOfView / Global.ZoomFactor), Aspect, 0.1f, 10000.0f); +} + +void editor_mode::toggle_ortho() +{ + Global.EditorOrtho = !Global.EditorOrtho; + if (Global.EditorOrtho) + { + m_ortho_pitch = Camera.Angle.x; + Camera.Angle.x = -M_PI_2; + Camera.Angle.z = 0.0; + } + else + { + Camera.Angle.x = m_ortho_pitch; + } +} + +void editor_mode::on_scroll(double const Xoffset, double const Yoffset) +{ + if (false == Global.EditorOrtho || ImGui::GetIO().WantCaptureMouse) + return; + Global.EditorOrthoExtent = std::clamp(Global.EditorOrthoExtent * static_cast(std::pow(0.85, Yoffset)), 5.0f, 5000.0f); +} + void editor_mode::update_camera(double const Deltatime) { Camera.Update(); + if (Global.EditorOrtho) + { + Camera.Angle.x = -M_PI_2; + Camera.Angle.z = 0.0; + } // focus animation runs after Camera.Update() so it overrides any residual velocity/rotation; // it smoothly drives both position and orientation toward the framed object @@ -2544,6 +2586,8 @@ void editor_mode::exit() m_track_point = {}; m_route = {}; m_route_gizmo_using = false; + if (Global.EditorOrtho) + toggle_ortho(); g_redo.clear(); m_history.clear(); @@ -2588,6 +2632,7 @@ void editor_mode::on_key(int const Key, int const Scancode, int const Action, in bool handled = true; switch (Key) { + case GLFW_KEY_O: toggle_ortho(); break; case GLFW_KEY_Q: m_gizmo_op = gizmo_operation::translate; break; case GLFW_KEY_W: m_gizmo_op = gizmo_operation::rotate; break; case GLFW_KEY_E: m_gizmo_op = gizmo_operation::scale; break; diff --git a/application/editormode.h b/application/editormode.h index b0885718..67019ad6 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -50,10 +50,7 @@ class editor_mode : public application_mode void on_key(int Key, int Scancode, int Action, int Mods) override; void on_cursor_pos(double Horizontal, double Vertical) override; void on_mouse_button(int Button, int Action, int Mods) override; - void on_scroll(double const Xoffset, double const Yoffset) override - { - ; - } + void on_scroll(double const Xoffset, double const Yoffset) override; void on_window_resize(int w, int h) override { ; @@ -64,6 +61,8 @@ class editor_mode : public application_mode static bool focus_active(); static void set_focus_active(bool isActive); static TCamera& get_camera() { return Camera; } + static glm::mat4 projection_matrix(float const Aspect); + void toggle_ortho(); static bool change_history() { return m_change_history; } static void set_change_history(bool enabled) { m_change_history = enabled; } static bool settings_open() { return m_settings_open; } @@ -148,6 +147,7 @@ class editor_mode : public application_mode static bool m_change_history; static bool m_settings_open; + double m_ortho_pitch{0.0}; // camera fly-mode (right mouse button held); used to flush motion when it's released command_relay m_camera_relay; bool m_camera_flying{false}; diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 4799ce62..1c8c55e8 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -36,9 +36,8 @@ class screen_projection ImGuiIO const &io = ImGui::GetIO(); m_size = io.DisplaySize; m_camera = GfxRenderer->Camera_Position(); - float const fovy = glm::radians(Global.FieldOfView / Global.ZoomFactor); float const aspect = m_size.y > 0.0f ? m_size.x / m_size.y : 1.0f; - m_viewprojection = glm::perspective(fovy, aspect, 0.1f, 10000.0f) * GfxRenderer->Camera_View_Matrix(); + m_viewprojection = editor_mode::projection_matrix(aspect) * GfxRenderer->Camera_View_Matrix(); } glm::vec4 clip(glm::dvec3 const &Point) const @@ -244,15 +243,14 @@ void editor_mode::render_track_gizmo() bool const pointmode = ui()->mode() == nodebank_panel::TRACK && m_track_point.valid() && m_track_point.path < static_cast(track->m_paths.size()); ImGuizmo::BeginFrame(); - ImGuizmo::SetOrthographic(false); + ImGuizmo::SetOrthographic(Global.EditorOrtho); ImGuiIO const &io = ImGui::GetIO(); ImGuizmo::SetRect(0.0f, 0.0f, io.DisplaySize.x, io.DisplaySize.y); glm::mat4 const view = GfxRenderer->Camera_View_Matrix(); glm::dvec3 const camerapos = GfxRenderer->Camera_Position(); - float const fovy = glm::radians(Global.FieldOfView / Global.ZoomFactor); float const aspect = io.DisplaySize.y > 0.0f ? io.DisplaySize.x / io.DisplaySize.y : 1.0f; - glm::mat4 const projection = glm::perspective(fovy, aspect, 0.1f, 10000.0f); + glm::mat4 const projection = editor_mode::projection_matrix(aspect); ImGuizmo::OPERATION const operation = (false == pointmode && m_gizmo_op == gizmo_operation::rotate) ? ImGuizmo::ROTATE_Y : ImGuizmo::TRANSLATE; @@ -869,14 +867,13 @@ void editor_mode::render_route_gizmo() } ImGuizmo::BeginFrame(); - ImGuizmo::SetOrthographic(false); + ImGuizmo::SetOrthographic(Global.EditorOrtho); ImGuiIO const &io = ImGui::GetIO(); ImGuizmo::SetRect(0.0f, 0.0f, io.DisplaySize.x, io.DisplaySize.y); glm::mat4 const view = GfxRenderer->Camera_View_Matrix(); glm::dvec3 const camerapos = GfxRenderer->Camera_Position(); - float const fovy = glm::radians(Global.FieldOfView / Global.ZoomFactor); float const aspect = io.DisplaySize.y > 0.0f ? io.DisplaySize.x / io.DisplaySize.y : 1.0f; - glm::mat4 const projection = glm::perspective(fovy, aspect, 0.1f, 10000.0f); + glm::mat4 const projection = editor_mode::projection_matrix(aspect); auto const position{route_vertex_position(route.vertex)}; if (false == m_route_gizmo_using) diff --git a/rendering/opengl33renderer.cpp b/rendering/opengl33renderer.cpp index c07efca4..767618a1 100644 --- a/rendering/opengl33renderer.cpp +++ b/rendering/opengl33renderer.cpp @@ -1593,6 +1593,13 @@ void opengl33_renderer::setup_pass(viewport_config &Viewport, renderpass_config auto const znear = ( Znear > 1.f ? Znear : Znear > 0.f ? Znear * zfar : 0.1f * Global.ZoomFactor); camera.projection() = perspective_projection(Viewport.projection, znear, zfar, frustumtest_proj); + if (EditorModeFlag && Global.EditorOrtho && Viewport.main) + { + auto const height{Global.EditorOrthoExtent}; + auto const width{height * Global.window_size.x / std::max(1.f, static_cast(Global.window_size.y))}; + camera.projection() = ortho_projection(-width, width, -height, height, -zfar, zfar); + frustumtest_proj = ortho_frustumtest_projection(-width, width, -height, height, -zfar, zfar); + } break; } case rendermode::shadows: @@ -1692,6 +1699,13 @@ void opengl33_renderer::setup_pass(viewport_config &Viewport, renderpass_config } camera.projection() = perspective_projection(proj, znear, zfar, frustumtest_proj); + if (EditorModeFlag && Global.EditorOrtho && Viewport.main) + { + auto const height{Global.EditorOrthoExtent}; + auto const width{height * Global.window_size.x / std::max(1.f, static_cast(Global.window_size.y))}; + camera.projection() = ortho_projection(-width, width, -height, height, -zfar, zfar); + frustumtest_proj = ortho_frustumtest_projection(-width, width, -height, height, -zfar, zfar); + } break; } case rendermode::reflections: diff --git a/utilities/Globals.h b/utilities/Globals.h index 800737f6..b7c20f69 100644 --- a/utilities/Globals.h +++ b/utilities/Globals.h @@ -168,6 +168,8 @@ struct global_settings { bool bUseVBO{ true }; // czy jest VBO w karcie graficznej (czy użyć) float AnisotropicFiltering{ 8.f }; // requested level of anisotropic filtering. TODO: move it to renderer object float FieldOfView{ 45.f }; // vertical field of view for the camera. TODO: move it to the renderer + bool EditorOrtho{ false }; + float EditorOrthoExtent{ 150.f }; GLint iMaxTextureSize{ 4096 }; // maksymalny rozmiar tekstury GLint iMaxCabTextureSize{ 4096 }; // largest allowed texture in vehicle cab int iMultisampling{ 2 }; // tryb antyaliasingu: 0=brak,1=2px,2=4px,3=8px,4=16px