diff --git a/res/content/base/preload.json b/res/content/base/preload.json index be5d5bec..364813ec 100644 --- a/res/content/base/preload.json +++ b/res/content/base/preload.json @@ -6,5 +6,8 @@ "models": [ "cube", "item" + ], + "rigs": [ + "drop" ] } diff --git a/res/content/base/rigs/drop.json b/res/content/base/rigs/drop.json new file mode 100644 index 00000000..675759f7 --- /dev/null +++ b/res/content/base/rigs/drop.json @@ -0,0 +1,5 @@ +{ + "root": { + "model": "cube" + } +} diff --git a/res/content/base/scripts/components/drop.lua b/res/content/base/scripts/components/drop.lua index 72acec7c..3ce24f95 100644 --- a/res/content/base/scripts/components/drop.lua +++ b/res/content/base/scripts/components/drop.lua @@ -1,5 +1,6 @@ local tsf = entity.transform local body = entity.rigidbody +local rig = entity.modeltree inair = true ready = false diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index 65f52da7..e426a8c1 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -24,13 +24,14 @@ static debug::Logger logger("assets-loader"); AssetsLoader::AssetsLoader(Assets* assets, const ResPaths* paths) : 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); - addLoader(AssetType::model, assetload::model); + 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); + addLoader(AssetType::MODEL, assetload::model); + addLoader(AssetType::RIG, assetload::rig); } void AssetsLoader::addLoader(AssetType tag, aloader_func func) { @@ -80,7 +81,7 @@ void addLayouts(const scriptenv& env, const std::string& prefix, const fs::path& if (file.extension().u8string() != ".xml") continue; std::string name = prefix+":"+file.stem().u8string(); - loader.add(AssetType::layout, file.u8string(), name, std::make_shared(env)); + loader.add(AssetType::LAYOUT, file.u8string(), name, std::make_shared(env)); } } @@ -89,18 +90,19 @@ void AssetsLoader::tryAddSound(const std::string& name) { return; } std::string file = SOUNDS_FOLDER+"/"+name; - add(AssetType::sound, file, 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; - case AssetType::model: return MODELS_FOLDER; + 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; + case AssetType::MODEL: return MODELS_FOLDER; + case AssetType::RIG: return RIGS_FOLDER; } return ""; } @@ -118,7 +120,7 @@ void AssetsLoader::processPreload( } map->str("path", path); switch (tag) { - case AssetType::sound: + case AssetType::SOUND: add(tag, path, name, std::make_shared( map->get("keep-pcm", false) )); @@ -153,11 +155,12 @@ void AssetsLoader::processPreloadList(AssetType tag, dynamic::List* list) { void AssetsLoader::processPreloadConfig(const 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")); - processPreloadList(AssetType::model, root->list("models")); + processPreloadList(AssetType::FONT, root->list("fonts")); + processPreloadList(AssetType::SHADER, root->list("shaders")); + processPreloadList(AssetType::TEXTURE, root->list("textures")); + processPreloadList(AssetType::SOUND, root->list("sounds")); + processPreloadList(AssetType::MODEL, root->list("models")); + processPreloadList(AssetType::RIG, root->list("rigs")); // layouts are loaded automatically } @@ -176,18 +179,18 @@ void AssetsLoader::processPreloadConfigs(const Content* content) { } void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) { - 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/no_world_icon", "gui/no_world_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"); - loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/refresh", "gui/refresh"); + 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/no_world_icon", "gui/no_world_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"); + loader.add(AssetType::TEXTURE, TEXTURES_FOLDER+"/gui/refresh", "gui/refresh"); if (content) { loader.processPreloadConfigs(content); @@ -206,8 +209,8 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) { addLayouts(pack->getEnvironment(), info.id, folder, loader); } } - loader.add(AssetType::atlas, TEXTURES_FOLDER+"/blocks", "blocks"); - loader.add(AssetType::atlas, TEXTURES_FOLDER+"/items", "items"); + loader.add(AssetType::ATLAS, TEXTURES_FOLDER+"/blocks", "blocks"); + loader.add(AssetType::ATLAS, TEXTURES_FOLDER+"/items", "items"); } bool AssetsLoader::loadExternalTexture( diff --git a/src/assets/AssetsLoader.hpp b/src/assets/AssetsLoader.hpp index 91eb1036..29a23428 100644 --- a/src/assets/AssetsLoader.hpp +++ b/src/assets/AssetsLoader.hpp @@ -20,13 +20,14 @@ namespace dynamic { } enum class AssetType { - texture, - shader, - font, - atlas, - layout, - sound, - model, + TEXTURE, + SHADER, + FONT, + ATLAS, + LAYOUT, + SOUND, + MODEL, + RIG, }; class ResPaths; diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index 0fd5a36d..690422cb 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -18,6 +18,7 @@ #include "../graphics/core/Font.hpp" #include "../graphics/core/Model.hpp" #include "../graphics/core/TextureAnimation.hpp" +#include "../objects/rigging.hpp" #include "../frontend/UiDocument.hpp" #include "../constants.hpp" @@ -216,7 +217,7 @@ assetload::postfunc assetload::model( for (auto& mesh : model->meshes) { if (mesh.texture.find('$') == std::string::npos) { auto filename = TEXTURES_FOLDER+"/"+mesh.texture; - loader->add(AssetType::texture, filename, mesh.texture, nullptr); + loader->add(AssetType::TEXTURE, filename, mesh.texture, nullptr); } } assets->store(std::unique_ptr(model), name); @@ -227,6 +228,27 @@ assetload::postfunc assetload::model( } } +assetload::postfunc assetload::rig( + AssetsLoader* loader, + const ResPaths* paths, + const std::string& file, + const std::string& name, + const std::shared_ptr& +) { + auto path = paths->find(file+".json"); + auto text = files::read_string(path); + try { + auto rig = rigging::RigConfig::parse(text, path.u8string()).release(); + return [=](Assets* assets) { + // TODO: add models loading + assets->store(std::unique_ptr(rig), name); + }; + } catch (const parsing_error& err) { + std::cerr << err.errorLog() << std::endl; + throw; + } +} + static void read_anim_file( const std::string& animFile, std::vector>& frameList diff --git a/src/assets/assetload_funcs.hpp b/src/assets/assetload_funcs.hpp index 71b3cb93..cad64d32 100644 --- a/src/assets/assetload_funcs.hpp +++ b/src/assets/assetload_funcs.hpp @@ -18,49 +18,56 @@ namespace assetload { AssetsLoader*, const ResPaths* paths, const std::string& filename, - const std::string &name, + const std::string& name, const std::shared_ptr& settings ); postfunc shader( AssetsLoader*, const ResPaths* paths, const std::string& filename, - const std::string &name, + const std::string& name, const std::shared_ptr& settings ); postfunc atlas( AssetsLoader*, const ResPaths* paths, - const std::string &directory, - const std::string &name, + const std::string& directory, + const std::string& name, const std::shared_ptr& settings ); postfunc font( AssetsLoader*, const ResPaths* paths, const std::string& filename, - const std::string &name, + const std::string& name, const std::shared_ptr& settings ); postfunc layout( AssetsLoader*, const ResPaths* paths, const std::string& file, - const std::string &name, + const std::string& name, const std::shared_ptr& settings ); postfunc sound( AssetsLoader*, const ResPaths* paths, const std::string& file, - const std::string &name, + const std::string& name, const std::shared_ptr& settings ); postfunc model( AssetsLoader*, const ResPaths* paths, const std::string& file, - const std::string &name, + const std::string& name, + const std::shared_ptr& settings + ); + postfunc rig( + AssetsLoader*, + const ResPaths* paths, + const std::string& file, + const std::string& name, const std::shared_ptr& settings ); } diff --git a/src/coders/json.cpp b/src/coders/json.cpp index dbe42b5a..b3b3d1ea 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -238,11 +238,11 @@ Value Parser::parseValue() { throw error("unexpected character '"+std::string({next})+"'"); } -dynamic::Map_sptr json::parse(const std::string& filename, const std::string& source) { +dynamic::Map_sptr json::parse(std::string_view filename, std::string_view source) { Parser parser(filename, source); return parser.parse(); } -dynamic::Map_sptr json::parse(const std::string& source) { +dynamic::Map_sptr json::parse(std::string_view source) { return parse("", source); } diff --git a/src/coders/json.hpp b/src/coders/json.hpp index f4379fdc..7dc7b92e 100644 --- a/src/coders/json.hpp +++ b/src/coders/json.hpp @@ -9,8 +9,8 @@ #include namespace json { - dynamic::Map_sptr parse(const std::string& filename, const std::string& source); - dynamic::Map_sptr parse(const std::string& source); + dynamic::Map_sptr parse(std::string_view filename, std::string_view source); + dynamic::Map_sptr parse(std::string_view source); std::string stringify( const dynamic::Map* obj, diff --git a/src/constants.hpp b/src/constants.hpp index 76c13a74..022fa955 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -50,5 +50,6 @@ inline const std::string FONTS_FOLDER = "fonts"; inline const std::string LAYOUTS_FOLDER = "layouts"; inline const std::string SOUNDS_FOLDER = "sounds"; inline const std::string MODELS_FOLDER = "models"; +inline const std::string RIGS_FOLDER = "rigs"; #endif // CONSTANTS_HPP_ diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 512f7213..9df28497 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -36,7 +36,8 @@ entityid_t Entities::spawn(EntityDef& def, glm::vec3 pos, dynamic::Value args) { auto id = nextID++; registry.emplace(entity, static_cast(id), def); registry.emplace(entity, pos, size, glm::mat3(1.0f)); - auto& body = registry.emplace(entity, true, Hitbox {pos, def.hitbox}, std::vector{}); + auto& body = registry.emplace( + entity, true, Hitbox {pos, def.hitbox}, std::vector{}); for (auto& box : def.triggers) { body.triggers.emplace_back(Trigger{true, id, box, AABB{}, {}, {}, [=](auto entityid, auto index, auto otherid) { @@ -140,7 +141,8 @@ void Entities::updatePhysics(float delta) { hitbox.linearDamping = hitbox.grounded * 24; transform.setPos(hitbox.position); if (hitbox.grounded && !grounded) { - scripting::on_entity_grounded(*get(eid.uid), glm::length(prevVel-hitbox.velocity)); + scripting::on_entity_grounded( + *get(eid.uid), glm::length(prevVel-hitbox.velocity)); } if (!hitbox.grounded && grounded) { scripting::on_entity_fall(*get(eid.uid)); diff --git a/src/objects/Entities.hpp b/src/objects/Entities.hpp index 91fe106c..0e0982a3 100644 --- a/src/objects/Entities.hpp +++ b/src/objects/Entities.hpp @@ -77,7 +77,12 @@ class Entity { entt::registry& registry; const entt::entity entity; public: - Entity(Entities& entities, entityid_t id, entt::registry& registry, const entt::entity entity) + Entity( + Entities& entities, + entityid_t id, + entt::registry& registry, + const entt::entity entity + ) : entities(entities), id(id), registry(registry), entity(entity) {} EntityId& getID() const { diff --git a/src/objects/EntityDef.hpp b/src/objects/EntityDef.hpp index 3db361b0..5c4293c0 100644 --- a/src/objects/EntityDef.hpp +++ b/src/objects/EntityDef.hpp @@ -8,6 +8,10 @@ #include "../typedefs.hpp" #include "../maths/aabb.hpp" +namespace rigging { + class RigConfig; +} + struct EntityDef { /// @brief Entity string id (with prefix included) std::string const name; @@ -15,9 +19,11 @@ struct EntityDef { std::string scriptName = name.substr(name.find(':')+1); glm::vec3 hitbox {0.5f}; std::vector triggers {}; + std::string rigName = name.substr(name.find(":")+1); struct { entityid_t id; + rigging::RigConfig* rig; } rt {}; EntityDef(const std::string& name) : name(name) {} diff --git a/src/objects/rigging.cpp b/src/objects/rigging.cpp index 9b6cd14f..4d08548d 100644 --- a/src/objects/rigging.cpp +++ b/src/objects/rigging.cpp @@ -1,10 +1,89 @@ #include "rigging.hpp" +#include "../assets/Assets.hpp" +#include "../graphics/core/Model.hpp" +#include "../coders/json.hpp" + using namespace rigging; -RigNode::RigNode(size_t index, std::string name, std::vector> subnodes) - : index(index), name(std::move(name)), subnodes(std::move(subnodes)) { +RigNode::RigNode( + size_t index, + std::string name, + std::string model, + std::vector> subnodes) + : index(index), + name(std::move(name)), + modelName(model), + subnodes(std::move(subnodes)) +{} + +void RigNode::setModel(const Assets* assets, const std::string& name) { + modelName = name; + model = assets->get(name); } RigConfig::RigConfig(std::unique_ptr root) : root(std::move(root)) { } + +size_t RigConfig::update( + size_t index, + Rig& rig, + RigNode* node, + glm::mat4 matrix) +{ + rig.calculated.matrices[index] = matrix * rig.pose.matrices[index]; + index++; + for (auto& subnode : node->getSubnodes()) { + index = update(index, rig, subnode.get(), rig.calculated.matrices[index]); + } + return index; +} + +void RigConfig::update(Rig& rig, glm::mat4 matrix) { + update(0, rig, root.get(), matrix); +} + +void RigConfig::setup(const Assets* assets, RigNode* node) { + if (node == nullptr) { + setup(assets, root.get()); + } else { + node->setModel(assets, node->getModelName()); + for (auto& subnode : node->getSubnodes()) { + setup(assets, subnode.get()); + } + } +} + +static std::tuple> read_node( + dynamic::Map* root, size_t index +) { + std::string name; + std::string model; + root->str("name", name); + root->str("model", model); + std::vector> subnodes; + size_t count = 1; + if (auto nodesList = root->list("nodes")) { + for (size_t i = 0; i < nodesList->size(); i++) { + if (auto map = nodesList->map(i)) { + auto [subcount, subNode] = read_node(map, index+count); + subcount += count; + subnodes.push_back(std::move(subNode)); + } + } + } + return {index, std::make_unique(index, name, model, std::move(subnodes))}; +} + +std::unique_ptr RigConfig::parse( + std::string_view src, + std::string_view file +) { + auto root = json::parse(file, src); + auto rootNodeMap = root->map("root"); + if (rootNodeMap == nullptr) { + throw std::runtime_error("missing 'root' element"); + } + auto [count, rootNode] = read_node(root.get(), 0); + return std::make_unique(std::move(rootNode)); +} diff --git a/src/objects/rigging.hpp b/src/objects/rigging.hpp index 717aef87..ed084f27 100644 --- a/src/objects/rigging.hpp +++ b/src/objects/rigging.hpp @@ -9,6 +9,12 @@ #include #include +class Assets; + +namespace model { + struct Model; +} + namespace rigging { struct Rig; @@ -19,9 +25,21 @@ namespace rigging { class RigNode { size_t index; std::string name; + std::string modelName; std::vector> subnodes; + model::Model* model = nullptr; public: - RigNode(size_t index, std::string name, std::vector> subnodes); + RigNode( + size_t index, + std::string name, + std::string model, + std::vector> subnodes); + + void setModel(const Assets* assets, const std::string& name); + + const std::string& getModelName() const { + return modelName; + } size_t getIndex() const { return index; @@ -36,13 +54,28 @@ namespace rigging { std::unique_ptr root; std::unordered_map indices; std::vector nodes; + + size_t update( + size_t index, + Rig& rig, + RigNode* node, + glm::mat4 matrix); public: RigConfig(std::unique_ptr root); + + void update(Rig& rig, glm::mat4 matrix); + void setup(const Assets* assets, RigNode* node=nullptr); + + static std::unique_ptr parse( + std::string_view src, + std::string_view file + ); }; struct Rig { RigConfig* config; Pose pose; + Pose calculated; std::vector textures; }; };