#define GLFW_INCLUDE_NONE #include #include #include #include #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 Auto-terminate after N seconds (for testing/CI)\n"); fmt::print(" --scene 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; } // Wire up resize callback win.on_resize([&ctx](int width, int height) { ctx.set_size(width, height); }); 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(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(now - prev).count(); prev = now; active_scene->update(dt); active_scene->render(win.width(), win.height()); win.swap_buffers(); win.poll_events(); if (take_screenshot) { win.screenshot(); win.stop(); } } return 0; }