#pragma once #define UI_MARGIN 5 #define MIN_FPS 24 #define MAX_FPS 240 #include #include #include #include #include "data/glm.hpp" #include "data/circular_buffer.hpp" #include "render/Renderer.hpp" #include "world/Universe.hpp" #include "control/Camera.hpp" #include "contouring/index.hpp" inline glm::vec4 fromHex(const std::string& str) { std::array rgb = {UCHAR_MAX}; sscanf(str.c_str() + 1, "%02X%02X%02X", (unsigned int *)&rgb[0], (unsigned int *)&rgb[1], (unsigned int *)&rgb[2]); return glm::vec4(rgb[0] * 1.f / UCHAR_MAX, rgb[1] * 1.f / UCHAR_MAX, rgb[2] * 1.f / UCHAR_MAX, 1); } inline std::string toHexa(const glm::vec4& rgb) { std::ostringstream sstr; sstr << std::hex << std::setw(2) << std::setfill('0') << static_cast(rgb.x * UCHAR_MAX) << static_cast(rgb.y * UCHAR_MAX) << static_cast(rgb.z * UCHAR_MAX) << std::endl; return sstr.str(); } /// Savable game options struct options { const char *PATH = "config.toml"; /// Load from PATH options() { auto config = std::filesystem::exists(PATH) ? toml::parse_file(PATH) : toml::table(); target_fps = config["window"]["fps"].value_or(60); samples = config["window"]["samples"].value_or(-1); fullscreen = config["window"]["fullscreen"].value_or(false); renderer.textures = config["render"]["textures"].value_or(renderer.textures); renderer.mipMapLOD = config["render"]["texture_quality"].value_or(renderer.mipMapLOD); renderer.main.pbr = config["render"]["pbr"].value_or(renderer.main.pbr); renderer.main.triplanar = config["render"]["triplanar"].value_or(renderer.main.triplanar); renderer.main.geometry = config["render"]["geometry"].value_or(renderer.main.geometry); renderer.main.blend = config["render"]["blend"].value_or(renderer.main.blend); renderer.main.fog = config["render"]["fog"].value_or(renderer.main.fog); const std::string fog = config["render"]["fog_color"].value_or(std::string{"#000000"}); renderer.clear_color = fromHex(fog); renderer.skybox = config["render"]["skybox"].value_or(renderer.skybox); world.loadDistance = config["world"]["load_distance"].value_or(world.loadDistance); world.keepDistance = config["world"]["keep_distance"].value_or(world.keepDistance); world.folderPath = config["world"]["path"].value_or(world.folderPath); voxel_density = config["world"]["voxel_density"].value_or(1); culling = config["mesh"]["culling"].value_or(true); contouring_idx = contouring::idxByName(config["mesh"]["mode"].value_or(std::string(""))); for(const auto& name: contouring::names) { contouring_data.emplace(name, config["mesh"]["options"][name].value_or(std::string(""))); } camera.far = config["camera"]["far"].value_or(camera.far); camera.near = config["camera"]["near"].value_or(camera.near); camera.fov = config["camera"]["fov"].value_or(camera.fov); control.sensibility = config["control"]["sensibility"].value_or(control.sensibility); control.speed = config["control"]["speed"].value_or(control.speed); control.collide = config["control"]["collide"].value_or(control.collide); overlay_show = config["overlay"]["visible"].value_or(true); overlay_corner = config["overlay"]["corner"].value_or(3); //TODO: console show_debug_menu = config["show_debug"]["menu"].value_or(false); show_debug_render = config["show_debug"]["render"].value_or(false); show_debug_world = config["show_debug"]["world"].value_or(false); show_debug_contouring = config["show_debug"]["contouring"].value_or(false); show_debug_controls = config["show_debug"]["controls"].value_or(false); editor_show = config["editor"]["visible"].value_or(false); tool.material = config["editor"]["tool"]["material"].value_or(tool.material); tool.radius = config["editor"]["tool"]["radius"].value_or(tool.radius); } /// Write to PATH void save() { auto config = toml::table(); config.insert_or_assign("window", toml::table({ {"fps", target_fps}, {"samples", samples}, {"fullscreen", fullscreen}, })); config.insert_or_assign("render", toml::table({ {"textures", renderer.textures}, {"texture_quality", renderer.mipMapLOD}, {"pbr", renderer.main.pbr}, {"triplanar", renderer.main.triplanar}, {"geometry", renderer.main.geometry}, {"blend", renderer.main.blend}, {"fog", renderer.main.fog}, {"fog_color", toHexa(renderer.clear_color)}, {"skybox", renderer.skybox} })); config.insert_or_assign("world", toml::table({ {"load_distance", world.loadDistance}, {"keep_distance", world.keepDistance}, {"voxel_density", voxel_density}, {"path", world.folderPath} })); config.insert_or_assign("mesh", toml::table({ {"culling", culling}, {"mode", contouring::names[contouring_idx]}, {"options", toml::table()} })); for(const auto& [key, val]: contouring_data) { if(!val.empty()) config["mesh"]["options"].as_table()->insert_or_assign(key, val); } config.insert_or_assign("camera", toml::table({ {"far", camera.far}, {"near", camera.near}, {"fov", camera.fov} })); config.insert_or_assign("control", toml::table({ {"sensibility", control.sensibility}, {"speed", control.speed}, {"collide", control.collide} })); config.insert_or_assign("overlay", toml::table({ {"visible", overlay_show}, {"corner", overlay_corner} })); config.insert_or_assign("show_debug", toml::table({ {"menu", show_debug_menu}, {"render", show_debug_render}, {"world", show_debug_world}, {"contouring", show_debug_contouring}, {"controls", show_debug_controls} })); config.insert_or_assign("editor", toml::table({ {"visible", editor_show}, {"tool", toml::table({ {"material", (int)tool.material}, {"radius", tool.radius} })} })); std::ofstream out; out.open(PATH, std::ios::out | std::ios::trunc); out << config << "\n\n"; out.close(); } bool show_debug_menu; bool show_debug_render; int target_fps; int samples; bool fullscreen; Renderer::options renderer; bool show_debug_world; world::Universe::options world; int voxel_density; bool show_debug_contouring; bool culling; int contouring_idx; std::map contouring_data; bool show_debug_controls; Controllable::options control; Camera::options camera; bool editor_show; struct tool { int radius = 2; unsigned short material = 2; } tool; bool overlay_show; int overlay_corner; bool console_show = false; bool console_scrool = true; }; /// Live state struct state { bool capture_mouse = true; camera_pos position = camera_pos(voxel_pos(0), 1); std::optional look_at = {}; std::shared_ptr contouring; std::array console_buffer; }; /// Readonly metrics struct reports { size_t tris_count = 0; size_t models_count = 0; };