hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/src/rendering/render_path.cpp
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-08-24 03:57:40 +0200
committerhachem <im@hachem.wtf>2026-08-24 03:57:40 +0200
commitf71276fec04c317d07bfe768ebfbe1b28ddd60e7 (patch)
treea188e6cc7ac46f993438fb900655ccbfeb805de8 /src/rendering/render_path.cpp
parent9968e5a8682aabe5711842a34d3ee6f6716fc2b0 (diff)
feat: work on render path exporting
Diffstat (limited to 'src/rendering/render_path.cpp')
-rw-r--r--src/rendering/render_path.cpp90
1 files changed, 90 insertions, 0 deletions
diff --git a/src/rendering/render_path.cpp b/src/rendering/render_path.cpp
index 0963d2a..e3ca366 100644
--- a/src/rendering/render_path.cpp
+++ b/src/rendering/render_path.cpp
@@ -1,10 +1,18 @@
#include "render_path.h"
#include "scene/scene.h"
+#include "core/log.h"
+
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include "stb_image_write.h"
#include <glm/gtc/matrix_transform.hpp>
#include <algorithm>
#include <cmath>
+#include <filesystem>
+#include <ctime>
+#include <cstdio>
+#include <vector>
namespace Donut
{
@@ -84,4 +92,86 @@ namespace Donut
m_black_hole_renderer.reset();
m_cubemap.reset();
}
+
+ static auto write_pfm(const std::string& path, int w, int h, const std::vector<float>& rgba) -> bool
+ {
+ std::FILE* f = std::fopen(path.c_str(), "wb");
+ if (!f) return false;
+ std::fprintf(f, "PF\n%d %d\n-1.0\n", w, h);
+ std::vector<float> rgb((size_t)w * 3);
+ for (int y = h - 1; y >= 0; --y)
+ {
+ const float* src = &rgba[(size_t)y * w * 4];
+ for (int x = 0; x < w; ++x) { rgb[x*3+0] = src[x*4+0]; rgb[x*3+1] = src[x*4+1]; rgb[x*3+2] = src[x*4+2]; }
+ std::fwrite(rgb.data(), sizeof(float), (size_t)w * 3, f);
+ }
+ std::fclose(f);
+ return true;
+ }
+
+ static auto write_csv(const std::string& path, int w, int h, const std::vector<float>& rgba) -> bool
+ {
+ std::FILE* f = std::fopen(path.c_str(), "wb");
+ if (!f) return false;
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x) std::fprintf(f, x ? ",%g" : "%g", rgba[((size_t)y * w + x) * 4]);
+ std::fputc('\n', f);
+ }
+ std::fclose(f);
+ return true;
+ }
+
+ auto RenderPath::export_frame(Scene& scene, const ExportConfig& cfg) -> int
+ {
+ if (!m_device) return 0;
+ const int w = std::max(cfg.width, 1), h = std::max(cfg.height, 1);
+ const bool raw = cfg.format != ExportFormat::Png;
+ auto target = m_device->create_render_target(
+ w, h, raw ? RHI::Format::RGBA32F : RHI::Format::RGBA8, RHI::Format::None, RHI::Filter::Nearest);
+
+ GeodesicView gv;
+ glm::vec3 pos, fwd;
+ if (scene.sim_camera.get_camera_mode() == CameraMode::FPS)
+ { pos = scene.sim_camera.get_position(); fwd = scene.sim_camera.get_forward_direction(); }
+ else
+ { pos = scene.sim_camera.get_orbital_position(); fwd = glm::normalize(scene.sim_camera.get_orbital_target() - pos); }
+ glm::vec3 right = glm::normalize(glm::cross(fwd, glm::vec3(0, 1, 0)));
+ gv.position = pos; gv.right = right; gv.up = glm::cross(right, fwd); gv.forward = fwd;
+ gv.tan_half_fov = (float)tan(glm::radians(scene.black_hole.fov_degrees * 0.5f));
+ gv.aspect = (float)w / (float)h;
+ gv.moving = false;
+ gv.time = 0.0f;
+
+ std::error_code ec; std::filesystem::create_directories(cfg.directory, ec);
+ char stamp[32]; std::time_t t = std::time(nullptr);
+ std::strftime(stamp, sizeof(stamp), "%Y%m%d_%H%M%S", std::localtime(&t));
+ const char* ext = cfg.format == ExportFormat::Pfm ? "pfm" : cfg.format == ExportFormat::Csv ? "csv" : "png";
+
+ int written = 0;
+ std::vector<uint8_t> px8; std::vector<float> pxf;
+ auto do_channel = [&](bool enabled, int channel, const char* name)
+ {
+ if (!enabled) return;
+ m_device->run_offscreen([&](RHI::CommandList& cmd) {
+ m_black_hole_renderer->render_export(cmd, target.get(), gv, scene.black_hole,
+ scene.objects, m_cubemap.get(), channel, raw);
+ });
+ std::string path = cfg.directory + "/donut_" + name + "_" + stamp + "." + ext;
+ bool ok;
+ if (cfg.format == ExportFormat::Png)
+ { m_device->read_render_target(target.get(), px8); ok = stbi_write_png(path.c_str(), w, h, 4, px8.data(), w * 4) != 0; }
+ else
+ {
+ m_device->read_render_target_float(target.get(), pxf);
+ ok = (cfg.format == ExportFormat::Pfm) ? write_pfm(path, w, h, pxf) : write_csv(path, w, h, pxf);
+ }
+ if (ok) { DONUT_INFO("Exported {}", path); ++written; } else DONUT_ERROR("Export failed: {}", path);
+ };
+ do_channel(cfg.color, 0, "color");
+ do_channel(cfg.redshift, 1, "redshift");
+ do_channel(cfg.temperature, 2, "temperature");
+ do_channel(cfg.impact, 3, "impact");
+ return written;
+ }
}