From a4d9b349143e1d81d3957bbd0224837d94364cbd Mon Sep 17 00:00:00 2001
From: Joseph Walton-Rivers <joseph@walton-rivers.uk>
Date: Sun, 5 Jun 2022 00:38:22 +0100
Subject: [PATCH] clean transform - which somehow fixed the sphere

---
 demo/data/rollball.yml                 | 16 ++++---
 demo/demo/rollball.cpp                 | 41 ++++++++++------
 demo/include/rollball.hpp              | 11 ++++-
 fggl/phys/bullet/simulation.cpp        | 10 +++-
 include/fggl/ecs3/prototype/loader.hpp | 65 +++++++++++++++++++++-----
 include/fggl/math/types.hpp            | 65 ++++++++++++++++----------
 6 files changed, 149 insertions(+), 59 deletions(-)

diff --git a/demo/data/rollball.yml b/demo/data/rollball.yml
index 0a58e7f..ee508ba 100644
--- a/demo/data/rollball.yml
+++ b/demo/data/rollball.yml
@@ -6,7 +6,7 @@ prefabs:
       StaticMesh:
         pipeline: phong
         shape:
-          type: cube
+          type: box
           scale: [1.0, 5.0, 41]
       phys::Body:
         type: static
@@ -20,7 +20,7 @@ prefabs:
       StaticMesh:
         pipeline: phong
         shape:
-          type: cube
+          type: box
           scale: [39, 5, 1]
       phys::Body:
         type: static
@@ -33,13 +33,13 @@ prefabs:
       StaticMesh:
         pipeline: phong
         shape:
-          type: cube # we don't (currently) support planes...
-          scale: [40, 0.5, 40]
+          type: box # we don't (currently) support planes...
+          scale: [39, 0.5, 39]
       phys::Body:
         type: static
         shape:
           type: box # we don't (currently) support planes...
-          extents: [20, 0.25, 20]
+          extents: [19.5, 0.25, 19.5]
   - name: player
     components:
       Transform:
@@ -57,8 +57,10 @@ prefabs:
       StaticMesh:
         pipeline: phong
         shape:
-          type: cube
+          type: box
       phys::Body:
         type: kinematic
         shape:
-          type: box
\ No newline at end of file
+          type: box
+      phys::Callbacks:
+      phys::Cache:
\ No newline at end of file
diff --git a/demo/demo/rollball.cpp b/demo/demo/rollball.cpp
index 5d8851c..c4fb400 100644
--- a/demo/demo/rollball.cpp
+++ b/demo/demo/rollball.cpp
@@ -24,12 +24,14 @@
 #include "fggl/util/service.h"
 #include "fggl/ecs3/prototype/loader.hpp"
 
+#include <array>
+
 struct Prefabs {
 	fggl::ecs3::entity_t collectable;
 	fggl::ecs3::entity_t player;
 };
 
-static void setupPrefabs(fggl::ecs3::World& world, Prefabs& prefabs) {
+static void setup_prefabs(fggl::ecs3::World& world, Prefabs& prefabs) {
 
 	auto storage = fggl::util::ServiceLocator::instance().get<fggl::data::Storage>();
 	fggl::ecs3::load_prototype_file(world, *storage, "rollball.yml");
@@ -51,7 +53,7 @@ static void setupPrefabs(fggl::ecs3::World& world, Prefabs& prefabs) {
 
 }
 
-static void setupCamera(fggl::ecs3::World& world) {
+static void setup_camera(fggl::ecs3::World& world) {
 	auto prototype = world.create(false);
 
 	// setup camera position/transform
@@ -64,7 +66,7 @@ static void setupCamera(fggl::ecs3::World& world) {
 	world.add<fggl::gfx::Camera>(prototype);
 }
 
-static fggl::ecs3::entity_t setupEnvironment(fggl::ecs3::World& world, fggl::math::vec2& size) {
+static fggl::ecs3::entity_t setup_environment(fggl::ecs3::World& world, fggl::math::vec2& size, demo::RollState& state) {
 	{
 		auto northWall = world.createFromPrototype("wallX");
 		auto* transform = world.get<fggl::math::Transform>(northWall);
@@ -95,10 +97,9 @@ static fggl::ecs3::entity_t setupEnvironment(fggl::ecs3::World& world, fggl::mat
 		transform->origin({0.0f, -2.5f, 0.0f});
 	}
 
-	auto player = fggl::ecs3::NULL_ENTITY;
 	{
 		// player just starts off as the prefab dictates
-		player = world.createFromPrototype("player");
+		state.player = world.createFromPrototype("player");
 	}
 
 	{
@@ -110,14 +111,17 @@ static fggl::ecs3::entity_t setupEnvironment(fggl::ecs3::World& world, fggl::mat
 		}};
 
 		// build the collectables
+		int collectCount = 0;
 		for (auto& pos : collectPos) {
 			auto collectable = world.createFromPrototype("collectable");
 			auto* transform = world.get<fggl::math::Transform>(collectable);
 			transform->origin(pos);
+
+			state.collectables[collectCount++] = collectable;
 		}
 	}
 
-	return player;
+	return state.player;
 }
 
 
@@ -130,22 +134,22 @@ namespace demo {
 		Game::activate();
 
 		Prefabs prefabs{};
-		setupPrefabs(world(), prefabs);
+		setup_prefabs(world(), prefabs);
 
 		// collectable callbacks
 		auto* collectableCallbacks = world().get<fggl::phys::CollisionCallbacks>(prefabs.collectable);
 		collectableCallbacks->onEnter = [this](auto ourID, auto theirID) {
-			if ( theirID == player) {
+			if ( theirID == state.player) {
 				this->world().destroy(ourID);
 			}
 		};
 
 		// actual scene objects
-		setupCamera(world());
+		setup_camera(world());
 
 		// create a 20x20 grid
 		fggl::math::vec2 size{40.0f, 40.0f};
-		player = setupEnvironment(world(), size);
+		setup_environment(world(), size, state);
 
 	}
 
@@ -153,7 +157,7 @@ namespace demo {
 		Game::update();
 		auto& input = this->input();
 
-			if ( player != fggl::ecs3::NULL_ENTITY ) {
+			if ( state.player != fggl::ecs3::NULL_ENTITY ) {
 				auto &world = this->world();
 
 				const fggl::math::vec3 forward = fggl::math::FORWARD;
@@ -185,7 +189,7 @@ namespace demo {
 				if ( moved ) {
 					float forceFactor = 3.0F;
 					force = glm::normalize(force) * forceFactor;
-					auto *dynamics = world.get<fggl::phys::Dynamics>(player);
+					auto *dynamics = world.get<fggl::phys::Dynamics>(state.player);
 					dynamics->force += force;
 				}
 
@@ -193,12 +197,23 @@ namespace demo {
 					auto cameras = world.findMatching<fggl::gfx::Camera>();
 					fggl::ecs3::entity_t cam = cameras[0];
 					auto *camComp = world.get<fggl::gfx::Camera>(cam);
-					camComp->target = world.get<fggl::math::Transform>(player)->origin();
+					camComp->target = world.get<fggl::math::Transform>(state.player)->origin();
 
 					auto *camTransform = world.get<fggl::math::Transform>(cam);
 					camTransform->origin( camComp->target + cameraOffset );
 				}
 
+				// rotation
+				for ( auto& entity : state.collectables ) {
+					if ( world.alive(entity) ) {
+						auto* transform = world.get<fggl::math::Transform>(entity);
+
+						// rotate the cubes
+						fggl::math::vec3 angles{1.0F, 2.0F, 3.0F};
+						transform->rotateEuler( angles * (60.0F / 1000) );
+					}
+				}
+				state.time += (60.0f / 1000);
 		}
 	}
 
diff --git a/demo/include/rollball.hpp b/demo/include/rollball.hpp
index d1523de..de86c64 100644
--- a/demo/include/rollball.hpp
+++ b/demo/include/rollball.hpp
@@ -29,6 +29,15 @@
 
 namespace demo {
 
+	struct RollState {
+		fggl::ecs::entity_t player = fggl::ecs::NULL_ENTITY;
+		std::array<fggl::ecs3::entity_t, 3> collectables {
+			fggl::ecs::NULL_ENTITY,
+			fggl::ecs::NULL_ENTITY,
+			fggl::ecs::NULL_ENTITY };
+		float time = 0.0f;
+	};
+
 	class RollBall : public fggl::scenes::Game {
 
 		public:
@@ -40,7 +49,7 @@ namespace demo {
 			void render(fggl::gfx::Graphics& gfx) override;
 
 		private:
-			fggl::ecs3::entity_t player = fggl::ecs3::NULL_ENTITY;
+			RollState state;
 			fggl::math::vec3 cameraOffset = {-15.0F, 15.0F, 0.0F};
 	};
 
diff --git a/fggl/phys/bullet/simulation.cpp b/fggl/phys/bullet/simulation.cpp
index da1291a..e771e5d 100644
--- a/fggl/phys/bullet/simulation.cpp
+++ b/fggl/phys/bullet/simulation.cpp
@@ -159,6 +159,10 @@ namespace fggl::phys::bullet {
 			auto* transform = m_ecs->get<math::Transform>(ent);
 			auto* physBody = m_ecs->get<BulletBody>(ent);
 
+			if ( physBody->body->isKinematicObject() ) {
+				continue;
+			}
+
 			btTransform bTransform;
 			physBody->motion->getWorldTransform(bTransform);
 
@@ -169,8 +173,10 @@ namespace fggl::phys::bullet {
 
 			// set the rotation
 			const auto& btRot = bTransform.getRotation();
-			math::quat rot{btRot.x(), btRot.y(), btRot.z(), btRot.w()};
-			transform->setRotation(rot);
+
+			math::vec3 rot;
+			btRot.getEulerZYX(rot.x, rot.y, rot.z);
+			transform->euler(rot);
 		}
 	}
 
diff --git a/include/fggl/ecs3/prototype/loader.hpp b/include/fggl/ecs3/prototype/loader.hpp
index 10f3346..1a39331 100644
--- a/include/fggl/ecs3/prototype/loader.hpp
+++ b/include/fggl/ecs3/prototype/loader.hpp
@@ -86,6 +86,30 @@ namespace YAML {
 		}
 	};
 
+	template<>
+	struct convert<fggl::math::quat> {
+		static Node encode(const fggl::math::quat& rhs){
+			Node node;
+			node.push_back(rhs.x);
+			node.push_back(rhs.y);
+			node.push_back(rhs.z);
+			node.push_back(rhs.w);
+			return node;
+		}
+
+		static bool decode(const Node& node, fggl::math::quat& rhs) {
+			if ( !node.IsSequence() || node.size() != 3) {
+				return false;
+			}
+
+			rhs.x = node[0].as<float>();
+			rhs.y = node[1].as<float>();
+			rhs.z = node[2].as<float>();
+			rhs.w = node[3].as<float>();
+			return true;
+		}
+	};
+
 	template<>
 	struct convert<fggl::phys::BodyType> {
 		const static std::string TYPE_KINEMATIC;
@@ -124,10 +148,10 @@ namespace YAML {
 
 namespace fggl::ecs {
 
-	template<>
-	bool restore_config(math::Transform* comp, const YAML::Node& node) {
-		return true;
-	}
+	constexpr int DEFAULT_STACKS = 16;
+	constexpr int DEFAULT_SLICES = 16;
+	constexpr const char* SHAPE_SPHERE_VALUE{"sphere"};
+	constexpr const char* SHAPE_BOX_VALUE{"box"};
 
 	static void process_mesh(data::Mesh& out, const YAML::Node& shape) {
 		auto transform = data::OFFSET_NONE;
@@ -143,15 +167,23 @@ namespace fggl::ecs {
 
 		// now the shape itself
 		auto shapeType = shape["type"].as<std::string>();
-		if ( shapeType == "cube" ) {
+		if ( shapeType == SHAPE_BOX_VALUE ) {
 			data::make_cube(out, transform);
-		} else if ( shapeType == "sphere" ) {
-			int stacks = shape["stacks"].as<int>(16);
-			int slices = shape["slices"].as<int>(16);
+		} else if ( shapeType == SHAPE_SPHERE_VALUE ) {
+			int stacks = shape["stacks"].as<int>(DEFAULT_STACKS);
+			int slices = shape["slices"].as<int>(DEFAULT_SLICES);
 			data::make_sphere(out, transform, stacks, slices);
 		}
 	}
 
+	template<>
+	bool restore_config(math::Transform* comp, const YAML::Node& node) {
+		comp->origin(node["origin"].as<math::vec3>( math::VEC3_ZERO ));
+		comp->euler(node["rotation"].as<math::vec3>(math::VEC3_ZERO));
+		comp->scale(node["scale"].as<math::vec3>(math::VEC3_ONES));
+		return true;
+	}
+
 	template<>
 	bool restore_config(data::StaticMesh* meshComp, const YAML::Node& node) {
 		if ( !node["pipeline"] ) {
@@ -182,7 +214,6 @@ namespace fggl::ecs {
 		return true;
 	}
 
-
 	template<>
 	bool restore_config(phys::RigidBody* body, const YAML::Node& node) {
 		body->type = node["type"].as<fggl::phys::BodyType>(fggl::phys::BodyType::DYNAMIC);
@@ -196,11 +227,11 @@ namespace fggl::ecs {
 		if ( node["shape"] ) {
 			auto type = node["shape"]["type"].as< std::string >();
 
-			if (type == "box") {
+			if ( type == SHAPE_BOX_VALUE ) {
 				// assume unit box if extents are missing
 				auto extents = node["shape"]["extents"].as<math::vec3>(phys::UNIT_EXTENTS);
 				body->shape = new phys::Box(extents);
-			} else if (type == "sphere") {
+			} else if ( type == SHAPE_SPHERE_VALUE ) {
 				auto radius = node["shape"]["radius"].as<float>(0.5F);
 				body->shape = new phys::Sphere(radius);
 			} else {
@@ -211,6 +242,18 @@ namespace fggl::ecs {
 		return true;
 	}
 
+	template<>
+	bool restore_config(phys::CollisionCallbacks* callbacks, const YAML::Node& node) {
+		// TODO implement callback scripting
+		return true;
+	}
+
+	template<>
+	bool restore_config(phys::CollisionCache* cache, const YAML::Node& node){
+		// probably nothing needed here. Ideally, this should be automatically added when callbacks are.
+		return true;
+	}
+
 }
 
 #endif //FGGL_ECS3_PROTOTYPE_LOADER_HPP
diff --git a/include/fggl/math/types.hpp b/include/fggl/math/types.hpp
index 4b635c1..82bcfec 100644
--- a/include/fggl/math/types.hpp
+++ b/include/fggl/math/types.hpp
@@ -2,9 +2,11 @@
 #define FGGL_MATH_TYPES_H
 
 #include <tuple>
+#include <iostream>
 
 #include <glm/glm.hpp>
 #include <glm/ext/matrix_transform.hpp>
+#include <glm/gtx/transform.hpp>
 #include <glm/gtc/quaternion.hpp>
 #include <glm/gtx/quaternion.hpp>
 
@@ -46,6 +48,10 @@ namespace fggl::math {
 
 	constexpr static const math::vec2 VEC2_ZERO {0.0F, 0.0F};
 	constexpr static const math::vec3 VEC3_ZERO {0.0F, 0.0F, 0.0F};
+	constexpr static const math::vec3 VEC3_ONES {1.0F, 1.0F, 1.0F};
+
+	constexpr static const math::mat4 IDENTITY_M4 {1.0F};
+	constexpr static const math::quat IDENTITY_Q {1.0F, 0.0, 0.0, 0.0};
 
 	// fastFloor from OpenSimplex2
 	inline int fastFloor(double x) {
@@ -90,33 +96,35 @@ namespace fggl::math {
 	struct Transform {
 			constexpr static const char name[] = "Transform";
 
-			Transform() : m_local(1.0f), m_model(1.0f), m_origin(0.0f), m_rotation(1.0f, 0.0f, 0.0f, 0.0f) {
+			Transform() :
+				m_model(IDENTITY_M4),
+				m_origin(math::VEC3_ZERO),
+				m_euler(math::VEC3_ZERO),
+				m_scale(math::VEC3_ONES) {
 			}
 
 			// local reference vectors
 			[[nodiscard]]
 			inline vec3 up() const {
-				return vec4(UP, 1.0) * m_local;
+				return vec4(UP, 1.0) * model();
 			}
 
 			[[nodiscard]]
 			inline vec3 forward() const {
-				return vec4(FORWARD, 1.0) * m_local;
+				return vec4(FORWARD, 1.0) * model();
 			}
 
 			[[nodiscard]]
 			inline vec3 right() const {
-				return vec4(RIGHT, 1.0) * m_local;
+				return vec4(RIGHT, 1.0) * model();
 			}
 
 			inline void translate(const vec3 change) {
 				m_origin += change;
-				update();
 			}
 
 			inline void origin(const vec3 pos) {
 				m_origin = pos;
-				update();
 			}
 
 			[[nodiscard]]
@@ -124,44 +132,51 @@ namespace fggl::math {
 				return m_origin;
 			}
 
-			void setRotation(const math::quat& newRot) {
-				m_rotation = newRot;
-				update();
+			inline void scale(const vec3 scale) {
+				m_scale = scale;
+			}
+
+			[[nodiscard]]
+			inline vec3 scale() const {
+				return m_scale;
 			}
 
-			inline void rotate(math::vec3 axis, float angle) {
+			inline void rotate(math::vec3 axis, float radianAngle) {
 				// documentation claims this is in degrees, based on experimentation this actually appears to be radians...
-				m_rotation = glm::rotate(m_rotation, angle, axis);
-				update();
+				auto angles = axis * radianAngle;
+				m_euler += angles;
+			}
+
+			inline void rotateEuler(vec3 angles) {
+				m_euler += angles;
 			}
 
 			inline void euler(vec3 angles) {
-				m_rotation = quat(angles);
-				update();
+				m_euler = angles;
 			}
 
 			[[nodiscard]]
 			inline glm::vec3 euler() const {
-				return glm::eulerAngles(m_rotation);
+				return m_euler;
 			}
 
 			inline mat4 model() const {
-				return glm::translate(glm::mat4(1.0F), m_origin)
-					* glm::toMat4(m_rotation);
+				const glm::mat4 transformX = glm::rotate( math::IDENTITY_M4,glm::radians(m_euler.x), glm::vec3(1.0f, 0.0f, 0.0f) );
+				const glm::mat4 transformY = glm::rotate( math::IDENTITY_M4, glm::radians(m_euler.y), glm::vec3(0.0f, 1.0f, 0.0f) );
+				const glm::mat4 transformZ = glm::rotate( math::IDENTITY_M4, glm::radians(m_euler.z), glm::vec3(0.0f, 0.0f, 1.0f) );
+
+				const auto rotation = transformY * transformX * transformZ;
+				return glm::translate(math::IDENTITY_M4, m_origin)
+					* rotation
+					* glm::scale( math::IDENTITY_M4, m_scale );
 			}
 
 		private:
-			mat4 m_local; // us -> parent
 			mat4 m_model; // us -> world
 			vec3 m_origin;
-			quat m_rotation;
+			vec3 m_euler;
+			vec3 m_scale;
 
-			inline void update() {
-				mat4 t(1.0f);
-				t *= glm::toMat4(m_rotation);
-				t = glm::translate(t, m_origin);
-				m_local = t;
-			}
 	};
 
 }
-- 
GitLab