diff options
| author | hachem <im@hachem.wtf> | 2025-08-22 00:22:12 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2025-08-22 00:22:12 +0200 |
| commit | 0d4f5f665f2e4b735fb4f978acc6c8d4a16fcb5d (patch) | |
| tree | dad79333f7026e19732fae0e35ca9dff8ea7ade4 /src/engine/engine.cpp | |
| parent | d8ba57f6d45b9467520377ffcc1f453154d77430 (diff) | |
feat: frame exporting
Diffstat (limited to 'src/engine/engine.cpp')
| -rw-r--r-- | src/engine/engine.cpp | 154 |
1 files changed, 151 insertions, 3 deletions
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index ef9b93d..3b5e320 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -3,6 +3,10 @@ #include <sstream> #include <GLFW/glfw3.h> +#include <glad/glad.h> + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" #include "engine.h" #include "core/log.h" @@ -325,7 +329,7 @@ namespace Donut for (size_t i = 0; i < m_objects.size(); ++i) { const auto& obj = m_objects[i]; - DONUT_INFO("Object {}: Pos=({:.2e}, {:.2e}, {:.2e}), Radius={:.2e}, Mass={:.2e}, Color=({:.2f}, {:.2f}, {:.2f})", + DONUT_INFO("Object {}: Pos=({}, {}, {}), Radius={}, Mass={}, Color=({}, {}, {})", i, obj.m_pos_radius.x, obj.m_pos_radius.y, obj.m_pos_radius.z, obj.m_pos_radius.w, @@ -333,12 +337,156 @@ namespace Donut obj.m_color.x, obj.m_color.y, obj.m_color.z ); } - DONUT_INFO("Camera position: ({:.2e}, {:.2e}, {:.2e})", + DONUT_INFO("Camera position: ({}, {}, {})", m_camera.get_orbital_position().x, m_camera.get_orbital_position().y, m_camera.get_orbital_position().z ); - DONUT_INFO("Camera radius: {:.2e}", m_camera.get_orbital_radius()); + DONUT_INFO("Camera radius: {}", m_camera.get_orbital_radius()); DONUT_INFO("========================"); } + + void Engine::export_high_res_frame(const std::string& filename, int width, int height) + { + DONUT_INFO("Exporting high-resolution frame: {}x{} to {}", width, height, filename); + + if (width <= 0 || height <= 0) + { + DONUT_ERROR("Invalid dimensions for export: {}x{}", width, height); + return; + } + + if (filename.empty()) + { + DONUT_ERROR("Invalid filename for export"); + return; + } + + int original_width = m_width; + int original_height = m_height; + int original_compute_height = m_compute_height; + + m_width = width; + m_height = height; + m_compute_height = height; + + int compute_height = height; + int compute_width = (width * compute_height) / height; + + if (compute_width <= 0 || compute_height <= 0) + { + DONUT_ERROR("Invalid compute dimensions: {}x{}", compute_width, compute_height); + return; + } + + FramebufferSpecification fb_spec; + fb_spec.Width = width; + fb_spec.Height = height; + fb_spec.attachments = { FramebufferTextureFormat::RGBA8 }; + + auto high_res_framebuffer = Framebuffer::create(fb_spec); + if (!high_res_framebuffer) + { + DONUT_ERROR("Failed to create high-resolution framebuffer"); + return; + } + + high_res_framebuffer->bind(); + + RenderCommand::set_viewport(0, 0, width, height); + RenderCommand::clear(); + + auto high_res_texture = Texture2D::create(compute_width, compute_height); + if (!high_res_texture) + { + DONUT_ERROR("Failed to create high-resolution texture"); + return; + } + + high_res_texture->set_data(nullptr, compute_width * compute_height * 4); + m_compute_program->bind(); + + struct UBOData + { + glm::vec3 pos; float _pad0; + glm::vec3 right; float _pad1; + glm::vec3 up; float _pad2; + glm::vec3 forward; float _pad3; + float tan_half_fov; + float aspect; + bool moving; + int _pad4; + } data; + + glm::vec3 fwd = glm::normalize(m_camera.get_orbital_target() - m_camera.get_orbital_position()); + glm::vec3 up = glm::vec3(0, 1, 0); + glm::vec3 right = glm::normalize(glm::cross(fwd, up)); + up = glm::cross(right, fwd); + + data.pos = m_camera.get_orbital_position(); + data.right = right; + data.up = up; + data.forward = fwd; + data.tan_half_fov = static_cast<float>(tan(glm::radians(60.0f * 0.5f))); + data.aspect = static_cast<float>(compute_width) / static_cast<float>(compute_height); + data.moving = m_camera.is_dragging() || m_camera.is_panning(); + + m_camera_ubo->set_data(&data, sizeof(UBOData)); + m_camera_ubo->bind(1); + + upload_disk_ubo(); + upload_objects_ubo(m_objects); + upload_simulation_ubo(); + high_res_texture->bind_as_image(0, false); + + uint32_t groups_x = static_cast<uint32_t>(std::ceil(compute_width / 16.0f)); + uint32_t groups_y = static_cast<uint32_t>(std::ceil(compute_height / 16.0f)); + m_compute_program->dispatch(groups_x, groups_y, 1); + m_compute_program->memory_barrier(IMAGE_ACCESS_BARRIER_BIT); + + m_shader_program->bind(); + m_quad_vao->bind(); + + high_res_texture->bind(0); + m_shader_program->set_int("u_ScreenTexture", 0); + + RenderCommand::disable_depth_test(); + RenderCommand::draw_arrays(6); + RenderCommand::enable_depth_test(); + + std::vector<unsigned char> pixels(width * height * 4); + DONUT_INFO("Reading {} pixels from framebuffer...", width * height); + RenderCommand::read_pixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); + + DONUT_INFO("Flipping image vertically..."); + std::vector<unsigned char> flipped_pixels(width * height * 4); + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + int src_index = (y * width + x) * 4; + int dst_index = ((height - 1 - y) * width + x) * 4; + flipped_pixels[dst_index + 0] = pixels[src_index + 0]; + flipped_pixels[dst_index + 1] = pixels[src_index + 1]; + flipped_pixels[dst_index + 2] = pixels[src_index + 2]; + flipped_pixels[dst_index + 3] = pixels[src_index + 3]; + } + } + + DONUT_INFO("Saving PNG file: {}...", filename); + int result = stbi_write_png(filename.c_str(), width, height, 4, flipped_pixels.data(), width * 4); + + if (result) + DONUT_INFO("Successfully exported high-resolution frame to: {}", filename); + else + DONUT_ERROR("Failed to export high-resolution frame to: {}", filename); + + high_res_framebuffer->unbind(); + + m_width = original_width; + m_height = original_height; + m_compute_height = original_compute_height; + + RenderCommand::set_viewport(0, 0, original_width, original_height); + } } |
