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 | 01fadbecbed89afffb18b2cf5cf3972845a7a169 (patch) | |
| tree | 1faf64e41f4a9b59e8ad6f68363c590dfc089cc4 /src/Engine | |
| parent | 6f72049359651155be62846e7d080d7a5e871096 (diff) | |
[add]: frame exporting
Diffstat (limited to 'src/Engine')
| -rw-r--r-- | src/Engine/Engine.cpp | 154 | ||||
| -rw-r--r-- | src/Engine/Engine.h | 1 |
2 files changed, 152 insertions, 3 deletions
diff --git a/src/Engine/Engine.cpp b/src/Engine/Engine.cpp index fa010a7..245f6fd 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" @@ -326,7 +330,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_PosRadius.x, obj.m_PosRadius.y, obj.m_PosRadius.z, obj.m_PosRadius.w, @@ -334,12 +338,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.GetOrbitalPosition().x, m_Camera.GetOrbitalPosition().y, m_Camera.GetOrbitalPosition().z ); - DONUT_INFO("Camera radius: {:.2e}", m_Camera.GetOrbitalRadius()); + DONUT_INFO("Camera radius: {}", m_Camera.GetOrbitalRadius()); DONUT_INFO("========================"); } + + void Engine::ExportHighResFrame(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 originalWidth = m_Width; + int originalHeight = m_Height; + int originalComputeHeight = m_ComputeHeight; + + m_Width = width; + m_Height = height; + m_ComputeHeight = height; + + int computeHeight = height; + int computeWidth = (width * computeHeight) / height; + + if (computeWidth <= 0 || computeHeight <= 0) + { + DONUT_ERROR("Invalid compute dimensions: {}x{}", computeWidth, computeHeight); + return; + } + + FramebufferSpecification fbSpec; + fbSpec.Width = width; + fbSpec.Height = height; + fbSpec.Attachments = { FramebufferTextureFormat::RGBA8 }; + + auto highResFramebuffer = Framebuffer::Create(fbSpec); + if (!highResFramebuffer) + { + DONUT_ERROR("Failed to create high-resolution framebuffer"); + return; + } + + highResFramebuffer->Bind(); + + RenderCommand::SetViewport(0, 0, width, height); + RenderCommand::Clear(); + + auto highResTexture = Texture2D::Create(computeWidth, computeHeight); + if (!highResTexture) + { + DONUT_ERROR("Failed to create high-resolution texture"); + return; + } + + highResTexture->SetData(nullptr, computeWidth * computeHeight * 4); + m_ComputeProgram->Bind(); + + struct UBOData + { + glm::vec3 pos; float _pad0; + glm::vec3 right; float _pad1; + glm::vec3 up; float _pad2; + glm::vec3 forward; float _pad3; + float tanHalfFov; + float aspect; + bool moving; + int _pad4; + } data; + + glm::vec3 fwd = glm::normalize(m_Camera.GetOrbitalTarget() - m_Camera.GetOrbitalPosition()); + 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.GetOrbitalPosition(); + data.right = right; + data.up = up; + data.forward = fwd; + data.tanHalfFov = static_cast<float>(tan(glm::radians(60.0f * 0.5f))); + data.aspect = static_cast<float>(computeWidth) / static_cast<float>(computeHeight); + data.moving = m_Camera.IsDragging() || m_Camera.IsPanning(); + + m_CameraUBO->SetData(&data, sizeof(UBOData)); + m_CameraUBO->Bind(1); + + UploadDiskUBO(); + UploadObjectsUBO(m_Objects); + UploadSimulationUBO(); + highResTexture->BindAsImage(0, false); + + uint32_t groupsX = static_cast<uint32_t>(std::ceil(computeWidth / 16.0f)); + uint32_t groupsY = static_cast<uint32_t>(std::ceil(computeHeight / 16.0f)); + m_ComputeProgram->Dispatch(groupsX, groupsY, 1); + m_ComputeProgram->MemoryBarrier(IMAGE_ACCESS_BARRIER_BIT); + + m_ShaderProgram->Bind(); + m_QuadVAO->Bind(); + + highResTexture->Bind(0); + m_ShaderProgram->SetInt("u_ScreenTexture", 0); + + RenderCommand::DisableDepthTest(); + RenderCommand::DrawArrays(6); + RenderCommand::EnableDepthTest(); + + std::vector<unsigned char> pixels(width * height * 4); + DONUT_INFO("Reading {} pixels from framebuffer...", width * height); + RenderCommand::ReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); + + DONUT_INFO("Flipping image vertically..."); + std::vector<unsigned char> flippedPixels(width * height * 4); + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + int srcIndex = (y * width + x) * 4; + int dstIndex = ((height - 1 - y) * width + x) * 4; + flippedPixels[dstIndex + 0] = pixels[srcIndex + 0]; // R + flippedPixels[dstIndex + 1] = pixels[srcIndex + 1]; // G + flippedPixels[dstIndex + 2] = pixels[srcIndex + 2]; // B + flippedPixels[dstIndex + 3] = pixels[srcIndex + 3]; // A + } + } + + DONUT_INFO("Saving PNG file: {}...", filename); + int result = stbi_write_png(filename.c_str(), width, height, 4, flippedPixels.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); + + highResFramebuffer->Unbind(); + + m_Width = originalWidth; + m_Height = originalHeight; + m_ComputeHeight = originalComputeHeight; + + RenderCommand::SetViewport(0, 0, originalWidth, originalHeight); + } } diff --git a/src/Engine/Engine.h b/src/Engine/Engine.h index f3724ff..328435a 100644 --- a/src/Engine/Engine.h +++ b/src/Engine/Engine.h @@ -101,6 +101,7 @@ namespace Donut void SetEarlyExitDistance(float distance) { m_EarlyExitDistance = distance; } void LoadObjectsFromScene(const std::vector<Donut::Object>& objects); + void ExportHighResFrame(const std::string& filename, int width = 4096, int height = 3072); void PrintObjectInfo() const; private: Ref<Shader> CreateComputeProgram(const char* path); |
