diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 0d04c645..81bc5dda 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -155,6 +155,7 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/OpenGL/OpenGLRenderTargetView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/OpenGL/OpenGLDepthStencilView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/OpenGL/OpenGLScreenshot.cpp + ${CMAKE_SOURCE_DIR}/tests/OpenGL/package/src/glad.c # RHI Factory ${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/RHIFactory.cpp @@ -254,7 +255,7 @@ target_include_directories(XCEngine PUBLIC ) if(MSVC) - target_compile_options(XCEngine PRIVATE /W3) + target_compile_options(XCEngine PRIVATE /W3 /FS) else() target_compile_options(XCEngine PRIVATE -Wall) endif() diff --git a/engine/include/XCEngine/RHI/OpenGL/OpenGLScreenshot.h b/engine/include/XCEngine/RHI/OpenGL/OpenGLScreenshot.h new file mode 100644 index 00000000..f120ced3 --- /dev/null +++ b/engine/include/XCEngine/RHI/OpenGL/OpenGLScreenshot.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +namespace XCEngine { +namespace RHI { + +class OpenGLSwapChain; +class OpenGLDevice; + +class OpenGLScreenshot { +public: + static bool Capture(OpenGLDevice& device, + OpenGLSwapChain& swapChain, + const char* filename); + + static bool Capture(OpenGLDevice& device, + OpenGLSwapChain& swapChain, + const char* filename, + int width, + int height); + +private: + static bool SavePPM(const char* filename, + int width, int height); +}; + +} // namespace RHI +} // namespace XCEngine \ No newline at end of file diff --git a/engine/src/RHI/OpenGL/OpenGLScreenshot.cpp b/engine/src/RHI/OpenGL/OpenGLScreenshot.cpp new file mode 100644 index 00000000..cbd8eb0a --- /dev/null +++ b/engine/src/RHI/OpenGL/OpenGLScreenshot.cpp @@ -0,0 +1,71 @@ +#include "XCEngine/RHI/OpenGL/OpenGLScreenshot.h" +#include "XCEngine/RHI/OpenGL/OpenGLDevice.h" +#include "XCEngine/RHI/OpenGL/OpenGLSwapChain.h" +#include "XCEngine/Debug/Logger.h" +#include "XCEngine/Debug/ConsoleLogSink.h" +#include "XCEngine/Containers/String.h" + +#include + +#include +#include +#include + +using namespace XCEngine::Debug; + +namespace XCEngine { +namespace RHI { +namespace { + +bool SavePPM(const char* filename, int width, int height) { + unsigned char* pixels = (unsigned char*)malloc(width * height * 3); + if (!pixels) { + Logger::Get().Error(LogCategory::Rendering, "[OpenGLScreenshot] Failed to allocate pixel buffer"); + return false; + } + + glFinish(); + glReadBuffer(GL_BACK); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels); + + FILE* f = fopen(filename, "wb"); + if (!f) { + Logger::Get().Error(LogCategory::Rendering, "[OpenGLScreenshot] Failed to open file"); + free(pixels); + return false; + } + + fprintf(f, "P6\n%d %d\n255\n", width, height); + + unsigned char* row = (unsigned char*)malloc(width * 3); + for (int y = height - 1; y >= 0; y--) { + memcpy(row, pixels + y * width * 3, width * 3); + fwrite(row, 1, width * 3, f); + } + + free(row); + free(pixels); + fclose(f); + + Logger::Get().Debug(LogCategory::Rendering, "[OpenGLScreenshot] Screenshot saved"); + return true; +} + +} // anonymous namespace + +bool OpenGLScreenshot::Capture(OpenGLDevice& device, OpenGLSwapChain& swapChain, const char* filename) { + return Capture(device, swapChain, filename, swapChain.GetWidth(), swapChain.GetHeight()); +} + +bool OpenGLScreenshot::Capture(OpenGLDevice& device, OpenGLSwapChain& swapChain, const char* filename, int width, int height) { + (void)device; + (void)swapChain; + return SavePPM(filename, width, height); +} + +} // namespace RHI +} // namespace XCEngine \ No newline at end of file