From 59b7c407550d63e997ed1e7bed286be5c332c285 Mon Sep 17 00:00:00 2001 From: hachem Date: Mon, 24 Aug 2026 03:57:40 +0200 Subject: [feat]: work on render path exporting --- src/ui/ui_layer.cpp | 39 +++++++++++++++++++++++++++++++-------- src/ui/workspace.h | 4 ++++ 2 files changed, 35 insertions(+), 8 deletions(-) (limited to 'src/ui') diff --git a/src/ui/ui_layer.cpp b/src/ui/ui_layer.cpp index 4aa470b..f1ec4d2 100644 --- a/src/ui/ui_layer.cpp +++ b/src/ui/ui_layer.cpp @@ -280,31 +280,54 @@ namespace Donut public: auto name() const -> const char* override { return "Export"; } auto view() const -> View override { return View::None; } - auto on_ui(const UIContext&) -> void override + auto on_ui(const UIContext& ctx) -> void override { - ImGui::TextDisabled("Render observables to disk for analysis."); + ImGui::TextDisabled("Render the black hole + observables to disk for analysis."); ImGui::Spacing(); if (ImGui::CollapsingHeader("Channels", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::Checkbox("Colour (tonemapped)", &m_color); + ImGui::Checkbox("Colour (tone-mapped)", &m_color); ImGui::Checkbox("Redshift (g)", &m_redshift); ImGui::Checkbox("Emission temperature", &m_temperature); ImGui::Checkbox("Impact parameter", &m_impact); + ImGui::TextDisabled("Redshift & temperature are disk-only; impact is whole-frame (false-colour)."); } if (ImGui::CollapsingHeader("Output", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::Combo("Resolution", &m_resolution, "960 x 540\0" "1920 x 1080\0" "3840 x 2160\0"); - ImGui::Combo("Format", &m_format, "PNG\0" "EXR (HDR)\0" "CSV (data)\0"); + ImGui::Combo("Resolution", &m_resolution, "1280 x 720\0" "1920 x 1080\0" "2560 x 1440\0" "3840 x 2160\0"); + ImGui::Combo("Format", &m_format, "Display image (PNG)\0" "Raw float image (PFM)\0" "Raw values (CSV)\0"); + if (m_format == 0) ImGui::TextDisabled("Tone-mapped colour / false-coloured observables."); + else ImGui::TextDisabled("Actual physical values: g, temperature (K), impact (r_s)."); } + ImGui::Spacing(); - ImGui::BeginDisabled(); - ImGui::Button("Export frame", ImVec2(-1.0f, 0.0f)); + const bool any = m_color || m_redshift || m_temperature || m_impact; + ImGui::BeginDisabled(!any); + if (ImGui::Button("Export frame", ImVec2(-1.0f, 0.0f))) + { + static const int dims[][2] = { {1280,720}, {1920,1080}, {2560,1440}, {3840,2160} }; + ExportConfig cfg; + cfg.color = m_color; cfg.redshift = m_redshift; + cfg.temperature = m_temperature; cfg.impact = m_impact; + cfg.width = dims[m_resolution][0]; cfg.height = dims[m_resolution][1]; + cfg.format = static_cast(m_format); + m_written = ctx.actions.export_frame(cfg); + m_reported = true; + } ImGui::EndDisabled(); - ImGui::TextDisabled("Not wired up yet: needs the RHI readback path + G-buffer shader outputs."); + + if (m_reported) + { + if (m_written > 0) + ImGui::TextColored(ImVec4(0.55f, 0.85f, 0.45f, 1.0f), "Wrote %d PNG(s) to exports/", m_written); + else + ImGui::TextColored(ImVec4(0.95f, 0.45f, 0.35f, 1.0f), "Export failed (see log)"); + } } private: bool m_color = true, m_redshift = false, m_temperature = false, m_impact = false; int m_resolution = 1, m_format = 0; + int m_written = 0; bool m_reported = false; }; } diff --git a/src/ui/workspace.h b/src/ui/workspace.h index 804d5a8..d86614a 100644 --- a/src/ui/workspace.h +++ b/src/ui/workspace.h @@ -1,6 +1,7 @@ #pragma once #include "rendering/view.h" +#include "rendering/export_config.h" #include namespace Donut @@ -18,6 +19,9 @@ namespace Donut 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; + // Renders the requested channels off-screen and writes PNGs; returns the + // number of files written (0 on failure). + virtual auto export_frame(const ExportConfig& cfg) -> int = 0; }; // What a workspace gets each frame to build its panels. `scene` is mutable — -- cgit v1.3