From 7449434f5ca36330b65c2365f9f66dee1d5cc2ff Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 11 Mar 2024 15:09:21 +0300 Subject: [PATCH] assets preloading with preload.json --- res/preload.json | 14 ++++ src/assets/AssetsLoader.cpp | 144 +++++++++++++++++++++++++-------- src/assets/AssetsLoader.h | 33 +++++--- src/assets/assetload_funcs.cpp | 12 ++- src/data/dynamic.cpp | 28 +++++++ src/data/dynamic.h | 5 ++ src/files/files.h | 35 +++++--- src/frontend/gui/gui_xml.cpp | 2 +- 8 files changed, 209 insertions(+), 64 deletions(-) create mode 100644 res/preload.json diff --git a/res/preload.json b/res/preload.json new file mode 100644 index 00000000..bc5bc2ac --- /dev/null +++ b/res/preload.json @@ -0,0 +1,14 @@ +{ + "shaders": [ + "ui3d", + "screen", + "background", + "skybox_gen" + ], + "textures": [ + "misc/moon", + "misc/sun", + "gui/crosshair" + ] +} + diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index d686e6cf..1d27e06e 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -7,26 +7,30 @@ #include #include "../constants.h" +#include "../data/dynamic.h" +#include "../files/files.h" #include "../files/engine_paths.h" #include "../content/Content.h" +#include "../content/ContentPack.h" #include "../logic/scripting/scripting.h" AssetsLoader::AssetsLoader(Assets* assets, const ResPaths* paths) - : assets(assets), paths(paths) { - addLoader(ASSET_SHADER, assetload::shader); - addLoader(ASSET_TEXTURE, assetload::texture); - addLoader(ASSET_FONT, assetload::font); - addLoader(ASSET_ATLAS, assetload::atlas); - addLoader(ASSET_LAYOUT, assetload::layout); - addLoader(ASSET_SOUND, assetload::sound); + : assets(assets), paths(paths) +{ + addLoader(AssetType::shader, assetload::shader); + addLoader(AssetType::texture, assetload::texture); + addLoader(AssetType::font, assetload::font); + addLoader(AssetType::atlas, assetload::atlas); + addLoader(AssetType::layout, assetload::layout); + addLoader(AssetType::sound, assetload::sound); } -void AssetsLoader::addLoader(int tag, aloader_func func) { +void AssetsLoader::addLoader(AssetType tag, aloader_func func) { loaders[tag] = func; } -void AssetsLoader::add(int tag, const std::string filename, const std::string alias, std::shared_ptr settings) { - entries.push(aloader_entry{ tag, filename, alias, settings}); +void AssetsLoader::add(AssetType tag, const std::string filename, const std::string alias, std::shared_ptr settings) { + entries.push(aloader_entry{tag, filename, alias, settings}); } bool AssetsLoader::hasNext() const { @@ -39,7 +43,7 @@ bool AssetsLoader::loadNext() { std::cout.flush(); auto found = loaders.find(entry.tag); if (found == loaders.end()) { - std::cerr << "unknown asset tag " << entry.tag << std::endl; + std::cerr << "unknown asset tag " << static_cast(entry.tag) << std::endl; return false; } aloader_func loader = found->second; @@ -57,7 +61,7 @@ void addLayouts(int env, const std::string& prefix, const fs::path& folder, Asse if (file.extension().u8string() != ".xml") continue; std::string name = prefix+":"+file.stem().u8string(); - loader.add(ASSET_LAYOUT, file.u8string(), name, std::make_shared(env)); + loader.add(AssetType::layout, file.u8string(), name, std::make_shared(env)); } } @@ -65,29 +69,103 @@ void AssetsLoader::tryAddSound(std::string name) { if (name.empty()) { return; } - fs::path file = SOUNDS_FOLDER+"/"+name+".ogg"; - add(ASSET_SOUND, file, name); + std::string file = SOUNDS_FOLDER+"/"+name; + add(AssetType::sound, file, name); +} + +static std::string assets_def_folder(AssetType tag) { + switch (tag) { + case AssetType::font: return FONTS_FOLDER; + case AssetType::shader: return SHADERS_FOLDER; + case AssetType::texture: return TEXTURES_FOLDER; + case AssetType::atlas: return TEXTURES_FOLDER; + case AssetType::layout: return LAYOUTS_FOLDER; + case AssetType::sound: return SOUNDS_FOLDER; + } + return ""; +} + +void AssetsLoader::processPreload( + AssetType tag, + const std::string& name, + dynamic::Map* map +) { + std::string defFolder = assets_def_folder(tag); + std::string path = defFolder+"/"+name; + if (map == nullptr) { + add(tag, path, name); + return; + } + map->str("path", path); + switch (tag) { + case AssetType::sound: + add(tag, path, name, std::make_shared( + map->getBool("keep-pcm", false) + )); + break; + default: + add(tag, path, name); + break; + } +} + +void AssetsLoader::processPreloadList(AssetType tag, dynamic::List* list) { + if (list == nullptr) { + return; + } + for (uint i = 0; i < list->size(); i++) { + auto value = list->get(i); + switch (value->type) { + case dynamic::valtype::string: + processPreload(tag, *value->value.str, nullptr); + break; + case dynamic::valtype::map: { + auto name = value->value.map->getStr("name"); + processPreload(tag, name, value->value.map); + break; + } + default: + throw std::runtime_error("invalid entry type"); + } + } +} + +void AssetsLoader::processPreloadConfig(fs::path file) { + auto root = files::read_json(file); + processPreloadList(AssetType::font, root->list("fonts")); + processPreloadList(AssetType::shader, root->list("shaders")); + processPreloadList(AssetType::texture, root->list("textures")); + processPreloadList(AssetType::sound, root->list("sounds")); + // layouts are loaded automatically +} + +void AssetsLoader::processPreloadConfigs(const Content* content) { + for (auto& entry : content->getPacks()) { + const auto& pack = entry.second; + auto preloadFile = pack->getInfo().folder / fs::path("preload.json"); + if (fs::exists(preloadFile)) { + processPreloadConfig(preloadFile); + } + } + auto preloadFile = paths->getMainRoot()/fs::path("preload.json"); + if (fs::exists(preloadFile)) { + processPreloadConfig(preloadFile); + } } void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) { - loader.add(ASSET_FONT, FONTS_FOLDER+"/font", "normal"); - loader.add(ASSET_SHADER, SHADERS_FOLDER+"/ui", "ui"); - loader.add(ASSET_SHADER, SHADERS_FOLDER+"/main", "main"); - loader.add(ASSET_SHADER, SHADERS_FOLDER+"/lines", "lines"); - loader.add(ASSET_TEXTURE, TEXTURES_FOLDER+"/gui/menubg.png", "gui/menubg"); - loader.add(ASSET_TEXTURE, TEXTURES_FOLDER+"/gui/delete_icon.png", "gui/delete_icon"); - loader.add(ASSET_TEXTURE, TEXTURES_FOLDER+"/gui/no_icon.png", "gui/no_icon"); - loader.add(ASSET_TEXTURE, TEXTURES_FOLDER+"/gui/warning.png", "gui/warning"); - loader.add(ASSET_TEXTURE, TEXTURES_FOLDER+"/gui/error.png", "gui/error"); - loader.add(ASSET_TEXTURE, TEXTURES_FOLDER+"/gui/cross.png", "gui/cross"); + loader.add(AssetType::font, FONTS_FOLDER+"/font", "normal"); + loader.add(AssetType::shader, SHADERS_FOLDER+"/ui", "ui"); + loader.add(AssetType::shader, SHADERS_FOLDER+"/main", "main"); + loader.add(AssetType::shader, SHADERS_FOLDER+"/lines", "lines"); + loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/menubg", "gui/menubg"); + loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/delete_icon", "gui/delete_icon"); + loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/no_icon", "gui/no_icon"); + loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/warning", "gui/warning"); + loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/error", "gui/error"); + loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/cross", "gui/cross"); if (content) { - loader.add(ASSET_SHADER, SHADERS_FOLDER+"/ui3d", "ui3d"); - loader.add(ASSET_SHADER, SHADERS_FOLDER+"/screen", "screen"); - loader.add(ASSET_SHADER, SHADERS_FOLDER+"/background", "background"); - loader.add(ASSET_SHADER, SHADERS_FOLDER+"/skybox_gen", "skybox_gen"); - loader.add(ASSET_TEXTURE, TEXTURES_FOLDER+"/misc/moon.png", "misc/moon"); - loader.add(ASSET_TEXTURE, TEXTURES_FOLDER+"/misc/sun.png", "misc/sun"); - loader.add(ASSET_TEXTURE, TEXTURES_FOLDER+"/gui/crosshair.png", "gui/crosshair"); + loader.processPreloadConfigs(content); for (auto& entry : content->getBlockMaterials()) { auto& material = entry.second; @@ -104,8 +182,8 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) { addLayouts(pack->getEnvironment()->getId(), info.id, folder, loader); } } - loader.add(ASSET_ATLAS, TEXTURES_FOLDER+"/blocks", "blocks"); - loader.add(ASSET_ATLAS, TEXTURES_FOLDER+"/items", "items"); + loader.add(AssetType::atlas, TEXTURES_FOLDER+"/blocks", "blocks"); + loader.add(AssetType::atlas, TEXTURES_FOLDER+"/items", "items"); } const ResPaths* AssetsLoader::getPaths() const { diff --git a/src/assets/AssetsLoader.h b/src/assets/AssetsLoader.h index 2c277418..5de43150 100644 --- a/src/assets/AssetsLoader.h +++ b/src/assets/AssetsLoader.h @@ -3,16 +3,24 @@ #include #include +#include #include #include #include -inline constexpr short ASSET_TEXTURE = 1; -inline constexpr short ASSET_SHADER = 2; -inline constexpr short ASSET_FONT = 3; -inline constexpr short ASSET_ATLAS = 4; -inline constexpr short ASSET_LAYOUT = 5; -inline constexpr short ASSET_SOUND = 6; +namespace dynamic { + class Map; + class List; +} + +enum class AssetType { + texture, + shader, + font, + atlas, + layout, + sound +}; class ResPaths; class Assets; @@ -38,7 +46,7 @@ struct SoundCfg : AssetCfg { using aloader_func = std::function)>; struct aloader_entry { - int tag; + AssetType tag; const std::string filename; const std::string alias; std::shared_ptr config; @@ -46,14 +54,19 @@ struct aloader_entry { class AssetsLoader { Assets* assets; - std::map loaders; + std::map loaders; std::queue entries; const ResPaths* paths; void tryAddSound(std::string name); + + void processPreload(AssetType tag, const std::string& name, dynamic::Map* map); + void processPreloadList(AssetType tag, dynamic::List* list); + void processPreloadConfig(std::filesystem::path file); + void processPreloadConfigs(const Content* content); public: AssetsLoader(Assets* assets, const ResPaths* paths); - void addLoader(int tag, aloader_func func); + void addLoader(AssetType tag, aloader_func func); /// @brief Enqueue asset load /// @param tag asset type @@ -61,7 +74,7 @@ public: /// @param alias internal asset name /// @param settings asset loading settings (based on asset type) void add( - int tag, + AssetType tag, const std::string filename, const std::string alias, std::shared_ptr settings=nullptr diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index aae0a8f6..833b8850 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -37,7 +37,7 @@ bool assetload::texture( std::shared_ptr ) { std::unique_ptr texture( - png::load_texture(paths->find(filename).u8string()) + png::load_texture(paths->find(filename+".png").u8string()) ); if (texture == nullptr) { std::cerr << "failed to load texture '" << name << "'" << std::endl; @@ -171,26 +171,24 @@ bool assetload::sound( auto cfg = dynamic_cast(config.get()); bool keepPCM = cfg ? cfg->keepPCM : false; - size_t lastindex = file.find_last_of("."); - std::string extension = file.substr(lastindex); - std::string extensionless = file.substr(0, lastindex); + std::string extension = ".ogg"; try { std::unique_ptr baseSound = nullptr; // looking for 'sound_name' as base sound - auto soundFile = paths->find(file); + auto soundFile = paths->find(file+extension); if (fs::exists(soundFile)) { baseSound.reset(audio::load_sound(soundFile, keepPCM)); } // looking for 'sound_name_0' as base sound - auto variantFile = paths->find(extensionless+"_0"+extension); + auto variantFile = paths->find(file+"_0"+extension); if (fs::exists(variantFile)) { baseSound.reset(audio::load_sound(variantFile, keepPCM)); } // loading sound variants for (uint i = 1; ; i++) { - auto variantFile = paths->find(extensionless+"_"+std::to_string(i)+extension); + auto variantFile = paths->find(file+"_"+std::to_string(i)+extension); if (!fs::exists(variantFile)) { break; } diff --git a/src/data/dynamic.cpp b/src/data/dynamic.cpp index 26452151..e7f1a10c 100644 --- a/src/data/dynamic.cpp +++ b/src/data/dynamic.cpp @@ -155,6 +155,34 @@ void Map::str(std::string key, std::string& dst) const { dst = getStr(key, dst); } +std::string Map::getStr(std::string key) const { + if (values.find(key) == values.end()) { + throw std::runtime_error("missing key '"+key+"'"); + } + return getStr(key, ""); +} + +double Map::getNum(std::string key) const { + if (values.find(key) == values.end()) { + throw std::runtime_error("missing key '"+key+"'"); + } + return getNum(key, 0); +} + +int64_t Map::getInt(std::string key) const { + if (values.find(key) == values.end()) { + throw std::runtime_error("missing key '"+key+"'"); + } + return getInt(key, 0); +} + +bool Map::getBool(std::string key) const { + if (values.find(key) == values.end()) { + throw std::runtime_error("missing key '"+key+"'"); + } + return getBool(key, false); +} + std::string Map::getStr(std::string key, const std::string& def) const { auto found = values.find(key); if (found == values.end()) diff --git a/src/data/dynamic.h b/src/data/dynamic.h index 928ddb60..be8a2e39 100644 --- a/src/data/dynamic.h +++ b/src/data/dynamic.h @@ -77,6 +77,11 @@ namespace dynamic { std::unordered_map> values; ~Map(); + std::string getStr(std::string key) const; + double getNum(std::string key) const; + int64_t getInt(std::string key) const; + bool getBool(std::string key) const; + std::string getStr(std::string key, const std::string& def) const; double getNum(std::string key, double def) const; int64_t getInt(std::string key, int64_t def) const; diff --git a/src/files/files.h b/src/files/files.h index f7515712..eba79a07 100644 --- a/src/files/files.h +++ b/src/files/files.h @@ -15,7 +15,7 @@ namespace dynamic { } namespace files { - /* Read-only random access file */ + /// @brief Read-only random access file class rafile { std::ifstream file; size_t filelength; @@ -27,34 +27,43 @@ namespace files { size_t length() const; }; - /* Write bytes array to the file without any extra data */ - extern bool write_bytes(fs::path, const ubyte* data, size_t size); + /// @brief Write bytes array to the file without any extra data + /// @param file target file + /// @param data data bytes array + /// @param size size of data bytes array + extern bool write_bytes(fs::path file, const ubyte* data, size_t size); - /* Append bytes array to the file without any extra data */ - extern uint append_bytes(fs::path, const ubyte* data, size_t size); + /// @brief Append bytes array to the file without any extra data + /// @param file target file + /// @param data data bytes array + /// @param size size of data bytes array + extern uint append_bytes(fs::path file, const ubyte* data, size_t size); - /* Write string to the file */ + /// @brief Write string to the file extern bool write_string(fs::path filename, const std::string content); - /* Write dynamic data to the JSON file - @param nice if true, - human readable format will be used, otherwise minimal */ + /// @brief Write dynamic data to the JSON file + /// @param nice if true, human readable format will be used, otherwise minimal extern bool write_json( fs::path filename, const dynamic::Map* obj, bool nice=true); - /* Write dynamic data to the binary JSON file - (see src/coders/binary_json_spec.md) - @param compressed use gzip compression */ + /// @brief Write dynamic data to the binary JSON file + /// (see src/coders/binary_json_spec.md) + /// @param compressed use gzip compression extern bool write_binary_json( fs::path filename, const dynamic::Map* obj, - bool compressed=false); + bool compressed=false + ); extern bool read(fs::path, char* data, size_t size); extern ubyte* read_bytes(fs::path, size_t& length); extern std::string read_string(fs::path filename); + + /// @brief Read JSON or BJSON file + /// @param file *.json or *.bjson file extern std::unique_ptr read_json(fs::path file); extern std::unique_ptr read_binary_json(fs::path file); extern std::vector read_list(fs::path file); diff --git a/src/frontend/gui/gui_xml.cpp b/src/frontend/gui/gui_xml.cpp index b2c02e0c..8ddb4494 100644 --- a/src/frontend/gui/gui_xml.cpp +++ b/src/frontend/gui/gui_xml.cpp @@ -289,7 +289,7 @@ static std::shared_ptr readImage(UiXmlReader& reader, xml::xmlelement el std::string src = element->attr("src", "").getText(); auto image = std::make_shared(src); _readUINode(reader, element, *image); - reader.getAssetsLoader().add(ASSET_TEXTURE, "textures/"+src+".png", src, nullptr); + reader.getAssetsLoader().add(AssetType::texture, "textures/"+src, src, nullptr); return image; }