Files
cuber/cuber.cpp
T
portersky 6f696d377b feat: add --scene and --screenshot CLI flags
Added --scene <cube|sphere> to select the initial scene from
the command line (useful for headless/CI testing where key
presses aren't possible).

Added --screenshot to render a single frame, save a PNG,
and exit immediately. Combined with --duration or used
alone, this allows fully automated screenshot capture
without relying on interactive key presses.

Updated help text to reflect new options.
2026-05-05 23:59:54 +02:00

134 lines
3.6 KiB
C++

#define GLFW_INCLUDE_NONE
#include <csignal>
#include <chrono>
#include <string>
#include <string_view>
#include "GLFW/glfw3.h"
#include "asio.hpp"
#include "asio/steady_timer.hpp"
#include "fmt/std.h"
#include "cbt/window.hpp"
#include "cbt/opengl/context.hpp"
#include "scenes/cube.hpp"
#include "scenes/sphere.hpp"
auto main(int argc, char const* argv[]) -> int {
float max_duration_seconds = 0.0f;
std::string_view scene_name = "cube";
bool take_screenshot = false;
for (int i = 1; i < argc; ++i) {
std::string_view arg = argv[i];
if (arg == "--help" || arg == "-h") {
fmt::print("Usage: {} [options]\n", argv[0]);
fmt::print(" --duration <seconds> Auto-terminate after N seconds (for testing/CI)\n");
fmt::print(" --scene <cube|sphere> Select initial scene (default: cube)\n");
fmt::print(" --screenshot Render one frame, save screenshot, and exit\n");
fmt::print(" S key Take screenshot (saved as screenshot.png)\n");
fmt::print(" 1/2 key Switch between cube/sphere scene\n");
return 0;
}
if (arg == "--duration" && i + 1 < argc) {
max_duration_seconds = std::stof(std::string(argv[++i]));
continue;
}
if (arg == "--scene" && i + 1 < argc) {
scene_name = argv[++i];
continue;
}
if (arg == "--screenshot") {
take_screenshot = true;
continue;
}
}
auto win = cbt::window("cuber", 1280, 720);
if (!win.valid()) {
return 1;
}
auto ctx = cbt::opengl::context(win);
if (!ctx.valid()) {
return 1;
}
auto cube_scn = cbt::scenes::cube();
auto sphere_scn = cbt::scenes::sphere();
cbt::scene* active_scene = nullptr;
if (!cube_scn.init() || !sphere_scn.init()) {
return 1;
}
if (scene_name == "sphere" || scene_name == "2") {
active_scene = &sphere_scn;
} else {
active_scene = &cube_scn;
}
// signal handling + optional duration timer (via ASIO)
asio::io_context io;
asio::signal_set signals(io, SIGINT, SIGTERM);
signals.async_wait([&](auto, auto) {
win.stop();
io.stop();
});
asio::steady_timer duration_timer(io);
if (max_duration_seconds > 0.0f) {
duration_timer.expires_after(std::chrono::milliseconds(
static_cast<long long>(max_duration_seconds * 1000.0f)));
duration_timer.async_wait([&win](auto ec) {
if (!ec) {
win.stop();
}
});
}
auto process_signals = [&]() -> void {
while (io.poll()) {}
};
// render loop
auto prev = std::chrono::steady_clock::now();
while (!win.should_close()) {
process_signals();
if (glfwGetKey(win.raw(), GLFW_KEY_Q) == GLFW_PRESS) {
win.stop();
}
if (glfwGetKey(win.raw(), GLFW_KEY_S) == GLFW_PRESS) {
win.screenshot();
}
if (glfwGetKey(win.raw(), GLFW_KEY_1) == GLFW_PRESS) {
active_scene = &cube_scn;
}
if (glfwGetKey(win.raw(), GLFW_KEY_2) == GLFW_PRESS) {
active_scene = &sphere_scn;
}
auto now = std::chrono::steady_clock::now();
auto dt = std::chrono::duration<float>(now - prev).count();
prev = now;
active_scene->update(dt);
active_scene->render();
win.swap_buffers();
win.poll_events();
if (take_screenshot) {
win.screenshot();
win.stop();
}
}
return 0;
}