#pragma once #include #include #include #include #include "world/Universe.hpp" #include "render/Renderer.hpp" #include "contouring/index.hpp" #include "control/Camera.hpp" namespace config::client { 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(); } struct options { public: options(toml::node_view config) { assert(config["enabled"]); window.targetFPS = config["window"]["target_fps"].value_or(window.targetFPS); window.samples = config["window"]["samples"].value_or(window.samples); window.fullscreen = config["window"]["fullscreen"].value_or(window.fullscreen); renderer.inFlightFrames = config["window"]["parallel_frames"].value_or(renderer.inFlightFrames); preferVulkan = config["render"]["prefer_vulkan"].value_or(preferVulkan); renderer.textures = config["render"]["textures"].value_or(renderer.textures); renderer.mipMapLOD = config["render"]["texture_quality"].value_or(renderer.mipMapLOD); renderer.anisotropy = config["render"]["texture_angular_quality"].value_or(renderer.anisotropy); renderer.voxel.pbr = config["render"]["pbr"].value_or(renderer.voxel.pbr); renderer.voxel.triplanar = config["render"]["triplanar"].value_or(renderer.voxel.triplanar); renderer.voxel.stochastic = config["render"]["stochastic"].value_or(renderer.voxel.stochastic); renderer.voxel.geometry = config["render"]["geometry"].value_or(renderer.voxel.geometry); renderer.voxel.blend = config["render"]["blend"].value_or(renderer.voxel.blend); renderer.voxel.fog = config["render"]["fog"].value_or(renderer.voxel.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); renderer.voxel.curvature = config["render"]["curvature"].value_or(renderer.voxel.curvature); renderer.voxel.curv_depth = config["render"]["curvature_depth"].value_or(renderer.voxel.curv_depth); culling = config["render"]["culling"].value_or(culling); contouring.idx = contouring::idxByName(config["contouring"]["mode"].value_or(std::string(""))); for(const auto& name: contouring::names) { contouring.data.emplace(name, config["contouring"]["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); world.loadDistance = config["world"]["load_distance"].value_or(world.loadDistance); world.keepDistance = config["world"]["keep_distance"].value_or(world.keepDistance); voxel_density = config["world"]["voxel_density"].value_or(voxel_density); #ifndef STANDALONE const auto useLocal = config["connection"]["use_local"].value_or(true); #else const auto useLocal = false; #endif if(!useLocal) { world::client::Universe::connection ct; ct.address = config["connection"]["address"].value_or(ct.address); ct.port = config["connection"]["port"].value_or(ct.port); connection = ct; } editor.visible = config["editor"]["visible"].value_or(editor.visible); editor.tool.radius = config["editor"]["tool"]["radius"].value_or(editor.tool.radius); editor.tool.material = config["editor"]["tool"]["material"].value_or(editor.tool.material); editor.tool.emptyAir = config["editor"]["tool"]["empty_air"].value_or(editor.tool.emptyAir); debugMenu.bar = config["debug_menu"]["bar"].value_or(debugMenu.bar); debugMenu.render = config["debug_menu"]["render"].value_or(debugMenu.render); debugMenu.world = config["debug_menu"]["world"].value_or(debugMenu.world); debugMenu.contouring = config["debug_menu"]["contouring"].value_or(debugMenu.contouring); debugMenu.controls = config["debug_menu"]["controls"].value_or(debugMenu.controls); overlay.visible = config["overlay"]["visible"].value_or(overlay.visible); overlay.corner = config["overlay"]["corner"].value_or(overlay.corner); } toml::table save() { auto config = toml::table(); config.insert_or_assign("enabled", true); config.insert_or_assign("window", toml::table({ {"target_fps", window.targetFPS}, {"samples", window.samples}, {"fullscreen", window.fullscreen}, {"parallel_frames", renderer.inFlightFrames} })); config.insert_or_assign("render", toml::table({ {"prefer_vulkan", preferVulkan}, {"textures", renderer.textures}, {"texture_quality", renderer.mipMapLOD}, {"texture_angular_quality", renderer.anisotropy}, {"pbr", renderer.voxel.pbr}, {"triplanar", renderer.voxel.triplanar}, {"stochastic", renderer.voxel.stochastic}, {"geometry", renderer.voxel.geometry}, {"blend", renderer.voxel.blend}, {"fog", renderer.voxel.fog}, {"fog_color", toHexa(renderer.clear_color)}, {"skybox", renderer.skybox}, {"curvature", renderer.voxel.curvature}, {"curvature_depth", renderer.voxel.curv_depth}, {"culling", culling} })); config.insert_or_assign("contouring", toml::table({ {"mode", contouring::names[contouring.idx]}, {"options", toml::table()} })); for(const auto& [key, val]: contouring.data) { if(!val.empty()) config["contouring"]["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("world", toml::table({ {"load_distance", world.loadDistance}, {"keep_distance", world.keepDistance}, {"voxel_density", voxel_density} })); if(connection.has_value()) { const auto &ct = connection.value(); config.insert_or_assign("connection", toml::table({ #ifndef STANDALONE {"use_local", false}, #endif {"address", ct.address}, {"port", ct.port} })); } else { config.insert_or_assign("connection", toml::table({{"use_local", true}})); } config.insert_or_assign("editor", toml::table({ {"visible", editor.visible}, {"tool", toml::table({ {"radius", editor.tool.radius}, {"material", editor.tool.material}, {"empty_air", editor.tool.emptyAir} })} })); config.insert_or_assign("debug_menu", toml::table({ {"bar", debugMenu.bar}, {"render", debugMenu.render}, {"world", debugMenu.world}, {"contouring", debugMenu.contouring}, {"controls", debugMenu.controls} })); config.insert_or_assign("overlay", toml::table({ {"visible", overlay.visible}, {"corner", overlay.corner} })); return config; } world::client::Universe::options world; int voxel_density = 1; struct { bool bar = false; bool render = false; bool world = false; bool contouring = false; bool controls = false; } debugMenu; struct { int targetFPS = 60; int samples = -1; bool fullscreen = false; } window; bool preferVulkan = true; render::renderOptions renderer; int culling = 0; struct { int idx; std::map data; } contouring; Controllable::options control; Camera::options camera; struct { bool visible = false; struct { int radius = 2; unsigned short material = 5; bool emptyAir = true; } tool; } editor; struct { bool visible = true; int corner = 3; } overlay; std::optional connection = {}; }; }