From 53e0b2f4ddb5e3887497a60fad458939da5cc358 Mon Sep 17 00:00:00 2001 From: Joseph Walton-Rivers <joseph@walton-rivers.uk> Date: Mon, 17 Oct 2022 13:57:19 +0100 Subject: [PATCH] allow loading of scene data from disk --- demo/data/rollball.yml | 6 ++- demo/demo/rollball.cpp | 50 ++------------------- demo/include/rollball.hpp | 6 +-- fggl/entity/loader/loader.cpp | 62 +++++++++++++++++++++++++++ fggl/entity/module.cpp | 6 ++- include/fggl/app.hpp | 8 ++++ include/fggl/assets/loader.hpp | 33 ++++++++------ include/fggl/assets/types.hpp | 2 +- include/fggl/entity/entity.hpp | 61 ++++++++++++++++++++++++-- include/fggl/entity/loader/loader.hpp | 19 +++++++- include/fggl/entity/loader/spec.hpp | 1 + include/fggl/modules/manager.hpp | 4 ++ include/fggl/scenes/game.hpp | 3 +- include/fggl/util/guid.hpp | 10 +++-- 14 files changed, 195 insertions(+), 76 deletions(-) diff --git a/demo/data/rollball.yml b/demo/data/rollball.yml index f9895a6..5c562bc 100644 --- a/demo/data/rollball.yml +++ b/demo/data/rollball.yml @@ -71,6 +71,8 @@ prefabs: type: sphere radius: 1 - name: rb_collectable + tags: + - "collectable" components: Transform: StaticMesh: @@ -119,6 +121,8 @@ scene: - prefab: rb_collectable components: Transform: - origin: [6, -0.5, 15] + origin: [6, -0.5, -15] + - prefab: rb_player + name: "player" scripts: - "rollball.lua" \ No newline at end of file diff --git a/demo/demo/rollball.cpp b/demo/demo/rollball.cpp index 39fb33b..0838fff 100644 --- a/demo/demo/rollball.cpp +++ b/demo/demo/rollball.cpp @@ -49,57 +49,12 @@ static void setup_camera(fggl::entity::EntityManager& world) { } static fggl::entity::EntityID setup_environment(fggl::entity::EntityManager& world, fggl::entity::EntityFactory* factory, const fggl::math::vec2& size, demo::RollState& state) { - factory->create(WallNPrefab, world, [size](auto& manager, const auto& entity) { - auto& transform = manager.template get<fggl::math::Transform>(entity); - transform.origin({size.x/2, 0.0F, 0.0F}); - }); - - factory->create(WallNPrefab, world, [size](auto& manager, const auto& entity) { - auto& transform = manager.template get<fggl::math::Transform>(entity); - transform.origin({-size.x/2, 0.0F, 0.0F}); - }); - - factory->create(WallEPrefab, world, [size](auto& manager, const auto& entity) { - auto& transform = manager.template get<fggl::math::Transform>(entity); - transform.origin({0.0F, 0.0F, -size.y/2}); - }); - - factory->create(WallEPrefab, world, [size](auto& manager, const auto& entity) { - auto& transform = manager.template get<fggl::math::Transform>(entity); - transform.origin({0.0F, 0.0F, size.y/2}); - }); - - factory->create(floorPrefab, world, [](auto& manager, const auto& entity) { - auto& transform = manager.template get<fggl::math::Transform>(entity); - transform.origin({0.0F, -2.5F, 0.0F}); - }); - - // the player requires no special setup - state.player = factory->create(playerPrefab, world); - - { - // collectables - std::array<fggl::math::vec3, 3> collectPos {{ - {-5.0f, -0.5f, 12.0f}, - {15.0f, -0.5f, 0.5f}, - {6.0f, -0.5f, -15.0f} - }}; - - // build the collectables - int collectCount = 0; - for (auto& pos : collectPos) { - auto collectable = factory->create(collectablePrefab, world, [pos](auto& manager, const auto& entity) { - auto& transform = manager.template get<fggl::math::Transform>(entity); - transform.origin(pos); - }); - state.collectables[collectCount++] = collectable; - } - } - // ensure the state is clean state.closestPickup = fggl::entity::INVALID; state.mode = demo::DebugMode::NORMAL; state.time = 0.0F; + state.player = world.findByName("player"); + state.collectables = world.findByTag("collectable"); return state.player; } @@ -131,6 +86,7 @@ namespace demo { // asset loader auto* assetLoader = m_owner.service<fggl::assets::Loader>(); assetLoader->load("rollball.yml", fggl::entity::PROTOTYPE_ASSET); + assetLoader->load("rollball.yml", fggl::entity::SCENE, this); // collectable callbacks /*auto* collectableCallbacks = world().get<fggl::phys::CollisionCallbacks>(prefabs.collectable); diff --git a/demo/include/rollball.hpp b/demo/include/rollball.hpp index a470bfc..ef757f1 100644 --- a/demo/include/rollball.hpp +++ b/demo/include/rollball.hpp @@ -41,11 +41,7 @@ namespace demo { fggl::entity::EntityID player = fggl::entity::INVALID; fggl::entity::EntityID closestPickup; DebugMode mode = DebugMode::NORMAL; - - std::array<fggl::entity::EntityID, 3> collectables { - fggl::entity::INVALID, - fggl::entity::INVALID, - fggl::entity::INVALID }; + std::vector<fggl::entity::EntityID> collectables; float time = 0.0f; }; diff --git a/fggl/entity/loader/loader.cpp b/fggl/entity/loader/loader.cpp index c38715b..4baa790 100644 --- a/fggl/entity/loader/loader.cpp +++ b/fggl/entity/loader/loader.cpp @@ -18,9 +18,65 @@ #include "fggl/entity/loader/loader.hpp" #include "fggl/debug/logging.hpp" +#include "fggl/scenes/game.hpp" namespace fggl::entity { + assets::AssetRefRaw load_scene(assets::Loader* loader, const assets::AssetGUID& asset, assets::AssetData data, void* ptr) { + auto *filePath = std::get<assets::AssetPath *>(data); + scenes::Game* gamePtr = (scenes::Game*)ptr; + + // load assets + auto* entityFactory = gamePtr->owner().service<EntityFactory>(); + + auto nodes = YAML::LoadAllFromFile(filePath->c_str()); + for (const auto& node : nodes) { + auto scene = node["scene"]; + if ( !scene ) { + debug::warning("no scene node in YAML file..."); + return nullptr; + } + + // create and personalize + for (const auto& item : scene) { + + // only safe if personalize is called BEFORE end of loop itr + auto personalize = [&entityFactory, &item, gamePtr](EntityManager& manager, const EntityID eid) { + + for ( const auto& compConfig : item["components"]) { + auto compName = compConfig.first.as<fggl::util::GUID>(); + auto& compInfo = entityFactory->getInfo(compName); + + // setup config data + ComponentSpec spec; + spec.config = compConfig.second; + + // re-run the factory with the modified arguments (might need to support a 'patch' function...) + compInfo.factory( spec, manager, eid, gamePtr->owner().services()); + } + }; + + // finally, load the entity and trigger personalize + auto prefab = item["prefab"].as<fggl::util::GUID>(); + auto entity = entityFactory->create(prefab, gamePtr->world(), personalize); + + // metadata + if ( item["name"].IsDefined() ) { + gamePtr->world().setName(item["name"].as<std::string>(), entity); + } + + if ( item["tags"].IsDefined() ) { + for ( auto tag : item["tags"] ) { + auto tagGuid = tag.as<fggl::util::GUID>(); + gamePtr->world().addTag(entity, tagGuid); + } + } + } + } + + return nullptr; + } + assets::AssetRefRaw load_prototype(assets::Loader* loader, EntityFactory *factory, const assets::AssetGUID &guid, assets::AssetData data) { auto *filePath = std::get<assets::AssetPath *>(data); @@ -48,6 +104,12 @@ namespace fggl::entity { debug::trace("prefab {} has component {}", name, compId); } + if ( prefab["tags"].IsDefined() ) { + for ( auto& tagNode : prefab["tags"] ) { + entity.tags.push_back( tagNode.as< util::GUID >() ); + } + } + factory->define(name, entity); } } diff --git a/fggl/entity/module.cpp b/fggl/entity/module.cpp index 752b108..d6447e4 100644 --- a/fggl/entity/module.cpp +++ b/fggl/entity/module.cpp @@ -18,11 +18,14 @@ #include "fggl/entity/module.hpp" #include "fggl/math/types.hpp" +#include "fggl/scenes/game.hpp" namespace fggl::entity { void make_transform(const entity::ComponentSpec &spec, EntityManager &manager, const entity::EntityID entity, modules::Services &/*svc*/) { auto &transform = manager.add<math::Transform>(entity); + + //FIXME won't work for patching! transform.origin(spec.get<math::vec3>("origin", math::VEC3_ZERO)); transform.euler(spec.get<math::vec3>("rotation", math::VEC3_ZERO)); transform.scale(spec.get<math::vec3>("scale", math::VEC3_ONES)); @@ -41,9 +44,10 @@ namespace fggl::entity { // we are responsible for prefabs... auto *assetLoader = services.get<assets::Loader>(); - assetLoader->setFactory(PROTOTYPE_ASSET, [factory](assets::Loader* loader, const assets::AssetGUID &a, assets::AssetData b) { + assetLoader->setFactory(PROTOTYPE_ASSET, [factory](assets::Loader* loader, const assets::AssetGUID &a, assets::AssetData b, void* ptr) { return load_prototype(loader, factory, a, b); }, assets::LoadType::PATH); + assetLoader->setFactory(SCENE, load_scene, assets::LoadType::PATH); return true; } diff --git a/include/fggl/app.hpp b/include/fggl/app.hpp index a6c6a90..b50ada7 100644 --- a/include/fggl/app.hpp +++ b/include/fggl/app.hpp @@ -76,6 +76,10 @@ namespace fggl { virtual void deactivate() {} + inline App& owner() { + return m_owner; + } + protected: App &m_owner; }; @@ -128,6 +132,10 @@ namespace fggl { } } + inline modules::Services& services() { + return m_subsystems->services(); + } + inline bool running() const { return m_running; } diff --git a/include/fggl/assets/loader.hpp b/include/fggl/assets/loader.hpp index 48fd853..b58f506 100644 --- a/include/fggl/assets/loader.hpp +++ b/include/fggl/assets/loader.hpp @@ -68,21 +68,28 @@ namespace fggl::assets { m_requests.push(ResourceRequest{guid, type}); } - void load(const AssetGUID &guid, const AssetType &type) { + void load(const AssetGUID &guid, const AssetType &type, void* userPtr = nullptr) { auto path = m_storage->resolvePath(data::StorageType::Data, guid); - auto &config = m_factories.at(type); - switch (config.second) { - case LoadType::DIRECT: - // TODO we load the data into main memory and give a pointer to it. - debug::log(debug::Level::error, "Tried to load direct asset - no one wrote that yet!"); - break; - case LoadType::STAGED: - // TODO we load the data into temp memory and give a pointer to it. - debug::log(debug::Level::error, "Tried to load staged asset - no one wrote that yet!"); - break; - case LoadType::PATH: config.first(this, guid, AssetData(&path)); - break; + auto factoryItr = m_factories.find(type); + if ( factoryItr == m_factories.end() ) { + debug::error("attempted to load asset with unknown type: {}", type.get()); + return; + } + + const auto& [callback, callbackType] = factoryItr->second; + switch (callbackType) { + case LoadType::DIRECT: + // TODO we load the data into main memory and give a pointer to it. + debug::log(debug::Level::error, "Tried to load direct asset - no one wrote that yet!"); + break; + case LoadType::STAGED: + // TODO we load the data into temp memory and give a pointer to it. + debug::log(debug::Level::error, "Tried to load staged asset - no one wrote that yet!"); + break; + case LoadType::PATH: + callback(this, guid, AssetData(&path), userPtr); + break; } } diff --git a/include/fggl/assets/types.hpp b/include/fggl/assets/types.hpp index 7d84ddf..4b93825 100644 --- a/include/fggl/assets/types.hpp +++ b/include/fggl/assets/types.hpp @@ -48,7 +48,7 @@ namespace fggl::assets { class Loader; using AssetData = std::variant<MemoryBlock, AssetPath *, FILE *>; - using Checkin = std::function<AssetRefRaw(Loader* loader, const AssetGUID &, const AssetData &)>; + using Checkin = std::function<AssetRefRaw(Loader* loader, const AssetGUID &, const AssetData &, void* userPtr)>; } #endif //FGGL_ASSETS_TYPES_HPP diff --git a/include/fggl/entity/entity.hpp b/include/fggl/entity/entity.hpp index 0843299..cb1527e 100644 --- a/include/fggl/entity/entity.hpp +++ b/include/fggl/entity/entity.hpp @@ -20,6 +20,8 @@ #define FGGL_ENTITY_ENTITY_HPP #include <cstdint> +#include <map> +#include <vector> #include "fggl/debug/logging.hpp" #include "fggl/vendor/entt.hpp" @@ -41,7 +43,7 @@ namespace fggl::entity { template<typename Component, typename... Args> inline Component &add(EntityID entity, Args &&... args) { - return m_registry.emplace<Component>(entity, std::forward<Args>(args)...); + return m_registry.get_or_emplace<Component>(entity, std::forward<Args>(args)...); } template<typename Component> @@ -75,21 +77,74 @@ namespace fggl::entity { return m_registry.view<Components...>(); } + EntityID findByName(const std::string& name) const { + auto itr = m_names.find(name); + if ( itr == m_names.end() ){ + return INVALID; + } + return m_registry.valid(itr->second) ? itr->second : INVALID; + } + + inline void setName(const std::string& name, EntityID eid ) { + if ( eid == INVALID ) { + m_names.erase(name); + } else { + assert(m_registry.valid(eid)); + m_names[name] = eid; + } + } + template<typename ...Components> bool has(EntityID idx) const { return m_registry.template all_of<Components...>(idx); } - inline bool exists(EntityID idx) { + bool hasTag(EntityID entity, fggl::util::GUID tag) { + const auto mapItr = m_tags.find( tag ); + if ( mapItr == m_tags.end() || !m_registry.valid(entity) ) { + return false; + } + return std::find(mapItr->second.begin(), mapItr->second.end(), entity) != mapItr->second.end(); + } + + void addTag(const EntityID entity, const fggl::util::GUID tag) { + assert( m_registry.valid(entity) ); + auto& tagged = m_tags[ tag ]; + tagged.push_back( entity ); + } + + void removeTag(const EntityID entity, const fggl::util::GUID tag) { + auto mapItr = m_tags.find(tag); + if ( mapItr == m_tags.end() ) { + return; + } + std::remove_if( mapItr->second.begin(), mapItr->second.end(), [entity](auto other) { return other == entity;} ); + } + + inline std::vector<EntityID> findByTag(const char* tag) { + return findByTag( util::make_guid_rt(tag) ); + } + + std::vector<EntityID> findByTag(const fggl::util::GUID tag){ + auto mapItr = m_tags.find(tag); + if ( mapItr == m_tags.end() ) { + return {}; + } + return mapItr->second; + } + + inline bool exists(EntityID idx) const { return m_registry.valid(idx); } - inline bool alive(EntityID idx) { + inline bool alive(EntityID idx) const { return m_registry.valid(idx); } private: entt::registry m_registry; + std::map<std::string, EntityID> m_names; + std::map<fggl::util::GUID, std::vector<EntityID>> m_tags; }; struct Entity { diff --git a/include/fggl/entity/loader/loader.hpp b/include/fggl/entity/loader/loader.hpp index a234936..9a816dd 100644 --- a/include/fggl/entity/loader/loader.hpp +++ b/include/fggl/entity/loader/loader.hpp @@ -32,6 +32,8 @@ namespace fggl::entity { constexpr auto PROTOTYPE_ASSET = assets::AssetType::make("entity_prototype"); + constexpr auto SCENE = assets::AssetType::make("entity_scene"); + using FactoryFunc = std::function<void(const ComponentSpec &config, EntityManager &, const EntityID &, modules::Services &svc)>; using CustomiseFunc = std::function<void(EntityManager &, const EntityID &)>; @@ -66,12 +68,22 @@ namespace fggl::entity { finisher( manager, entity ); } + // use metadata to finalise + processTags(manager, entity, spec); + return entity; } + void processTags(EntityManager& manager, EntityID id, EntityType spec) { + auto type = m_prototypes.at(spec); + for ( auto& tag : type.tags ) { + manager.addTag(id, tag); + } + } + void log_known_types() const { debug::debug("dumping known types:"); - for(auto& [k,v] : m_factories) { + for(const auto& [k,v] : m_factories) { debug::debug("\ttype: {}", k); } } @@ -90,6 +102,10 @@ namespace fggl::entity { m_factories.erase(configNode); } + inline FactoryInfo& getInfo(ComponentID comp) { + return m_factories.at(comp); + } + private: modules::Services m_services; std::map<ComponentID, FactoryInfo> m_factories; @@ -158,6 +174,7 @@ namespace fggl::entity { }; assets::AssetRefRaw load_prototype(assets::Loader* loader, EntityFactory *factory, const assets::AssetGUID &guid, assets::AssetData data); + assets::AssetRefRaw load_scene(assets::Loader* loader, const assets::AssetGUID& asset, assets::AssetData data, void* ptr); } // namespace fggl::entity diff --git a/include/fggl/entity/loader/spec.hpp b/include/fggl/entity/loader/spec.hpp index 749fa4c..08190b9 100644 --- a/include/fggl/entity/loader/spec.hpp +++ b/include/fggl/entity/loader/spec.hpp @@ -51,6 +51,7 @@ namespace fggl::entity { struct EntitySpec { EntityType parent = NO_PARENT; + std::vector<util::GUID> tags; std::vector<ComponentID> ordering; std::map<ComponentID, ComponentSpec> components; diff --git a/include/fggl/modules/manager.hpp b/include/fggl/modules/manager.hpp index deb5f14..61fa6e0 100644 --- a/include/fggl/modules/manager.hpp +++ b/include/fggl/modules/manager.hpp @@ -235,6 +235,10 @@ namespace fggl::modules { m_locked = true; } + inline Services& services() { + return m_services; + } + private: bool m_locked = false; Services m_services; diff --git a/include/fggl/scenes/game.hpp b/include/fggl/scenes/game.hpp index 1a451d3..21f35cf 100644 --- a/include/fggl/scenes/game.hpp +++ b/include/fggl/scenes/game.hpp @@ -58,11 +58,12 @@ namespace fggl::scenes { void update(float dt) override; void render(fggl::gfx::Graphics &gfx) override; - protected: inline auto world() -> entity::EntityManager & { return *m_world; } + + protected: bool hasPhys() const { return m_phys != nullptr; } diff --git a/include/fggl/util/guid.hpp b/include/fggl/util/guid.hpp index 2b65560..03b52b5 100644 --- a/include/fggl/util/guid.hpp +++ b/include/fggl/util/guid.hpp @@ -77,14 +77,18 @@ namespace fggl::util { return GUID(hash_fnv1a_64(str)); } - inline GUID make_guid_rt(const std::string &str) { + inline GUID make_guid_rt(const char* str) { #ifndef NDEBUG - return internString(str.c_str()); + return internString(str); #else - return make_guid(str.c_str()); + return make_guid(str); #endif } + inline GUID make_guid_rt(const std::string &str) { + return make_guid_rt(str.c_str()); + } + } // namespace fggl::util // formatter -- GitLab