From c963ab328c273c164d20b09818dcdf4570875b19 Mon Sep 17 00:00:00 2001 From: Joseph Walton-Rivers <joseph@walton-rivers.uk> Date: Thu, 4 Aug 2022 12:56:30 +0100 Subject: [PATCH] add hexboard-style inheritance to prototypes --- demo/data/rollball.yml | 25 ++++---- fggl/entity/loader/loader.cpp | 5 +- include/fggl/entity/loader/loader.hpp | 83 ++++++++++++++++++--------- include/fggl/entity/loader/spec.hpp | 9 ++- 4 files changed, 78 insertions(+), 44 deletions(-) diff --git a/demo/data/rollball.yml b/demo/data/rollball.yml index ea445d5..bdf74de 100644 --- a/demo/data/rollball.yml +++ b/demo/data/rollball.yml @@ -1,6 +1,14 @@ --- prefabs: + - name: "environment" + components: + gfx::material: + ambient: [0.0215, 0.1754, 0.0215] + diffuse: [1, 1, 1] + specular: [0.0633, 0.727811, 0.633] + shininess: 16 - name: "wallX" + parent: "environment" components: Transform: StaticMesh: @@ -8,11 +16,6 @@ prefabs: shape: type: box scale: [1.0, 5.0, 41] - gfx::material: - ambient: [0.0215, 0.1754, 0.0215] - diffuse: [1, 1, 1] - specular: [0.0633, 0.727811, 0.633] - shininess: 16 phys::Body: type: static shape: @@ -20,6 +23,7 @@ prefabs: extents: [0.5, 2.5, 20.5] # Wall Z shorter to avoid z-fighting - name: "wallZ" + parent: "environment" components: Transform: StaticMesh: @@ -27,17 +31,13 @@ prefabs: shape: type: box scale: [39, 5, 1] - gfx::material: - ambient: [0.0215, 0.1754, 0.0215] - diffuse: [1, 1, 1] - specular: [0.0633, 0.727811, 0.633] - shininess: 16 phys::Body: type: static shape: type: box extents: [ 19.5, 2.5, 0.5 ] - name: "floor" + parent: "environment" components: Transform: StaticMesh: @@ -45,11 +45,6 @@ prefabs: shape: type: box # we don't (currently) support planes... scale: [39, 0.5, 39] - gfx::material: - ambient: [0.0215, 0.1754, 0.0215] - diffuse: [1, 1, 1] - specular: [0.0633, 0.727811, 0.633] - shininess: 16 phys::Body: type: static shape: diff --git a/fggl/entity/loader/loader.cpp b/fggl/entity/loader/loader.cpp index fb29fa8..362e1cc 100644 --- a/fggl/entity/loader/loader.cpp +++ b/fggl/entity/loader/loader.cpp @@ -28,6 +28,7 @@ namespace fggl::entity { auto nodes = YAML::LoadAllFromFile( filePath->c_str() ); for (const auto& node : nodes) { auto prefabs = node["prefabs"]; + for ( const auto& prefab : prefabs ) { auto name = prefab["name"].as<fggl::util::GUID>(); @@ -35,8 +36,10 @@ namespace fggl::entity { debug::info("found prefab: {}", fggl::util::guidToString(name) ); #endif - // setup the components + // set up the components EntitySpec entity{}; + entity.parent = prefab["parent"].as<fggl::util::GUID>(NO_PARENT); + for (const auto& compEntry : prefab["components"]) { auto compId = compEntry.first.as<fggl::util::GUID>(); diff --git a/include/fggl/entity/loader/loader.hpp b/include/fggl/entity/loader/loader.hpp index 8fc47fa..598ee62 100644 --- a/include/fggl/entity/loader/loader.hpp +++ b/include/fggl/entity/loader/loader.hpp @@ -33,12 +33,8 @@ namespace fggl::entity { constexpr auto PROTOTYPE_ASSET = assets::AssetType::make("entity_prototype"); using FactoryFunc = std::function<void(const ComponentSpec& config, EntityManager&, const EntityID&)>; - using CustomiseFunc = std::function<void(EntityManager&, const EntityID&)>; - using ComponentID = util::GUID; - using EntityType = util::GUID; - struct FactoryInfo { FactoryFunc factory; CustomiseFunc finalise = nullptr; @@ -50,36 +46,21 @@ namespace fggl::entity { EntityID create(const EntityType& spec, EntityManager& manager, const CustomiseFunc& customise = nullptr) { try { - auto &prototype = m_prototypes.at(spec); - std::vector<CustomiseFunc> finishers; - // invoke each component factory as required - auto entity = manager.create(); - for ( auto& component : prototype.ordering ) { - try { - auto& data = prototype.components.at(component); - - auto& info = m_factories.at(component); - info.factory(data, manager, entity); - - if ( info.finalise != nullptr ) { - finishers.push_back(info.finalise); - } - } catch (std::out_of_range& ex) { - #ifndef NDEBUG - debug::log(debug::Level::error, "EntityFactory: Unknown component factory type '{}'", fggl::util::guidToString(component)); - #endif - manager.destroy(entity); - return fggl::entity::INVALID; - } + // set up the components for the entity + auto entity = setupComponents(spec, manager, finishers); + if ( entity == entity::INVALID ) { + debug::error("Error attempting to create entity with type {}", std::to_string(spec.get()) ); + return entity::INVALID; } + // allow the caller to perform any setup needed if (customise != nullptr) { customise(manager, entity); } - // finalise the entities + // finally, we run any cleanup/init setups required by the component factories for( auto& finisher : finishers ) { finisher(manager, entity); } @@ -110,6 +91,56 @@ namespace fggl::entity { private: std::map<ComponentID, FactoryInfo> m_factories; std::map<EntityType, EntitySpec> m_prototypes; + + entity::EntityID setupComponents(EntityType entityType, EntityManager& manager, std::vector<CustomiseFunc>& finishers) { + auto entity = manager.create(); + std::vector<ComponentID> loadedComps; + + auto currentType = entityType; + while ( currentType != NO_PARENT ) { + auto& entitySpec = m_prototypes.at(currentType); + + for ( auto& component : entitySpec.ordering ) { + // skip comps loaded by children + if (std::find(loadedComps.begin(), loadedComps.end(), component) != loadedComps.end()) { + continue; + } + + try { + auto& data = getComponent(entitySpec, component); + loadedComps.push_back(component); + + auto& info = m_factories.at(component); + info.factory(data, manager, entity); + + if ( info.finalise != nullptr ) { + finishers.push_back(info.finalise); + } + } catch (std::out_of_range& ex) { + #ifndef NDEBUG + debug::log(debug::Level::error, "EntityFactory: Unknown component factory type '{}'", fggl::util::guidToString(component)); + #endif + manager.destroy(entity); + return entity::INVALID; + } + } + currentType = entitySpec.parent; + } + + return entity; + } + + ComponentSpec& getComponent(EntitySpec& prototype, util::GUID compToken) { + auto compItr = prototype.components.find(compToken); + if ( compItr != prototype.components.end() ) { + return compItr->second; + } + + if ( prototype.parent == NO_PARENT ) { + throw std::out_of_range("EntityFactory: no such component!"); + } + return getComponent(m_prototypes.at(prototype.parent), compToken); + } }; assets::AssetRefRaw load_prototype(EntityFactory* factory, const assets::AssetGUID& guid, assets::AssetData data); diff --git a/include/fggl/entity/loader/spec.hpp b/include/fggl/entity/loader/spec.hpp index c9ef555..b5f50ee 100644 --- a/include/fggl/entity/loader/spec.hpp +++ b/include/fggl/entity/loader/spec.hpp @@ -26,6 +26,10 @@ namespace fggl::entity { + using ComponentID = util::GUID; + using EntityType = util::GUID; + constexpr EntityType NO_PARENT = util::make_guid("FGGL_NULL_PARENT"); + struct ComponentSpec { template<typename T> @@ -46,8 +50,9 @@ namespace fggl::entity { }; struct EntitySpec { - std::vector<util::GUID> ordering; - std::map<util::GUID, ComponentSpec> components; + EntityType parent = NO_PARENT; + std::vector<ComponentID> ordering; + std::map<ComponentID, ComponentSpec> components; }; } // namespace fggl::entity -- GitLab