From e22424b7d71ef833d6ca6e61dd788b919fff6d6b Mon Sep 17 00:00:00 2001
From: Joseph Walton-Rivers <joseph@walton-rivers.uk>
Date: Sun, 24 Apr 2022 19:13:14 +0100
Subject: [PATCH] test scene using bullet physics

---
 demo/demo/rollball.cpp              | 22 +++++++----
 fggl/phys/bullet/simulation.cpp     | 57 ++++++++++++++++++++++++++---
 fggl/scenes/game.cpp                |  2 +
 include/fggl/math/types.hpp         |  9 +++++
 include/fggl/phys/bullet/bullet.hpp |  1 +
 include/fggl/phys/types.hpp         | 39 +++++++++++++++++++-
 6 files changed, 116 insertions(+), 14 deletions(-)

diff --git a/demo/demo/rollball.cpp b/demo/demo/rollball.cpp
index 4146ccd..24b50bf 100644
--- a/demo/demo/rollball.cpp
+++ b/demo/demo/rollball.cpp
@@ -46,6 +46,10 @@ static void setupPrefabs(fggl::ecs3::World& world, Prefabs& prefabs) {
 		auto transform = glm::scale(fggl::data::OFFSET_NONE, {1.0f, 5.f, 41.0f});
 		fggl::data::make_cube(meshComponent.mesh, transform );
 		world.set<fggl::data::StaticMesh>(prefabs.wallX, &meshComponent);
+
+		auto* rb = world.add<fggl::phys::RigidBody>(prefabs.wallX);
+		rb->mass = fggl::phys::MASS_STATIC; // flag the object as static
+		rb->shape = new fggl::phys::Box({0.5F, 2.5F, 20.5F});
 	}
 
 	{
@@ -60,6 +64,10 @@ static void setupPrefabs(fggl::ecs3::World& world, Prefabs& prefabs) {
 		auto transform = glm::scale(fggl::data::OFFSET_NONE, {39.f, 5.f, 1.0f});
 		fggl::data::make_cube(meshComponent.mesh, transform );
 		world.set<fggl::data::StaticMesh>(prefabs.wallZ, &meshComponent);
+
+		auto* rb = world.add<fggl::phys::RigidBody>(prefabs.wallZ);
+		rb->mass = fggl::phys::MASS_STATIC; // flag the object as static
+		rb->shape = new fggl::phys::Box({39.0F / 2, 2.5F, 0.5F});
 	}
 
 	{
@@ -67,18 +75,18 @@ static void setupPrefabs(fggl::ecs3::World& world, Prefabs& prefabs) {
 		prefabs.floor = world.create(true);
 		world.add<fggl::math::Transform>(prefabs.floor);
 
-		auto* rb = world.add<fggl::phys::RigidBody>(prefabs.floor);
-		rb->mass = fggl::phys::STATIC_MASS;
-
+		// create a procedural cube, but scale it to be plane-like
+		// TODO proper planes
 		fggl::data::StaticMesh meshComponent;
 		meshComponent.pipeline = "phong";
-
-		// create a procedural cube, but scale the model to be a long wall
 		auto transform = glm::scale(fggl::data::OFFSET_NONE, {40.f, 0.5f, 40.0f});
-
-		// TODO proper planes
 		fggl::data::make_cube(meshComponent.mesh, transform );
 		world.set<fggl::data::StaticMesh>(prefabs.floor, &meshComponent);
+
+		// physics: a static cube
+		auto* rb = world.add<fggl::phys::RigidBody>(prefabs.floor);
+		rb->mass = fggl::phys::MASS_STATIC; // flag the object as static
+		rb->shape = new fggl::phys::Box({20.0F, 0.5F, 20.0F});
 	}
 
 	{
diff --git a/fggl/phys/bullet/simulation.cpp b/fggl/phys/bullet/simulation.cpp
index bfd1f1e..381ac73 100644
--- a/fggl/phys/bullet/simulation.cpp
+++ b/fggl/phys/bullet/simulation.cpp
@@ -48,7 +48,33 @@ namespace fggl::phys::bullet {
 		auto myRot = myState->euler();
 		btQuaternion rotation;
 		rotation.setEulerZYX(myRot.x, myRot.y, myRot.z);
-		btState->motion = new btDefaultMotionState(btTransform(rotation, position));
+
+		btTransform transform;
+		transform.setIdentity();
+		transform.setRotation(rotation);
+		transform.setOrigin(position);
+
+		btState->motion = new btDefaultMotionState(transform);
+	}
+
+	inline btCollisionShape* shapeToBullet(const phys::RigidBody* fgglBody) {
+		if ( fgglBody->shape == nullptr ) {
+			// they forgot to put a shape, we'll assume a unit sphere to avoid crashes...
+			return new btSphereShape(0.5f);
+		}
+
+		switch (fgglBody->shape->type) {
+			case phys::ShapeType::BOX: {
+				auto extents = ((phys::Box*)fgglBody->shape)->extents;
+				return new btBoxShape({extents.x, extents.y, extents.z});
+			}
+			case phys::ShapeType::SPHERE: {
+				float radius = ((phys::Sphere*)fgglBody->shape)->radius;
+				return new btSphereShape(radius);
+			}
+			default:
+				return new btSphereShape(0.5f);
+		}
 	}
 
 	void BulletPhysicsEngine::checkForPhys() {
@@ -62,22 +88,38 @@ namespace fggl::phys::bullet {
 			}
 
 			btComp = m_ecs->add<BulletBody>(ent);
-			auto fgBody = m_ecs->get<phys::RigidBody>(ent);
+			const auto* fgBody = m_ecs->get<phys::RigidBody>(ent);
 
 			// setup the starting motion state
 			auto* transform = m_ecs->get<math::Transform>(ent);
 			build_motation_state(transform, btComp);
 
+			btCollisionShape* colShape = shapeToBullet(fgBody);
+			btVector3 localInt(0, 0, 0);
+			if ( !fgBody->isStatic() ) {
+				colShape->calculateLocalInertia(fgBody->mass, localInt);
+			}
+
 			// setup the construction information
-			// FIXME the world shouldn't be made only of unit spheres...
-			btSphereShape* shape = new btSphereShape(0.5f);
 			btRigidBody::btRigidBodyConstructionInfo bodyCI(
 				fgBody->mass,
 				btComp->motion,
-				shape
+				colShape,
+				localInt
 			);
 			btComp->body = new btRigidBody(bodyCI);
+
+			if (!fgBody->isStatic()) {
+				// FIXME this is for testing, needs to be removed
+				btComp->body->setRestitution(0.5f);
+				btComp->body->setRollingFriction(0.0f);
+			}
 			m_world->addRigidBody(btComp->body);
+
+			if ( !fgBody->isStatic() ) {
+				btComp->body->applyTorque({50.0f, 0.5f, 0.5f});
+				btComp->body->applyImpulse({5.0f, 0.5f, 0.5f}, {0.0f, 0.0f, 0.0f});
+			}
 		}
 	}
 
@@ -94,6 +136,11 @@ namespace fggl::phys::bullet {
 			auto& btPos = bTransform.getOrigin();
 			math::vec3 pos{btPos.x(), btPos.y(), btPos.z()};
 			transform->origin( pos );
+
+			// set the rotation
+			const auto& btRot = bTransform.getRotation();
+			math::quat rot{btRot.x(), btRot.y(), btRot.z(), btRot.w()};
+			transform->setRotation(rot);
 		}
 	}
 
diff --git a/fggl/scenes/game.cpp b/fggl/scenes/game.cpp
index 0cf1717..92aace6 100644
--- a/fggl/scenes/game.cpp
+++ b/fggl/scenes/game.cpp
@@ -38,6 +38,8 @@ namespace fggl::scenes {
 
 		// setup the scene
 		m_world = std::make_unique<ecs3::World>(*m_owner.registry());
+
+		// FIXME this ties bullet to the game state - which shouldn't be the case
 		m_phys = std::make_unique<phys::bullet::BulletPhysicsEngine>(m_world.get());
 	}
 
diff --git a/include/fggl/math/types.hpp b/include/fggl/math/types.hpp
index 70f2a22..4639d48 100644
--- a/include/fggl/math/types.hpp
+++ b/include/fggl/math/types.hpp
@@ -22,6 +22,7 @@ namespace fggl::math {
 	constexpr float HALF_PI = M_PI_2;
 	constexpr float TAU = PI * 2;
 
+
 	// math types (aliased for ease of use)
 	using vec4 = glm::vec4;
 	using vec4f = glm::vec4;
@@ -43,6 +44,9 @@ namespace fggl::math {
 	using mat4 = glm::mat4;
 	using quat = glm::quat;
 
+	constexpr static const math::vec2 VEC2_ZERO {0.0F, 0.0F};
+	constexpr static const math::vec3 VEC3_ZERO {0.0F, 0.0F, 0.0F};
+
 	// fastFloor from OpenSimplex2
 	inline int fastFloor(double x) {
 		int xi = (int) x;
@@ -120,6 +124,11 @@ namespace fggl::math {
 				return m_origin;
 			}
 
+			void setRotation(const math::quat& newRot) {
+				m_rotation = newRot;
+				update();
+			}
+
 			inline void rotate(math::vec3 axis, float angle) {
 				// documentation claims this is in degrees, based on experimentation this actually appears to be radians...
 				m_rotation = glm::rotate(m_rotation, angle, axis);
diff --git a/include/fggl/phys/bullet/bullet.hpp b/include/fggl/phys/bullet/bullet.hpp
index e5d301f..d377796 100644
--- a/include/fggl/phys/bullet/bullet.hpp
+++ b/include/fggl/phys/bullet/bullet.hpp
@@ -51,6 +51,7 @@ namespace fggl::phys::bullet {
 			void onLoad(ecs3::ModuleManager &manager, ecs3::TypeRegistry &types) override {
 				// dependencies
 				types.make<phys::RigidBody>();
+				types.make<phys::Dynamics>();
 
 				// my types
 				types.make<phys::bullet::BulletBody>();
diff --git a/include/fggl/phys/types.hpp b/include/fggl/phys/types.hpp
index 8ae73b6..4f06ade 100644
--- a/include/fggl/phys/types.hpp
+++ b/include/fggl/phys/types.hpp
@@ -26,11 +26,46 @@
 #define FGGL_PHYS_TYPES_HPP
 
 namespace fggl::phys {
-	constexpr float STATIC_MASS = 0.0F;
+	constexpr float MASS_STATIC = 0.0F;
+	constexpr float MASS_DEFAULT = 1.0F;
+
+	enum class ShapeType {
+			UNSET,
+			BOX,
+			SPHERE
+	};
+
+	struct PhyShape {
+		math::vec3 offset = math::VEC3_ZERO;
+		const ShapeType type;
+
+		inline PhyShape(ShapeType typ) : type(typ) {}
+	};
+
+	struct Box : public PhyShape {
+		math::vec3 extents;
+		explicit inline Box(math::vec3 ext) : PhyShape(ShapeType::BOX), extents(ext) {}
+	};
+
+	struct Sphere : public PhyShape {
+		float radius = 1.0F;
+		explicit inline Sphere(float rad) : PhyShape(ShapeType::SPHERE), radius(rad) {}
+	};
 
 	struct RigidBody {
 		constexpr static const char* name = "phys::Body";
-		float mass = 1.0F;
+		float mass = MASS_DEFAULT;
+		PhyShape* shape = nullptr;
+
+		[[nodiscard]]
+		inline bool isStatic() const {
+			return mass == MASS_STATIC;
+		}
+	};
+
+	struct Dynamics {
+		constexpr static const char* name = "phys::Dynamics";
+		math::vec3 force = math::VEC3_ZERO;
 	};
 
 	class PhysicsEngine {
-- 
GitLab