added dump mode
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,3 +4,4 @@
|
|||||||
/build*
|
/build*
|
||||||
/cmake-build*
|
/cmake-build*
|
||||||
/out
|
/out
|
||||||
|
/dump
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ FetchContent_MakeAvailable(rmlui)
|
|||||||
# Find other dependencies via vcpkg
|
# Find other dependencies via vcpkg
|
||||||
find_package(glfw3 CONFIG REQUIRED)
|
find_package(glfw3 CONFIG REQUIRED)
|
||||||
find_package(freetype CONFIG REQUIRED)
|
find_package(freetype CONFIG REQUIRED)
|
||||||
|
find_package(PNG REQUIRED)
|
||||||
|
|
||||||
# Get the RmlUi source directory for include paths
|
# Get the RmlUi source directory for include paths
|
||||||
FetchContent_GetProperties(rmlui)
|
FetchContent_GetProperties(rmlui)
|
||||||
@@ -37,7 +38,7 @@ add_executable(mosis-designer
|
|||||||
${rmlui_SOURCE_DIR}/Backends/RmlUi_Renderer_GL3.cpp
|
${rmlui_SOURCE_DIR}/Backends/RmlUi_Renderer_GL3.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(mosis-designer PUBLIC
|
target_link_libraries(mosis-designer PUBLIC
|
||||||
RmlUi::RmlUi RmlUi::Lua glfw freetype
|
RmlUi::RmlUi RmlUi::Lua glfw freetype PNG::PNG
|
||||||
)
|
)
|
||||||
target_include_directories(mosis-designer PUBLIC
|
target_include_directories(mosis-designer PUBLIC
|
||||||
${RMLUI_SOURCE_DIR}
|
${RMLUI_SOURCE_DIR}
|
||||||
|
|||||||
188
main.cpp
188
main.cpp
@@ -3,11 +3,17 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstring>
|
||||||
#include <RmlUi/Core.h>
|
#include <RmlUi/Core.h>
|
||||||
#include <RmlUi/Debugger.h>
|
#include <RmlUi/Debugger.h>
|
||||||
#include <RmlUi/Lua.h>
|
#include <RmlUi/Lua.h>
|
||||||
#include <RmlUi_Backend.h>
|
#include <RmlUi_Backend.h>
|
||||||
#include <RmlUi_Include_Windows.h>
|
#include <RmlUi_Include_Windows.h>
|
||||||
|
#include <RmlUi_Include_GL3.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <png.h>
|
||||||
|
|
||||||
void load_fonts(const std::filesystem::path& dir)
|
void load_fonts(const std::filesystem::path& dir)
|
||||||
{
|
{
|
||||||
@@ -20,6 +26,124 @@ void load_fonts(const std::filesystem::path& dir)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DumpElementTree(Rml::Element* element, std::ofstream& out, int depth = 0)
|
||||||
|
{
|
||||||
|
if (!element) return;
|
||||||
|
|
||||||
|
std::string indent(depth * 2, ' ');
|
||||||
|
std::string id = element->GetId().empty() ? "" : "#" + element->GetId();
|
||||||
|
|
||||||
|
out << indent << element->GetTagName() << id
|
||||||
|
<< " @ (" << element->GetAbsoluteLeft() << ", " << element->GetAbsoluteTop() << ") "
|
||||||
|
<< element->GetOffsetWidth() << "x" << element->GetOffsetHeight() << "\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < element->GetNumChildren(); i++)
|
||||||
|
DumpElementTree(element->GetChild(i), out, depth + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offscreen framebuffer for screenshot capture
|
||||||
|
struct OffscreenFBO {
|
||||||
|
GLuint fbo = 0;
|
||||||
|
GLuint texture = 0;
|
||||||
|
GLuint depth_rbo = 0;
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
|
||||||
|
bool create(int w, int h)
|
||||||
|
{
|
||||||
|
width = w;
|
||||||
|
height = h;
|
||||||
|
|
||||||
|
// Create framebuffer
|
||||||
|
glGenFramebuffers(1, &fbo);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
|
|
||||||
|
// Create color texture
|
||||||
|
glGenTextures(1, &texture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
|
||||||
|
|
||||||
|
// Create depth/stencil renderbuffer
|
||||||
|
glGenRenderbuffers(1, &depth_rbo);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, depth_rbo);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depth_rbo);
|
||||||
|
|
||||||
|
// Check completeness
|
||||||
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||||
|
{
|
||||||
|
std::println("Failed to create offscreen framebuffer");
|
||||||
|
destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy()
|
||||||
|
{
|
||||||
|
if (texture) glDeleteTextures(1, &texture);
|
||||||
|
if (depth_rbo) glDeleteRenderbuffers(1, &depth_rbo);
|
||||||
|
if (fbo) glDeleteFramebuffers(1, &fbo);
|
||||||
|
fbo = texture = depth_rbo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind()
|
||||||
|
{
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> readPixels()
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> pixels(width * height * 3);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
|
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels.data());
|
||||||
|
return pixels;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void SavePNG(const std::filesystem::path& path, const std::vector<unsigned char>& pixels, int width, int height)
|
||||||
|
{
|
||||||
|
// Flip vertically (OpenGL origin is bottom-left)
|
||||||
|
std::vector<unsigned char> flipped(width * height * 3);
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
std::memcpy(&flipped[y * width * 3],
|
||||||
|
&pixels[(height - 1 - y) * width * 3],
|
||||||
|
width * 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* fp = fopen(path.string().c_str(), "wb");
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
std::println("Failed to open {} for writing", path.string());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||||
|
png_infop info = png_create_info_struct(png);
|
||||||
|
png_init_io(png, fp);
|
||||||
|
png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB,
|
||||||
|
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||||
|
png_write_info(png, info);
|
||||||
|
|
||||||
|
std::vector<png_bytep> rows(height);
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
rows[y] = const_cast<png_bytep>(&flipped[y * width * 3]);
|
||||||
|
png_write_image(png, rows.data());
|
||||||
|
png_write_end(png, nullptr);
|
||||||
|
|
||||||
|
png_destroy_write_struct(&png, &info);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
std::println("Screenshot saved to: {}", path.string());
|
||||||
|
}
|
||||||
|
|
||||||
int main(const int argc, const char* argv[])
|
int main(const int argc, const char* argv[])
|
||||||
{
|
{
|
||||||
constexpr int window_width = 540;
|
constexpr int window_width = 540;
|
||||||
@@ -27,11 +151,12 @@ int main(const int argc, const char* argv[])
|
|||||||
|
|
||||||
if (argc < 2)
|
if (argc < 2)
|
||||||
{
|
{
|
||||||
std::println("Usage: mosis-designer file.rml");
|
std::println("Usage: mosis-designer file.rml [--dump]");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::filesystem::path file = argv[1];
|
const std::filesystem::path file = argv[1];
|
||||||
|
const bool dump_mode = (argc >= 3 && std::string(argv[2]) == "--dump");
|
||||||
if (!std::filesystem::exists(file))
|
if (!std::filesystem::exists(file))
|
||||||
{
|
{
|
||||||
std::println("File does not exist");
|
std::println("File does not exist");
|
||||||
@@ -65,6 +190,67 @@ int main(const int argc, const char* argv[])
|
|||||||
document->Show();
|
document->Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dump mode: render and capture screenshot
|
||||||
|
if (dump_mode)
|
||||||
|
{
|
||||||
|
// Create offscreen FBO at window dimensions
|
||||||
|
OffscreenFBO fbo;
|
||||||
|
if (!fbo.create(window_width, window_height))
|
||||||
|
{
|
||||||
|
std::println("Failed to create FBO");
|
||||||
|
Rml::Shutdown();
|
||||||
|
Backend::Shutdown();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First render to default framebuffer to initialize everything
|
||||||
|
Backend::ProcessEvents(context);
|
||||||
|
context->Update();
|
||||||
|
Backend::BeginFrame();
|
||||||
|
context->Render();
|
||||||
|
Backend::PresentFrame();
|
||||||
|
|
||||||
|
// Create dump folder if it doesn't exist
|
||||||
|
auto dump_folder = std::filesystem::absolute("dump");
|
||||||
|
std::filesystem::create_directories(dump_folder);
|
||||||
|
|
||||||
|
// Write element dump
|
||||||
|
std::ofstream dump_file((dump_folder / "element_dump.txt").string());
|
||||||
|
if (dump_file)
|
||||||
|
{
|
||||||
|
dump_file << "Window: " << window_width << "x" << window_height << "\n";
|
||||||
|
dump_file << "\n=== Element Layout ===\n";
|
||||||
|
DumpElementTree(document, dump_file);
|
||||||
|
dump_file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind FBO and set up OpenGL state for rendering
|
||||||
|
fbo.bind();
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||||
|
|
||||||
|
// Enable blending for proper text rendering
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
// Render to FBO
|
||||||
|
context->Update();
|
||||||
|
context->Render();
|
||||||
|
|
||||||
|
// Read pixels
|
||||||
|
auto pixels = fbo.readPixels();
|
||||||
|
fbo.destroy();
|
||||||
|
|
||||||
|
SavePNG(dump_folder / "screenshot.png", pixels, window_width, window_height);
|
||||||
|
|
||||||
|
// Restore default framebuffer
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
Rml::Shutdown();
|
||||||
|
Backend::Shutdown();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
const HANDLE hNotif = FindFirstChangeNotification(assets_path.c_str(),
|
const HANDLE hNotif = FindFirstChangeNotification(assets_path.c_str(),
|
||||||
TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE);
|
TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"dependencies": [
|
"dependencies": [
|
||||||
"glfw3",
|
"glfw3",
|
||||||
"freetype",
|
"freetype",
|
||||||
"lua"
|
"lua",
|
||||||
|
"libpng"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user