hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/application.cpp59
-rw-r--r--src/core/application.h11
-rw-r--r--src/platform/opengl/opengl_device.cpp1
-rw-r--r--src/platform/vulkan/vulkan_common.h2
-rw-r--r--src/platform/vulkan/vulkan_swapchain.cpp12
-rw-r--r--src/rendering/black_hole_renderer.cpp22
-rw-r--r--src/rendering/black_hole_renderer.h4
-rw-r--r--src/rendering/render_path.cpp4
-rw-r--r--src/rendering/rhi.h2
-rw-r--r--src/rendering/scene_renderer.cpp21
-rw-r--r--src/rendering/scene_renderer.h1
-rw-r--r--src/scene/scene.cpp4
-rw-r--r--src/scene/scene.h2
-rw-r--r--src/scene/scene_types.h2
-rw-r--r--src/ui/ui_layer.cpp97
-rw-r--r--src/ui/workspace.h12
16 files changed, 236 insertions, 20 deletions
diff --git a/src/core/application.cpp b/src/core/application.cpp
index f751d2f..1cb0be6 100644
--- a/src/core/application.cpp
+++ b/src/core/application.cpp
@@ -10,6 +10,8 @@
#include <imgui.h>
#include <glm/glm.hpp>
#include <algorithm>
+#include <thread>
+#include <chrono>
namespace Donut
{
@@ -79,6 +81,9 @@ namespace Donut
g_prev_scroll = glfwSetScrollCallback((GLFWwindow*)m_window->get_native_window(), donut_scroll_callback);
+ m_device->set_vsync(SettingsManager::get_v_sync_enabled());
+ glfwGetWindowSize((GLFWwindow*)m_window->get_native_window(), &m_windowed_w, &m_windowed_h);
+
m_start_time = glfwGetTime();
DONUT_INFO("Application ready on {} backend", m_device->device_name());
}
@@ -87,24 +92,72 @@ namespace Donut
{
while (m_running)
{
+ double frame_start = glfwGetTime();
glfwPollEvents();
if (!m_minimized && m_device)
render_frame();
+
+ if (!SettingsManager::get_v_sync_enabled())
+ {
+ int fps = SettingsManager::get_target_fps();
+ if (fps > 0)
+ {
+ double budget = 1.0 / (double)fps;
+ double elapsed = glfwGetTime() - frame_start;
+ if (elapsed < budget)
+ std::this_thread::sleep_for(std::chrono::duration<double>(budget - elapsed));
+ }
+ }
+ }
+ }
+
+ auto Application::set_resolution(int w, int h) -> void
+ {
+ if (m_window && !is_fullscreen())
+ glfwSetWindowSize((GLFWwindow*)m_window->get_native_window(), w, h);
+ }
+
+ auto Application::set_fullscreen(bool on) -> void
+ {
+ if (!m_window) return;
+ GLFWwindow* win = (GLFWwindow*)m_window->get_native_window();
+ if (on == is_fullscreen()) return;
+ if (on)
+ {
+ glfwGetWindowPos(win, &m_windowed_x, &m_windowed_y);
+ glfwGetWindowSize(win, &m_windowed_w, &m_windowed_h);
+ GLFWmonitor* mon = glfwGetPrimaryMonitor();
+ const GLFWvidmode* mode = glfwGetVideoMode(mon);
+ glfwSetWindowMonitor(win, mon, 0, 0, mode->width, mode->height, mode->refreshRate);
+ }
+ else
+ {
+ glfwSetWindowMonitor(win, nullptr, m_windowed_x, m_windowed_y, m_windowed_w, m_windowed_h, 0);
}
}
+ auto Application::set_vsync(bool on) -> void { if (m_device) m_device->set_vsync(on); }
+ auto Application::set_ui_scale(float scale) -> void { ImGui::GetIO().FontGlobalScale = scale; }
+ auto Application::is_fullscreen() const -> bool
+ { return m_window && glfwGetWindowMonitor((GLFWwindow*)m_window->get_native_window()) != nullptr; }
+ auto Application::get_window_size(int& w, int& h) const -> void
+ { w = 0; h = 0; if (m_window) glfwGetWindowSize((GLFWwindow*)m_window->get_native_window(), &w, &h); }
+
auto Application::render_frame() -> void
{
RHI::CommandList* cmd = m_device->begin_frame(glm::vec4(0.05f, 0.06f, 0.10f, 1.0f));
if (!cmd) return;
m_device->imgui_new_frame();
- View view = m_ui->draw(UIContext{ *m_scene, m_device->device_name() });
- update_input(view);
- m_render_path->sync_hdri(m_scene->hdri_path);
int w = 0, h = 0;
glfwGetFramebufferSize((GLFWwindow*)m_window->get_native_window(), &w, &h);
+ m_scene->scene_camera.set_projection(45.0f, (float)w / (float)std::max(h, 1), 0.1f, 1000.0f);
+
+ View view = m_ui->draw(UIContext{ *m_scene, m_device->device_name(), *this });
+ update_input(view);
+ m_render_path->sync_hdri(m_scene->hdri_path);
+
float time = (float)(glfwGetTime() - m_start_time);
m_render_path->render(*cmd, *m_scene, view, w, h, m_user_moving, time);
diff --git a/src/core/application.h b/src/core/application.h
index 880a495..d8136a5 100644
--- a/src/core/application.h
+++ b/src/core/application.h
@@ -15,7 +15,7 @@
namespace Donut
{
- class Application
+ class Application : public AppActions
{
public:
Application(const std::string& name = "Donut",
@@ -28,6 +28,13 @@ namespace Donut
auto get_window() -> Window& { return *m_window; }
static auto get() -> Application& { return *s_instance; }
+ auto set_resolution(int w, int h) -> void override;
+ auto set_fullscreen(bool on) -> void override;
+ auto set_vsync(bool on) -> void override;
+ auto set_ui_scale(float scale) -> void override;
+ auto get_window_size(int& w, int& h) const -> void override;
+ auto is_fullscreen() const -> bool override;
+
private:
auto on_init() -> void;
auto on_shutdown() -> void;
@@ -49,6 +56,8 @@ namespace Donut
double m_start_time = 0.0;
bool m_user_moving = false;
+ int m_windowed_x = 100, m_windowed_y = 100, m_windowed_w = 1280, m_windowed_h = 720;
+
bool m_running = true;
bool m_minimized = false;
diff --git a/src/platform/opengl/opengl_device.cpp b/src/platform/opengl/opengl_device.cpp
index 1eb3c7f..3fe6d73 100644
--- a/src/platform/opengl/opengl_device.cpp
+++ b/src/platform/opengl/opengl_device.cpp
@@ -220,6 +220,7 @@ namespace Donut::RHI
}
auto resize(int w, int h) -> void override { m_width = w; m_height = h; }
auto wait_idle() -> void override { glFinish(); }
+ auto set_vsync(bool enabled) -> void override { glfwSwapInterval(enabled ? 1 : 0); }
auto create_buffer(BufferType t, size_t size, const void* data) -> Ref<Buffer> override
{ return create_ref<GLBuffer>(t, size, data); }
diff --git a/src/platform/vulkan/vulkan_common.h b/src/platform/vulkan/vulkan_common.h
index 242ab9b..5689614 100644
--- a/src/platform/vulkan/vulkan_common.h
+++ b/src/platform/vulkan/vulkan_common.h
@@ -247,6 +247,7 @@ namespace Donut::RHI
auto shutdown() -> void override;
auto resize(int width, int height) -> void override { m_framebuffer_resized = true; m_width = width; m_height = height; }
auto wait_idle() -> void override { if (m_device) vkDeviceWaitIdle(m_device); }
+ auto set_vsync(bool enabled) -> void override { m_vsync = enabled; m_framebuffer_resized = true; }
auto create_buffer(BufferType type, size_t size, const void* data) -> Ref<Buffer> override;
auto create_texture(int w, int h, Format format, Filter filter, const void* data) -> Ref<Texture> override;
@@ -281,6 +282,7 @@ namespace Donut::RHI
GLFWwindow* m_window = nullptr;
int m_width = 0, m_height = 0;
bool m_framebuffer_resized = false;
+ bool m_vsync = true;
std::string m_gpu_name;
VkInstance m_instance = VK_NULL_HANDLE;
diff --git a/src/platform/vulkan/vulkan_swapchain.cpp b/src/platform/vulkan/vulkan_swapchain.cpp
index 914df62..c6157b0 100644
--- a/src/platform/vulkan/vulkan_swapchain.cpp
+++ b/src/platform/vulkan/vulkan_swapchain.cpp
@@ -117,7 +117,17 @@ namespace Donut::RHI
sci.imageExtent = m_extent; sci.imageArrayLayers = 1;
sci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
sci.preTransform = caps.currentTransform; sci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
- sci.presentMode = VK_PRESENT_MODE_FIFO_KHR; sci.clipped = VK_TRUE;
+ VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR;
+ if (!m_vsync)
+ {
+ uint32_t pmc = 0; vkGetPhysicalDeviceSurfacePresentModesKHR(m_physical, m_surface, &pmc, nullptr);
+ std::vector<VkPresentModeKHR> modes(pmc);
+ vkGetPhysicalDeviceSurfacePresentModesKHR(m_physical, m_surface, &pmc, modes.data());
+ auto has = [&](VkPresentModeKHR m){ return std::find(modes.begin(), modes.end(), m) != modes.end(); };
+ if (has(VK_PRESENT_MODE_IMMEDIATE_KHR)) present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
+ else if (has(VK_PRESENT_MODE_MAILBOX_KHR)) present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
+ }
+ sci.presentMode = present_mode; sci.clipped = VK_TRUE;
uint32_t fam[2] = { m_graphics_family, m_present_family };
if (m_graphics_family != m_present_family)
{ sci.imageSharingMode = VK_SHARING_MODE_CONCURRENT; sci.queueFamilyIndexCount = 2; sci.pQueueFamilyIndices = fam; }
diff --git a/src/rendering/black_hole_renderer.cpp b/src/rendering/black_hole_renderer.cpp
index c7b58c5..55d0ff2 100644
--- a/src/rendering/black_hole_renderer.cpp
+++ b/src/rendering/black_hole_renderer.cpp
@@ -42,9 +42,6 @@ namespace Donut
m_sim_ubo = device.create_buffer(BufferType::Uniform, 16);
std::vector<uint8_t> obj(800, 0);
- int num_objects = 1; std::memcpy(obj.data() + 0, &num_objects, 4);
- float pos_radius[4] = { 0, 0, 0, SagA_rs }; std::memcpy(obj.data() + 16, pos_radius, 16);
- float color[4] = { 0, 0, 0, 1 }; std::memcpy(obj.data() + 272, color, 16);
m_obj_ubo->update(obj.data(), obj.size());
{
@@ -78,7 +75,8 @@ namespace Donut
}
auto BlackHoleRenderer::render_geodesic(RHI::CommandList& cmd, const GeodesicView& view,
- const BlackHoleParams& params, RHI::Texture* cubemap) -> void
+ const BlackHoleParams& params,
+ const std::vector<SceneObject>& objects, RHI::Texture* cubemap) -> void
{
CamUBO cam{};
cam.pos = view.position; cam.right = view.right; cam.up = view.up; cam.fwd = view.forward;
@@ -106,6 +104,22 @@ namespace Donut
};
m_disk_ubo->update(disk, sizeof(disk));
+ {
+ const float k = SagA_rs / SCENE_UNITS_PER_RS;
+ std::vector<uint8_t> objbuf(800, 0);
+ int n = std::min((int)objects.size(), 16);
+ std::memcpy(objbuf.data(), &n, 4);
+ for (int i = 0; i < n; ++i)
+ {
+ const SceneObject& o = objects[i];
+ float pr[4] = { o.position.x * k, o.position.y * k, o.position.z * k, o.radius * k };
+ float col[4] = { o.color.r, o.color.g, o.color.b, 1.0f };
+ std::memcpy(objbuf.data() + 16 + 16 * i, pr, 16);
+ std::memcpy(objbuf.data() + 272 + 16 * i, col, 16);
+ }
+ m_obj_ubo->update(objbuf.data(), objbuf.size());
+ }
+
RenderTarget* target = view.moving ? m_geo_lo.get() : m_geo_hi.get();
m_last_target = target;
diff --git a/src/rendering/black_hole_renderer.h b/src/rendering/black_hole_renderer.h
index b68a791..414cd0c 100644
--- a/src/rendering/black_hole_renderer.h
+++ b/src/rendering/black_hole_renderer.h
@@ -3,6 +3,7 @@
#include "rhi.h"
#include "scene/scene_types.h"
#include <glm/glm.hpp>
+#include <vector>
namespace Donut
{
@@ -24,7 +25,8 @@ namespace Donut
auto init(RHI::Device& device) -> bool;
auto render_geodesic(RHI::CommandList& cmd, const GeodesicView& view,
- const BlackHoleParams& params, RHI::Texture* cubemap) -> void;
+ const BlackHoleParams& params,
+ const std::vector<SceneObject>& objects, RHI::Texture* cubemap) -> void;
auto blit(RHI::CommandList& cmd, int fb_width, int fb_height) -> void;
diff --git a/src/rendering/render_path.cpp b/src/rendering/render_path.cpp
index b096302..0963d2a 100644
--- a/src/rendering/render_path.cpp
+++ b/src/rendering/render_path.cpp
@@ -39,8 +39,6 @@ namespace Donut
cmd.begin_render_pass(nullptr, clear);
if (view == View::Scene)
{
- float aspect = (float)fb_width / (float)std::max(fb_height, 1);
- scene.scene_camera.set_projection(45.0f, aspect, 0.1f, 1000.0f);
CameraView cv;
cv.view = scene.scene_camera.get_view_matrix();
cv.projection = scene.scene_camera.get_projection_matrix();
@@ -72,7 +70,7 @@ namespace Donut
gv.moving = moving;
gv.time = time;
- m_black_hole_renderer->render_geodesic(cmd, gv, scene.black_hole, m_cubemap.get());
+ m_black_hole_renderer->render_geodesic(cmd, gv, scene.black_hole, scene.objects, m_cubemap.get());
cmd.begin_render_pass(nullptr, clear);
m_black_hole_renderer->blit(cmd, fb_width, fb_height);
m_device->imgui_render(cmd);
diff --git a/src/rendering/rhi.h b/src/rendering/rhi.h
index 7684aa0..db0711b 100644
--- a/src/rendering/rhi.h
+++ b/src/rendering/rhi.h
@@ -102,6 +102,8 @@ namespace Donut::RHI
virtual auto resize(int width, int height) -> void = 0;
virtual auto wait_idle() -> void = 0;
+ virtual auto set_vsync(bool enabled) -> void { (void)enabled; }
+
virtual auto create_buffer(BufferType type, size_t size, const void* data = nullptr) -> Ref<Buffer> = 0;
virtual auto create_texture(int width, int height, Format format, Filter filter, const void* data = nullptr) -> Ref<Texture> = 0;
virtual auto create_cubemap_from_hdri(const std::string& equirect_path) -> Ref<Texture> = 0;
diff --git a/src/rendering/scene_renderer.cpp b/src/rendering/scene_renderer.cpp
index c9da332..c2dd9d1 100644
--- a/src/rendering/scene_renderer.cpp
+++ b/src/rendering/scene_renderer.cpp
@@ -38,6 +38,8 @@ namespace Donut
struct SkyboxUBO { glm::mat4 projection; glm::mat4 view; };
static_assert(sizeof(SkyboxUBO) == 128, "SkyboxUBO std140 layout mismatch");
+
+ constexpr float BH_SCENE_RADIUS = SCENE_UNITS_PER_RS;
}
auto SceneRenderer::init(RHI::Device& device) -> bool
@@ -128,6 +130,8 @@ namespace Donut
m_skybox_pipeline = device.create_pipeline(d);
}
+ m_bh_ubo = device.create_buffer(BufferType::Uniform, sizeof(SphereUBO));
+
DONUT_INFO("SceneRenderer ready ({} grid verts, {} sphere indices, skybox)",
m_grid_vertex_count, m_sphere_index_count);
return true;
@@ -182,6 +186,23 @@ namespace Donut
cmd.bind_index_buffer(m_sphere_ib.get());
cmd.draw_indexed(m_sphere_index_count);
}
+
+ SphereUBO bh{};
+ bh.view_projection = vp;
+ bh.transform = glm::scale(glm::mat4(1.0f), glm::vec3(BH_SCENE_RADIUS));
+ bh.color = glm::vec3(0.02f);
+ bh.specular = 0.10f;
+ bh.light_pos = glm::vec3(10.0f, 20.0f, 10.0f);
+ bh.camera_pos = cam.position;
+ bh.is_selected = 1;
+ bh.outline_color = glm::vec3(0.98f, 0.62f, 0.20f);
+ bh.outline_width = 0.30f;
+ m_bh_ubo->update(&bh, sizeof(bh));
+ cmd.bind_uniform(0, m_bh_ubo.get());
+ cmd.bind_texture(1, cubemap);
+ cmd.bind_vertex_buffer(m_sphere_vb.get());
+ cmd.bind_index_buffer(m_sphere_ib.get());
+ cmd.draw_indexed(m_sphere_index_count);
}
{
diff --git a/src/rendering/scene_renderer.h b/src/rendering/scene_renderer.h
index 06d9d49..4939d43 100644
--- a/src/rendering/scene_renderer.h
+++ b/src/rendering/scene_renderer.h
@@ -40,6 +40,7 @@ namespace Donut
Ref<RHI::Buffer> m_sphere_ib;
int m_sphere_index_count = 0;
std::vector<Ref<RHI::Buffer>> m_sphere_ubos;
+ Ref<RHI::Buffer> m_bh_ubo;
Ref<RHI::Pipeline> m_skybox_pipeline;
Ref<RHI::Buffer> m_skybox_vb;
diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp
index 43716ca..92ff04c 100644
--- a/src/scene/scene.cpp
+++ b/src/scene/scene.cpp
@@ -21,8 +21,8 @@ namespace Donut
scene_camera.set_camera_mode(CameraMode::Orbital);
scene_camera.set_orbital_target(glm::vec3(0.0f));
- scene_camera.set_orbital_radius(15.0);
- scene_camera.set_orbital_limits(2.0, 200.0);
+ scene_camera.set_orbital_radius(30.0);
+ scene_camera.set_orbital_limits(3.0, 400.0);
scene_camera.set_orbital_speed(0.01f);
scene_camera.set_zoom_speed(2.0);
scene_camera.set_azimuth(0.0f);
diff --git a/src/scene/scene.h b/src/scene/scene.h
index 9f633c9..ada1878 100644
--- a/src/scene/scene.h
+++ b/src/scene/scene.h
@@ -13,7 +13,7 @@ namespace Donut
public:
Scene();
- std::vector<SceneObject> objects{ SceneObject{} };
+ std::vector<SceneObject> objects{ SceneObject{ { 18.0f, 6.0f, 0.0f }, 2.0f, { 0.85f, 0.35f, 0.2f } } };
int selected_object = 0;
BlackHoleParams black_hole;
diff --git a/src/scene/scene_types.h b/src/scene/scene_types.h
index d64a46b..1cdd7f2 100644
--- a/src/scene/scene_types.h
+++ b/src/scene/scene_types.h
@@ -4,6 +4,8 @@
namespace Donut
{
+ constexpr float SCENE_UNITS_PER_RS = 3.0f;
+
struct SceneObject
{
glm::vec3 position = { 0.0f, 2.0f, 0.0f };
diff --git a/src/ui/ui_layer.cpp b/src/ui/ui_layer.cpp
index ca83b59..40c395d 100644
--- a/src/ui/ui_layer.cpp
+++ b/src/ui/ui_layer.cpp
@@ -6,7 +6,12 @@
#include "rendering/render_api.h"
#include <imgui.h>
+#include <ImGuizmo.h>
#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+#include <algorithm>
+#include <cstdlib>
+#include <cstdio>
#include <string>
namespace Donut
@@ -58,9 +63,51 @@ namespace Donut
auto view() const -> View override { return View::None; }
auto on_ui(const UIContext& ctx) -> void override
{
+ auto& s = SettingsManager::get_settings();
+
+ if (ImGui::CollapsingHeader("Display", ImGuiTreeNodeFlags_DefaultOpen))
+ {
+ const bool fs = ctx.actions.is_fullscreen();
+ int cw = 0, ch = 0; ctx.actions.get_window_size(cw, ch);
+
+ struct Res { const char* label; int w, h; };
+ static const Res kRes[] = {
+ { "1280 x 720", 1280, 720 }, { "1600 x 900", 1600, 900 },
+ { "1920 x 1080", 1920, 1080 }, { "2560 x 1440", 2560, 1440 },
+ };
+ char cur_label[32]; std::snprintf(cur_label, sizeof(cur_label), "%d x %d", cw, ch);
+ ImGui::BeginDisabled(fs);
+ if (ImGui::BeginCombo("Resolution", cur_label))
+ {
+ for (const auto& r : kRes)
+ if (ImGui::Selectable(r.label, cw == r.w && ch == r.h))
+ ctx.actions.set_resolution(r.w, r.h);
+ ImGui::EndCombo();
+ }
+ ImGui::EndDisabled();
+
+ bool fs_v = fs;
+ if (ImGui::Checkbox("Fullscreen", &fs_v)) ctx.actions.set_fullscreen(fs_v);
+
+ if (ImGui::Checkbox("V-Sync", &s.graphics.v_sync_enabled))
+ {
+ ctx.actions.set_vsync(s.graphics.v_sync_enabled);
+ SettingsManager::save_settings();
+ }
+
+ ImGui::BeginDisabled(s.graphics.v_sync_enabled);
+ if (ImGui::SliderInt("Frame cap", &s.simulation.target_fps, 30, 240, "%d FPS"))
+ SettingsManager::save_settings();
+ ImGui::EndDisabled();
+ if (s.graphics.v_sync_enabled)
+ ImGui::TextDisabled("Frame cap applies only with V-Sync off.");
+
+ if (ImGui::SliderFloat("UI scale", &m_ui_scale, 0.8f, 1.6f, "%.2fx"))
+ ctx.actions.set_ui_scale(m_ui_scale);
+ }
+
if (ImGui::CollapsingHeader("Renderer", ImGuiTreeNodeFlags_DefaultOpen))
{
- auto& s = SettingsManager::get_settings();
const char* apis[] = { "OpenGL", "Vulkan" };
int cur = (s.graphics.render_api == "Vulkan") ? 1 : 0;
if (ImGui::Combo("Graphics API", &cur, apis, 2))
@@ -72,7 +119,15 @@ namespace Donut
if (s.graphics.render_api != running)
ImGui::TextColored(ImVec4(0.98f, 0.62f, 0.20f, 1.0f), "Restart to apply (%s running)", running);
else
- ImGui::TextDisabled("Active backend: %s", running);
+ ImGui::TextDisabled("Active backend: %s (%s)", running, ctx.device_name.c_str());
+ }
+
+ if (ImGui::CollapsingHeader("Overlay", ImGuiTreeNodeFlags_DefaultOpen))
+ {
+ bool dirty = false;
+ dirty |= ImGui::Checkbox("Show FPS", &s.graphics.show_fps);
+ dirty |= ImGui::Checkbox("Show frame time", &s.graphics.show_performance_metrics);
+ if (dirty) SettingsManager::save_settings();
}
if (ImGui::CollapsingHeader("Environment", ImGuiTreeNodeFlags_DefaultOpen))
@@ -93,6 +148,8 @@ namespace Donut
ImGui::TextDisabled("Switching rebuilds the cubemap (brief pause).");
}
}
+ private:
+ float m_ui_scale = 1.0f;
};
class SceneWorkspace : public Workspace
@@ -138,8 +195,32 @@ namespace Donut
ImGui::DragFloat3("Position", &o.position.x, 0.1f);
ImGui::DragFloat("Radius", &o.radius, 0.05f, 0.1f, 20.0f);
ImGui::ColorEdit3("Color", &o.color.x);
+
+ ImGui::Spacing();
+ if (ImGui::RadioButton("Move", m_op == ImGuizmo::TRANSLATE)) m_op = ImGuizmo::TRANSLATE;
+ ImGui::SameLine();
+ if (ImGui::RadioButton("Scale", m_op == ImGuizmo::SCALE)) m_op = ImGuizmo::SCALE;
+ ImGui::SameLine(); ImGui::TextDisabled("(drag the handles in the viewport)");
+
+ ImGuizmo::SetOrthographic(false);
+ ImGuizmo::SetDrawlist(ImGui::GetBackgroundDrawList());
+ ImGuiIO& io = ImGui::GetIO();
+ ImGuizmo::SetRect(0.0f, 0.0f, io.DisplaySize.x, io.DisplaySize.y);
+ glm::mat4 view = sc.scene_camera.get_view_matrix();
+ glm::mat4 proj = sc.scene_camera.get_projection_matrix();
+ glm::mat4 xform = glm::translate(glm::mat4(1.0f), o.position)
+ * glm::scale(glm::mat4(1.0f), glm::vec3(o.radius));
+ if (ImGuizmo::Manipulate(&view[0][0], &proj[0][0], m_op, ImGuizmo::WORLD, &xform[0][0]))
+ {
+ float t[3], r[3], s[3];
+ ImGuizmo::DecomposeMatrixToComponents(&xform[0][0], t, r, s);
+ o.position = glm::vec3(t[0], t[1], t[2]);
+ o.radius = std::max(0.05f, (s[0] + s[1] + s[2]) / 3.0f);
+ }
}
}
+ private:
+ ImGuizmo::OPERATION m_op = ImGuizmo::TRANSLATE;
};
class SimulationWorkspace : public Workspace
@@ -233,6 +314,8 @@ namespace Donut
static bool styled = false;
if (!styled) { apply_donut_style(); styled = true; }
+ ImGuizmo::BeginFrame();
+
ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);
ImGui::SetNextWindowSize(ImVec2(360, 620), ImGuiCond_FirstUseEver);
@@ -240,8 +323,14 @@ namespace Donut
ImGuiIO& io = ImGui::GetIO();
ImGui::TextDisabled("%s", ctx.device_name.empty() ? "GPU" : ctx.device_name.c_str());
- ImGui::Text("%.0f FPS", io.Framerate);
- ImGui::SameLine(); ImGui::TextDisabled("%.1f ms/frame", io.Framerate > 0 ? 1000.0f / io.Framerate : 0.0f);
+ const bool show_fps = SettingsManager::get_show_fps();
+ const bool show_ms = SettingsManager::get_show_performance_metrics();
+ if (show_fps) ImGui::Text("%.0f FPS", io.Framerate);
+ if (show_ms)
+ {
+ if (show_fps) ImGui::SameLine();
+ ImGui::TextDisabled("%.1f ms/frame", io.Framerate > 0 ? 1000.0f / io.Framerate : 0.0f);
+ }
ImGui::Spacing();
if (ImGui::BeginTabBar("workspaces"))
diff --git a/src/ui/workspace.h b/src/ui/workspace.h
index 7806f5c..04cc5db 100644
--- a/src/ui/workspace.h
+++ b/src/ui/workspace.h
@@ -7,10 +7,22 @@ namespace Donut
{
class Scene;
+ struct AppActions
+ {
+ virtual ~AppActions() = default;
+ virtual auto set_resolution(int w, int h) -> void = 0;
+ virtual auto set_fullscreen(bool on) -> void = 0;
+ virtual auto set_vsync(bool on) -> void = 0;
+ virtual auto set_ui_scale(float scale) -> void = 0;
+ virtual auto get_window_size(int& w, int& h) const -> void = 0;
+ virtual auto is_fullscreen() const -> bool = 0;
+ };
+
struct UIContext
{
Scene& scene;
std::string device_name;
+ AppActions& actions;
};
class Workspace