From 47f8b8598d1562af3cf0bd2cbbab7656367f3e51 Mon Sep 17 00:00:00 2001 From: caiowakamatsu Date: Thu, 16 Apr 2026 23:27:01 -0300 Subject: [PATCH] initial commit --- .gitignore | 2 + CMakeLists.txt | 6 ++ external/CMakeLists.txt | 66 ++++++++++++++++++++++ source/CMakeLists.txt | 18 ++++++ source/cli/args.cpp | 103 +++++++++++++++++++++++++++++++++++ source/cli/args.hpp | 26 +++++++++ source/main.cxx | 29 ++++++++++ source/util/logger.hpp | 118 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 368 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 external/CMakeLists.txt create mode 100644 source/CMakeLists.txt create mode 100644 source/cli/args.cpp create mode 100644 source/cli/args.hpp create mode 100644 source/main.cxx create mode 100644 source/util/logger.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9785597 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +.cache diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..dfbca9d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 4.2.0) +set(CMAKE_CXX_STANDARD 23) +project(rogue LANGUAGES CXX) + +add_subdirectory(external) +add_subdirectory(source) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt new file mode 100644 index 0000000..370c986 --- /dev/null +++ b/external/CMakeLists.txt @@ -0,0 +1,66 @@ +include(FetchContent) + +set(VUK_USE_SHADERC OFF CACHE BOOL "NO SHADERC" FORCE) +set(VUK_EXTRA_IMGUI OFF CACHE BOOL "WAYLAND GLFW" FORCE) +FetchContent_Declare( + vuk_fetch + GIT_REPOSITORY https://github.com/martty/vuk.git + GIT_TAG 06fac4b +) + +#set(GLFW_BUILD_WAYLAND OFF CACHE BOOL "WAYLAND GLFW" FORCE) +#FetchContent_Declare( +# glfw_fetch +# GIT_REPOSITORY https://github.com/glfw/glfw.git +# GIT_TAG 3.4 +#) + +FetchContent_Declare( + glm_fetch + GIT_REPOSITORY https://github.com/g-truc/glm.git + GIT_TAG 1.0.2 +) + +FetchContent_Declare( + entt_fetch + GIT_REPOSITORY https://github.com/skypjack/entt.git + GIT_TAG b4e58bd +) + +set(SLANG_ENABLE_SLANGC ON CACHE INTERNAL "" FORCE) +set(SLANG_LIB_TYPE SHARED CACHE INTERNAL "" FORCE) +set(SLANG_ENABLE_SLANGRT OFF CACHE INTERNAL "" FORCE) +set(SLANG_ENABLE_GFX OFF CACHE INTERNAL "" FORCE) +set(SLANG_ENABLE_REPLAYER OFF CACHE INTERNAL "" FORCE) + +set(SLANG_ENABLE_TESTS OFF CACHE INTERNAL "" FORCE) +set(SLANG_ENABLE_EXAMPLES OFF CACHE INTERNAL "" FORCE) +set(SLANG_ENABLE_SLANGD OFF CACHE INTERNAL "" FORCE) +set(SLANG_ENABLE_SLANGI OFF CACHE INTERNAL "" FORCE) + +set(SLANG_ENABLE_DXIL ON CACHE INTERNAL "" FORCE) +set(SLANG_ENABLE_SLANG_GLSLANG ON CACHE INTERNAL "" FORCE) +set(SLANG_ENABLE_PREBUILT_BINARIES ON CACHE INTERNAL "" FORCE) + +set(SLANG_ENABLE_FULL_IR_VALIDATION OFF CACHE INTERNAL "" FORCE) +set(SLANG_ENABLE_ASAN OFF CACHE INTERNAL "" FORCE) +set(SLANG_ENABLE_COVERAGE OFF CACHE INTERNAL "" FORCE) +set(SLANG_ENABLE_TIME_TRACE OFF CACHE INTERNAL "" FORCE) +set(SLANG_STANDARD_MODULE_DEVELOP_BUILD OFF CACHE INTERNAL "" FORCE) + +set(SLANG_EXCLUDE_DAWN ON CACHE INTERNAL "" FORCE) +set(SLANG_EXCLUDE_TINT ON CACHE INTERNAL "" FORCE) +SET(SLANG_LIB_TYPE "STATIC" CACHE STRING "" FORCE) +FetchContent_Declare( + slang_fetch + GIT_REPOSITORY https://github.com/shader-slang/slang.git + GIT_TAG 528ddf7 +) + +FetchContent_Declare( + fastgltf_fetch + GIT_REPOSITORY https://github.com/spnda/fastgltf.git + GIT_TAG 35368f913b79acc112c05d67291681e7badea68b +) + +FetchContent_MakeAvailable(vuk_fetch glm_fetch entt_fetch slang_fetch fastgltf_fetch) diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt new file mode 100644 index 0000000..520c432 --- /dev/null +++ b/source/CMakeLists.txt @@ -0,0 +1,18 @@ +add_library(rogue-lib STATIC + cli/args.cpp + cli/args.hpp +) +target_include_directories(rogue-lib PUBLIC source .) +target_compile_definitions(rogue-lib PUBLIC -DGLFW_INCLUDE_VULKAN -DGLM_ENABLE_EXPERIMENTAL) +target_link_libraries(rogue-lib PUBLIC + glm + vuk + vk-bootstrap + slang + fastgltf +) + +add_executable(rogue + main.cxx +) +target_link_libraries(rogue PUBLIC rogue-lib) diff --git a/source/cli/args.cpp b/source/cli/args.cpp new file mode 100644 index 0000000..f2a6e45 --- /dev/null +++ b/source/cli/args.cpp @@ -0,0 +1,103 @@ +#include "args.hpp" + +#include +#include +#include + +namespace rog::cli { + +args parse(int argc, char **argv) { + args result{}; + result.extent = {512, 512}; + result.samples_per_pixel = 256; + + if (argc < 2) { + throw std::runtime_error("no scene provided"); + } + + std::vector args_view(argv + 1, + argv + argc); // skip program name + + for (size_t i = 0; i < args_view.size(); ++i) { + std::string_view arg = args_view[i]; + + if (arg == "--help" || arg == "-h" || arg == "-?") { + result.help = true; + return result; + } + + if (arg[0] != '-' && result.scene_path.empty()) { + result.scene_path = std::filesystem::path(arg); + continue; + } + + if (arg == "--width" || arg == "-w") { + if (i + 1 >= args_view.size()) { + throw std::runtime_error("error: --width requires a value"); + } + const auto val = args_view[++i]; + uint32_t w = 0; + std::from_chars(val.data(), val.data() + val.size(), w); + result.extent.x = w; + continue; + } + + if (arg == "--height" || (arg == "-h" && i + 1 < args_view.size() && + args_view[i + 1][0] != '-')) { + if (i + 1 >= args_view.size()) { + throw std::runtime_error("error: --height requires a value"); + } + const auto val = args_view[++i]; + uint32_t h = 0; + std::from_chars(val.data(), val.data() + val.size(), h); + result.extent.y = h; + continue; + } + + if (arg == "--spp") { + if (i + 1 >= args_view.size()) { + throw std::runtime_error("error: --spp requires a value"); + } + const auto val = args_view[++i]; + uint32_t spp = 0; + std::from_chars(val.data(), val.data() + val.size(), spp); + result.samples_per_pixel = spp; + continue; + } + + if (arg == "--blender") { + if (i + 1 >= args_view.size()) { + throw std::runtime_error("error: --blender requires a path"); + } + result.blender_path = std::filesystem::path(args_view[++i]); + continue; + } + + if (arg[0] == '-') { + throw std::runtime_error(std::format("error: unknown option '{}'", arg)); + } + } + + return result; +} + +std::string_view help_message() noexcept { + return R"(Usage: rogue [OPTIONS] + +positional: + Path to .glb or .blend file (required) + +options: + -w, --width Render width (default: 512) + -h, --height Render height (default: 512) + --spp Samples per pixel (default: 256) + --blender Custom path to Blender executable + --help Show this help message + +examples: + rogue scene.glb + rogue my_scene.blend --interactive + rogue scene.blend -w 1920 -h 1080 --spp 1024 +)"; +} +} // namespace rog::cli diff --git a/source/cli/args.hpp b/source/cli/args.hpp new file mode 100644 index 0000000..7defa2d --- /dev/null +++ b/source/cli/args.hpp @@ -0,0 +1,26 @@ +#ifndef ARGS_HPP +#define ARGS_HPP + +#include +#include +#include +#include + +namespace rog::cli { + +struct args { + std::filesystem::path scene_path; + + glm::uvec2 extent = {512, 512}; + std::uint32_t samples_per_pixel = 256; + + std::optional blender_path; + + bool help = false; +}; +[[nodiscard]] args parse(int argc, char **argv); + +[[nodiscard]] std::string_view help_message() noexcept; +} // namespace rog::cli + +#endif diff --git a/source/main.cxx b/source/main.cxx new file mode 100644 index 0000000..1581625 --- /dev/null +++ b/source/main.cxx @@ -0,0 +1,29 @@ +#include +#include +#include + +auto main(int argc, char **argv) -> int { + auto log = rog::logger(rog::log_level::debug, [](std::string_view message) { + std::println("{}", message); + }); + + try { + const auto args = rog::cli::parse(argc, argv); + if (args.help) { + std::println("{}", rog::cli::help_message()); + return 0; + } + + if (args.scene_path.empty()) { + std::println("scene path required"); + return 1; + } + + if (!std::filesystem::exists(args.scene_path)) { + std::println("{} does not exist", args.scene_path.generic_string()); + return 2; + } + } catch (const std::exception &ex) { + log.error("{}", ex.what()); + } +} diff --git a/source/util/logger.hpp b/source/util/logger.hpp new file mode 100644 index 0000000..116cffc --- /dev/null +++ b/source/util/logger.hpp @@ -0,0 +1,118 @@ +#ifndef LOGGER_HPP +#define LOGGER_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace rog { + +enum class log_level { + debug = 0, + info = 1, + warn = 2, + error = 3, + fatal = 4, +}; + +class logger { +private: + using SinkFunction = std::function; + + std::vector sinks; + log_level current_level = log_level::info; // default level + + [[nodiscard]] bool verify_level(log_level l) const noexcept { + return static_cast(current_level) <= static_cast(l); + } + + static std::string get_timestamp() { + using namespace std::chrono; + + const auto now = system_clock::now(); + const auto time_t_now = system_clock::to_time_t(now); + + const auto ms = duration_cast(now.time_since_epoch()) % 1000; + + std::tm local_tm{}; + +#if defined(_MSC_VER) + localtime_s(&local_tm, &time_t_now); +#else + localtime_r(&time_t_now, &local_tm); // safe on macOS / Linux +#endif + + auto buf = std::array(); + std::strftime(buf.data(), 64, "%Y-%m-%d %H:%M:%S", &local_tm); + + auto buf_str = std::string(buf.data()); + + return std::format("{}.{:03}", buf_str, ms.count()); + } + void dispatch(log_level level, std::string_view level_str, + std::string_view message) { + if (!verify_level(level)) { + return; + } + + std::string timestamp = get_timestamp(); + + std::string formatted = + std::format("{:>5} [{}] -- {}", level_str, timestamp, message); + + for (const auto &sink : sinks) { + sink(formatted); + } + } + +public: + logger() = default; + + template + explicit logger(log_level level, Sinks &&...sinks) : current_level(level) { + (this->sinks.push_back(std::forward(sinks)), ...); + } + + void set_level(log_level level) noexcept { current_level = level; } + + template + void debug(std::format_string fmt, Args &&...args) { + dispatch(log_level::debug, "DEBUG", + std::format(fmt, std::forward(args)...)); + } + + template + void info(std::format_string fmt, Args &&...args) { + dispatch(log_level::info, "INFO", + std::format(fmt, std::forward(args)...)); + } + + template + void warn(std::format_string fmt, Args &&...args) { + dispatch(log_level::warn, "WARN", + std::format(fmt, std::forward(args)...)); + } + + template + void error(std::format_string fmt, Args &&...args) { + dispatch(log_level::error, "ERROR", + std::format(fmt, std::forward(args)...)); + } + + template + void fatal(std::format_string fmt, Args &&...args) { + dispatch(log_level::fatal, "FATAL", + std::format(fmt, std::forward(args)...)); + } + + // Add more sinks at runtime if desired + void add_sink(SinkFunction sink) { sinks.push_back(std::move(sink)); } +}; + +} // namespace rog + +#endif