initial commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
build
|
||||
.cache
|
||||
6
CMakeLists.txt
Normal file
6
CMakeLists.txt
Normal file
@@ -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)
|
||||
66
external/CMakeLists.txt
vendored
Normal file
66
external/CMakeLists.txt
vendored
Normal file
@@ -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)
|
||||
18
source/CMakeLists.txt
Normal file
18
source/CMakeLists.txt
Normal file
@@ -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)
|
||||
103
source/cli/args.cpp
Normal file
103
source/cli/args.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "args.hpp"
|
||||
|
||||
#include <charconv>
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
|
||||
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<std::string_view> 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] <scene>
|
||||
|
||||
positional:
|
||||
<scene> Path to .glb or .blend file (required)
|
||||
|
||||
options:
|
||||
-w, --width <uint> Render width (default: 512)
|
||||
-h, --height <uint> Render height (default: 512)
|
||||
--spp <uint> Samples per pixel (default: 256)
|
||||
--blender <path> 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
|
||||
26
source/cli/args.hpp
Normal file
26
source/cli/args.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef ARGS_HPP
|
||||
#define ARGS_HPP
|
||||
|
||||
#include <filesystem>
|
||||
#include <glm/glm.hpp>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace rog::cli {
|
||||
|
||||
struct args {
|
||||
std::filesystem::path scene_path;
|
||||
|
||||
glm::uvec2 extent = {512, 512};
|
||||
std::uint32_t samples_per_pixel = 256;
|
||||
|
||||
std::optional<std::filesystem::path> blender_path;
|
||||
|
||||
bool help = false;
|
||||
};
|
||||
[[nodiscard]] args parse(int argc, char **argv);
|
||||
|
||||
[[nodiscard]] std::string_view help_message() noexcept;
|
||||
} // namespace rog::cli
|
||||
|
||||
#endif
|
||||
29
source/main.cxx
Normal file
29
source/main.cxx
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <cli/args.hpp>
|
||||
#include <print>
|
||||
#include <util/logger.hpp>
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
118
source/util/logger.hpp
Normal file
118
source/util/logger.hpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#ifndef LOGGER_HPP
|
||||
#define LOGGER_HPP
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace rog {
|
||||
|
||||
enum class log_level {
|
||||
debug = 0,
|
||||
info = 1,
|
||||
warn = 2,
|
||||
error = 3,
|
||||
fatal = 4,
|
||||
};
|
||||
|
||||
class logger {
|
||||
private:
|
||||
using SinkFunction = std::function<void(std::string_view)>;
|
||||
|
||||
std::vector<SinkFunction> sinks;
|
||||
log_level current_level = log_level::info; // default level
|
||||
|
||||
[[nodiscard]] bool verify_level(log_level l) const noexcept {
|
||||
return static_cast<int>(current_level) <= static_cast<int>(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<milliseconds>(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<char, 64>();
|
||||
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 <typename... Sinks>
|
||||
explicit logger(log_level level, Sinks &&...sinks) : current_level(level) {
|
||||
(this->sinks.push_back(std::forward<Sinks>(sinks)), ...);
|
||||
}
|
||||
|
||||
void set_level(log_level level) noexcept { current_level = level; }
|
||||
|
||||
template <typename... Args>
|
||||
void debug(std::format_string<Args...> fmt, Args &&...args) {
|
||||
dispatch(log_level::debug, "DEBUG",
|
||||
std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void info(std::format_string<Args...> fmt, Args &&...args) {
|
||||
dispatch(log_level::info, "INFO",
|
||||
std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void warn(std::format_string<Args...> fmt, Args &&...args) {
|
||||
dispatch(log_level::warn, "WARN",
|
||||
std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void error(std::format_string<Args...> fmt, Args &&...args) {
|
||||
dispatch(log_level::error, "ERROR",
|
||||
std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void fatal(std::format_string<Args...> fmt, Args &&...args) {
|
||||
dispatch(log_level::fatal, "FATAL",
|
||||
std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
// Add more sinks at runtime if desired
|
||||
void add_sink(SinkFunction sink) { sinks.push_back(std::move(sink)); }
|
||||
};
|
||||
|
||||
} // namespace rog
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user