From 56babcc8955a7f083631032a597d8e22c4c0007d Mon Sep 17 00:00:00 2001
From: Joseph Walton-Rivers <joseph@walton-rivers.uk>
Date: Tue, 2 Aug 2022 15:37:47 +0100
Subject: [PATCH] callback-based creation

---
 demo/demo/rollball.cpp                        | 51 ++++++++-----------
 include/fggl/entity/loader/loader.hpp         | 37 +++++++++++---
 .../include/fggl/phys/bullet/motion.hpp       |  2 +
 integrations/bullet/src/service.cpp           | 10 ++--
 integrations/bullet/src/simulation.cpp        |  5 ++
 5 files changed, 66 insertions(+), 39 deletions(-)

diff --git a/demo/demo/rollball.cpp b/demo/demo/rollball.cpp
index d3661ca..3ef9ec5 100644
--- a/demo/demo/rollball.cpp
+++ b/demo/demo/rollball.cpp
@@ -49,40 +49,33 @@ 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) {
-	{
-		auto northWall = factory->create(WallNPrefab, world);
-		auto& transform = world.get<fggl::math::Transform>(northWall);
-		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});
+		});
 
-	{
-		auto southWall = factory->create(WallNPrefab, world);
-		auto& transform = world.get<fggl::math::Transform>(southWall);
+	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});
-	}
+	});
 
-	{
-		auto westWall = factory->create(WallEPrefab, world);
-		auto& transform = world.get<fggl::math::Transform>(westWall);
+	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});
-	}
+	});
 
-	{
-		auto eastWall = factory->create(WallEPrefab, world);
-		auto& transform = world.get<fggl::math::Transform>(eastWall);
+	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});
-	}
+	});
 
-	{
-		auto floor = factory->create(floorPrefab, world);
-		auto& transform = world.get<fggl::math::Transform>(floor);
+	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});
-	}
+	});
 
-	{
-		// player just starts off as the prefab dictates
-		state.player = factory->create(playerPrefab, world);
-	}
+	// the player requires no special setup
+	state.player = factory->create(playerPrefab, world);
 
 	{
 		// collectables
@@ -95,10 +88,10 @@ static fggl::entity::EntityID setup_environment(fggl::entity::EntityManager& wor
 		// build the collectables
 		int collectCount = 0;
 		for (auto& pos : collectPos) {
-			auto collectable = factory->create(collectablePrefab, world);
-			auto& transform = world.get<fggl::math::Transform>(collectable);
-			transform.origin(pos);
-
+			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;
 		}
 	}
diff --git a/include/fggl/entity/loader/loader.hpp b/include/fggl/entity/loader/loader.hpp
index 47381dd..8fc47fa 100644
--- a/include/fggl/entity/loader/loader.hpp
+++ b/include/fggl/entity/loader/loader.hpp
@@ -34,32 +34,56 @@ 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;
+	};
+
 	class EntityFactory {
 		public:
 			constexpr static const modules::ModuleService service = modules::make_service("fggl::entity:Factory");
 
-			EntityID create(const EntityType& spec, EntityManager& manager) {
+			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);
-							m_factories.at(component)(data, manager, entity);
+
+							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));
+								debug::log(debug::Level::error, "EntityFactory: Unknown component factory type '{}'", fggl::util::guidToString(component));
 							#endif
 							manager.destroy(entity);
 							return fggl::entity::INVALID;
 						}
 					}
 
+					if (customise != nullptr) {
+						customise(manager, entity);
+					}
+
+					// finalise the entities
+					for( auto& finisher : finishers ) {
+						finisher(manager, entity);
+					}
+
 					return entity;
 				} catch (std::out_of_range& ex) {
 					#ifndef NDEBUG
@@ -74,8 +98,9 @@ namespace fggl::entity {
 			}
 
 			// ability to set and unset factory functions
-			inline void bind(const ComponentID& configNode, FactoryFunc factory) {
-				m_factories[configNode] = std::move(factory);
+			inline void bind(const ComponentID& configNode, FactoryFunc factory, CustomiseFunc finalise = nullptr) {
+				m_factories[configNode].factory = std::move(factory);
+				m_factories[configNode].finalise = std::move(finalise);
 			}
 
 			inline void unbind(const ComponentID& configNode) {
@@ -83,7 +108,7 @@ namespace fggl::entity {
 			}
 
 		private:
-			std::map<ComponentID, FactoryFunc> m_factories;
+			std::map<ComponentID, FactoryInfo> m_factories;
 			std::map<EntityType, EntitySpec> m_prototypes;
 	};
 
diff --git a/integrations/bullet/include/fggl/phys/bullet/motion.hpp b/integrations/bullet/include/fggl/phys/bullet/motion.hpp
index 07c1a25..9fa5160 100644
--- a/integrations/bullet/include/fggl/phys/bullet/motion.hpp
+++ b/integrations/bullet/include/fggl/phys/bullet/motion.hpp
@@ -45,6 +45,8 @@ namespace fggl::phys::bullet {
 			~FgglMotionState() override = default;
 
 			void getWorldTransform(btTransform& worldTrans) const override {
+				debug::trace("BT: syncing from transform: {}.", (uint64_t)m_entity);
+
 				const auto* transform = m_world->tryGet<fggl::math::Transform>(m_entity);
 				if ( transform == nullptr ) {
 					debug::warning("BT: attempted to get transform of entity without transform component.");
diff --git a/integrations/bullet/src/service.cpp b/integrations/bullet/src/service.cpp
index a91c777..c50d791 100644
--- a/integrations/bullet/src/service.cpp
+++ b/integrations/bullet/src/service.cpp
@@ -52,7 +52,7 @@ namespace fggl::phys::bullet {
 		}
 	}
 
-	void add_bt_body(BulletPhysicsEngine* engine, const entity::ComponentSpec& spec, entity::EntityManager& manager, const entity::EntityID& id) {
+	void add_bt_body(const entity::ComponentSpec& spec, entity::EntityManager& manager, const entity::EntityID& id) {
 		auto& body = manager.add<BulletBody>(id);
 		body.motion = new FgglMotionState(&manager, id);
 
@@ -78,11 +78,11 @@ namespace fggl::phys::bullet {
 			shape,
 			inertia
 		};
+
 		body.body = new btRigidBody(constructionInfo);
 		setup_body(type, *body.body);
 
 		// add the body to the simulation
-		engine->addBody(id, body);
 		if ( type == BodyType::DYNAMIC ) {
 			manager.add<phys::Dynamics>(id);
 		}
@@ -92,8 +92,10 @@ namespace fggl::phys::bullet {
 		auto* engine = new BulletPhysicsEngine(entityManager);
 
 		// FIXME: this won't work across multiple scenes - reactive approach might be better...
-		factory->bind(util::make_guid("phys::Body"), [engine](const entity::ComponentSpec& config, entity::EntityManager& manager, const entity::EntityID& id) {
-			add_bt_body(engine, config, manager, id);
+		factory->bind(util::make_guid("phys::Body"), add_bt_body, [engine](auto& manager, const auto& entity) {
+			debug::trace("adding entity {} to physics scene...", (uint64_t)entity);
+			auto& physComp = manager.template get<BulletBody>(entity);
+			engine->addBody(entity, physComp);
 		});
 		engine->debugDraw = true;
 
diff --git a/integrations/bullet/src/simulation.cpp b/integrations/bullet/src/simulation.cpp
index df589cb..20bd2fb 100644
--- a/integrations/bullet/src/simulation.cpp
+++ b/integrations/bullet/src/simulation.cpp
@@ -85,6 +85,11 @@ namespace fggl::phys::bullet {
 
 	void BulletPhysicsEngine::addBody(entity::EntityID /*entity*/, BulletBody &body) {
 		m_world->addRigidBody(body.body);
+
+		btMatrix4x4 transform;
+		body.body->getMotionState()->getWorldTransform(body.transform);
+
+		body.body->setWorldTransform(body.body->getMotionState()->)
 	}
 
 	inline btCollisionShape* shape_to_bullet(const phys::RigidBody& fgglBody) {
-- 
GitLab