From 9dccd2988b725b3ba96faf719f9af419e42f5ba3 Mon Sep 17 00:00:00 2001 From: Joseph Walton-Rivers <joseph@walton-rivers.uk> Date: Sun, 24 Apr 2022 18:05:08 +0100 Subject: [PATCH] add basic support for bullet integration --- CMakeLists.txt | 1 + demo/demo/main.cpp | 2 + demo/demo/rollball.cpp | 4 + fggl/CMakeLists.txt | 3 + fggl/data/heightmap.cpp | 2 +- fggl/phys/bullet/CMakeLists.txt | 11 +++ fggl/phys/bullet/simulation.cpp | 121 ++++++++++++++++++++++++++++ fggl/scenes/game.cpp | 9 ++- include/fggl/ecs3/prototype/world.h | 19 +++++ include/fggl/ecs3/types.hpp | 7 +- include/fggl/phys/bullet/bullet.hpp | 70 ++++++++++++++++ include/fggl/phys/bullet/types.hpp | 82 +++++++++++++++++++ include/fggl/phys/types.hpp | 52 ++++++++++++ include/fggl/scenes/game.hpp | 2 + 14 files changed, 382 insertions(+), 3 deletions(-) create mode 100644 fggl/phys/bullet/CMakeLists.txt create mode 100644 fggl/phys/bullet/simulation.cpp create mode 100644 include/fggl/phys/bullet/bullet.hpp create mode 100644 include/fggl/phys/bullet/types.hpp create mode 100644 include/fggl/phys/types.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 18f5917..e1527a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) glm/0.9.9.8 spdlog/1.10.0 freetype/2.11.1 + bullet3/3.22a GENERATORS cmake_find_package cmake_find_package_multi diff --git a/demo/demo/main.cpp b/demo/demo/main.cpp index 8d025d8..45846b3 100644 --- a/demo/demo/main.cpp +++ b/demo/demo/main.cpp @@ -33,6 +33,7 @@ #include "fggl/util/service.h" #include "fggl/ecs3/types.hpp" +#include "fggl/phys/bullet/bullet.hpp" #include "fggl/scenes/menu.hpp" @@ -98,6 +99,7 @@ int main(int argc, const char* argv[]) { // load a bunch of modules to provide game functionality //app.use<fggl::ecs3::ecsTypes>(); app.use<fggl::gfx::SceneUtils>(); + app.use<fggl::phys::Bullet3>(); test_atlas_api(); diff --git a/demo/demo/rollball.cpp b/demo/demo/rollball.cpp index 49396f6..4146ccd 100644 --- a/demo/demo/rollball.cpp +++ b/demo/demo/rollball.cpp @@ -67,6 +67,9 @@ 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; + fggl::data::StaticMesh meshComponent; meshComponent.pipeline = "phong"; @@ -82,6 +85,7 @@ static void setupPrefabs(fggl::ecs3::World& world, Prefabs& prefabs) { // player (cube because my sphere function doesn't exist yet prefabs.player = world.create(true); world.add<fggl::math::Transform>(prefabs.player); + world.add<fggl::phys::RigidBody>(prefabs.player); fggl::data::StaticMesh meshComponent; meshComponent.pipeline = "phong"; diff --git a/fggl/CMakeLists.txt b/fggl/CMakeLists.txt index 486ce21..0d34492 100644 --- a/fggl/CMakeLists.txt +++ b/fggl/CMakeLists.txt @@ -70,6 +70,9 @@ target_link_libraries(${PROJECT_NAME} PUBLIC freetype) # Graphics backend add_subdirectory(gfx) +# physics integration +add_subdirectory(phys/bullet) + # Debug backend add_subdirectory(debug) diff --git a/fggl/data/heightmap.cpp b/fggl/data/heightmap.cpp index 72b9b78..525e886 100644 --- a/fggl/data/heightmap.cpp +++ b/fggl/data/heightmap.cpp @@ -99,7 +99,7 @@ namespace fggl::data { for (std::size_t x = 0; x < data::heightMaxX - 1; x++) { for (std::size_t z = 0; z < data::heightMaxZ; z++) { for (int k=0; k < 2; k++) { - int idx = (x+k) * data::heightMaxZ + z; + auto idx = (x+k) * data::heightMaxZ + z; mesh.pushIndex(idx); } } diff --git a/fggl/phys/bullet/CMakeLists.txt b/fggl/phys/bullet/CMakeLists.txt new file mode 100644 index 0000000..54d35e5 --- /dev/null +++ b/fggl/phys/bullet/CMakeLists.txt @@ -0,0 +1,11 @@ +# bullet integration support + +find_package( Bullet REQUIRED ) +target_link_libraries(fggl PUBLIC Bullet::Bullet ) + +# +target_sources(fggl + PRIVATE + simulation.cpp +) + diff --git a/fggl/phys/bullet/simulation.cpp b/fggl/phys/bullet/simulation.cpp new file mode 100644 index 0000000..bfd1f1e --- /dev/null +++ b/fggl/phys/bullet/simulation.cpp @@ -0,0 +1,121 @@ +/* + * ${license.title} + * Copyright (C) 2022 ${license.owner} + * ${license.mailto} + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "fggl/phys/bullet/types.hpp" + +namespace fggl::phys::bullet { + + BulletPhysicsEngine::BulletPhysicsEngine(ecs3::World* world) : m_ecs(world), m_config(), m_world(nullptr) { + m_config.broadphase = new btDbvtBroadphase(); + m_config.collisionConfiguration = new btDefaultCollisionConfiguration(); + m_config.dispatcher = new btCollisionDispatcher(m_config.collisionConfiguration); + m_config.solver = new btSequentialImpulseConstraintSolver(); + + m_world = new btDiscreteDynamicsWorld( + m_config.dispatcher, + m_config.broadphase, + m_config.solver, + m_config.collisionConfiguration); + } + + void BulletPhysicsEngine::step() { + checkForPhys(); + m_world->stepSimulation(60.0f); + syncToECS(); + } + + static void build_motation_state(math::Transform* myState, BulletBody* btState) { + auto myPos = myState->origin(); + btVector3 position{myPos.x, myPos.y, myPos.z}; + + auto myRot = myState->euler(); + btQuaternion rotation; + rotation.setEulerZYX(myRot.x, myRot.y, myRot.z); + btState->motion = new btDefaultMotionState(btTransform(rotation, position)); + } + + void BulletPhysicsEngine::checkForPhys() { + // FIXME without reactive-based approaches this is very slow + auto entsWantPhys = m_ecs->findMatching<phys::RigidBody>(); + for (auto ent : entsWantPhys) { + auto* btComp = m_ecs->get<BulletBody>(ent); + if ( btComp != nullptr ) { + // they are already in the simluation + continue; + } + + btComp = m_ecs->add<BulletBody>(ent); + 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); + + // 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 + ); + btComp->body = new btRigidBody(bodyCI); + m_world->addRigidBody(btComp->body); + } + } + + void BulletPhysicsEngine::syncToECS() { + auto physEnts = m_ecs->findMatching<BulletBody>(); + for (auto& ent : physEnts) { + auto* transform = m_ecs->get<math::Transform>(ent); + auto* physBody = m_ecs->get<BulletBody>(ent); + + btTransform bTransform; + physBody->motion->getWorldTransform(bTransform); + + // set calculate and set ecs position + auto& btPos = bTransform.getOrigin(); + math::vec3 pos{btPos.x(), btPos.y(), btPos.z()}; + transform->origin( pos ); + } + } + + BulletPhysicsEngine::~BulletPhysicsEngine() { + // clean up the rigid bodies + auto entRB = m_ecs->findMatching<BulletBody>(); + for (auto& ent : entRB) { + auto* bulletBody = m_ecs->get<BulletBody>(ent); + delete bulletBody->motion; + m_world->removeCollisionObject(bulletBody->body); + delete bulletBody->body; + m_ecs->remove<BulletBody>(ent); + } + + // delete the world + delete m_world; + + // cleanup the configuration object + delete m_config.solver; + delete m_config.broadphase; + delete m_config.dispatcher; + delete m_config.collisionConfiguration; + } + +} // namespace fggl::phys::bullet \ No newline at end of file diff --git a/fggl/scenes/game.cpp b/fggl/scenes/game.cpp index 623247d..0cf1717 100644 --- a/fggl/scenes/game.cpp +++ b/fggl/scenes/game.cpp @@ -24,6 +24,7 @@ #include "fggl/scenes/game.hpp" #include "fggl/util/service.h" +#include "fggl/phys/bullet/types.hpp" namespace fggl::scenes { @@ -36,10 +37,12 @@ namespace fggl::scenes { fggl::AppState::activate(); // setup the scene - m_world = std::make_unique<fggl::ecs3::World>(*m_owner.registry()); + m_world = std::make_unique<ecs3::World>(*m_owner.registry()); + m_phys = std::make_unique<phys::bullet::BulletPhysicsEngine>(m_world.get()); } void Game::deactivate() { + m_phys.reset(); m_world.reset(); } @@ -50,6 +53,10 @@ namespace fggl::scenes { m_owner.change_state(m_previous); } } + + if ( m_phys != nullptr ) { + m_phys->step(); + } } void Game::render(fggl::gfx::Graphics &gfx) { diff --git a/include/fggl/ecs3/prototype/world.h b/include/fggl/ecs3/prototype/world.h index 26dba7a..75969c3 100644 --- a/include/fggl/ecs3/prototype/world.h +++ b/include/fggl/ecs3/prototype/world.h @@ -59,6 +59,11 @@ namespace fggl::ecs3::prototype { return (C *) ptr; } + template<typename C> + void remove(){ + m_components.erase(Component<C>::typeID()); + } + inline void *get(component_type_t t) { return m_components.at(t); } @@ -215,6 +220,20 @@ namespace fggl::ecs3::prototype { } } + template<typename C> + void remove(entity_t entity_id) { + try { + auto &entity = m_entities.at(entity_id); + try { + return entity.remove<C>(); + } catch ( std::out_of_range& e ) { + std::cerr << "entity " << entity_id << " does not have component "<< C::name << std::endl; + } + } catch ( std::out_of_range& e) { + std::cerr << "tried to delete component on entity that didn't exist, entity was: " << entity_id << std::endl; + } + } + void *get(entity_t entity_id, component_type_t t) { auto &entity = m_entities.at(entity_id); return entity.get(t); diff --git a/include/fggl/ecs3/types.hpp b/include/fggl/ecs3/types.hpp index 35b02f0..4a7ffc3 100644 --- a/include/fggl/ecs3/types.hpp +++ b/include/fggl/ecs3/types.hpp @@ -176,7 +176,12 @@ namespace fggl::ecs3 { } inline std::shared_ptr<fggl::ecs::ComponentBase> meta(component_type_t type_id) const { - return m_types.at(type_id); + try { + return m_types.at(type_id); + } catch (std::out_of_range& err) { + std::cerr << "asked for metadata on type " << type_id << " but no such type is in the type system" << std::endl; + return nullptr; + } } inline component_type_t find(const char *name) const { diff --git a/include/fggl/phys/bullet/bullet.hpp b/include/fggl/phys/bullet/bullet.hpp new file mode 100644 index 0000000..e5d301f --- /dev/null +++ b/include/fggl/phys/bullet/bullet.hpp @@ -0,0 +1,70 @@ +/* + * ${license.title} + * Copyright (C) 2022 ${license.owner} + * ${license.mailto} + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// +// Created by webpigeon on 24/04/22. +// + +#ifndef FGGL_PHYS_BULLET_BULLET_HPP +#define FGGL_PHYS_BULLET_BULLET_HPP + +#include "fggl/ecs3/module/module.h" + +#include "fggl/phys/types.hpp" +#include "fggl/phys/bullet/types.hpp" + +namespace fggl::phys::bullet { + + /** + * Bullet integration module. + * + * This provides the ability for FGGL to use bullet as a physics backened. Bullet is mature, stable physics + * engine which makes it suitable for most use cases. For use with FGGL there is a reasonable amount of copying + * back and forth to keep the two states in sync. + */ + struct BulletModule : ecs3::Module { + public: + BulletModule() = default; + + [[nodiscard]] + std::string name() const override { + return "phys::Bullet3"; + } + + void onLoad(ecs3::ModuleManager &manager, ecs3::TypeRegistry &types) override { + // dependencies + types.make<phys::RigidBody>(); + + // my types + types.make<phys::bullet::BulletBody>(); + } + + }; + +} // namespace fggl::phys::bullet + +namespace fggl::phys { + + // allows using fggl::phys::Bullet3 as the module name + using Bullet3 = bullet::BulletModule; + +} // namespace fggl::phys + +#endif //FGGL_PHYS_BULLET_BULLET_HPP diff --git a/include/fggl/phys/bullet/types.hpp b/include/fggl/phys/bullet/types.hpp new file mode 100644 index 0000000..31d9a94 --- /dev/null +++ b/include/fggl/phys/bullet/types.hpp @@ -0,0 +1,82 @@ +/* + * ${license.title} + * Copyright (C) 2022 ${license.owner} + * ${license.mailto} + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// +// Created by webpigeon on 24/04/22. +// + +#ifndef FGGL_PHYS_BULLET_TYPES_HPP +#define FGGL_PHYS_BULLET_TYPES_HPP + +#include "fggl/ecs3/ecs.hpp" +#include "fggl/phys/types.hpp" + +#include <bullet/btBulletDynamicsCommon.h> +#include <bullet/btBulletCollisionCommon.h> + +namespace fggl::phys::bullet { + + struct BulletConfiguration { + btBroadphaseInterface* broadphase; + btCollisionConfiguration* collisionConfiguration; + btDispatcher* dispatcher; + btConstraintSolver* solver; + }; + + /** + * Bullet component. + */ + struct BulletBody { + constexpr static const char* name = "phys::bullet::body"; + btMotionState* motion; + btRigidBody* body; + }; + + /** + * Bullet physics engine implementation. + * + * This wraps the bullet physics world and provides the systems needed for integrating it into our ECS. The state + * is responsible for creating and manging this object. + */ + class BulletPhysicsEngine : public PhysicsEngine { + public: + explicit BulletPhysicsEngine(ecs3::World* world); + ~BulletPhysicsEngine() override; + + void step() override; + private: + ecs3::World* m_ecs; + BulletConfiguration m_config; + btDiscreteDynamicsWorld* m_world; + + /** + * Check for ECS components which aren't in the physics world and add them. + */ + void checkForPhys(); + + /** + * Sync the bullet world state back to the ECS. + */ + void syncToECS(); + }; + +} // namespace fggl::phys::bullet + +#endif //FGGL_PHYS_BULLET_TYPES_HPP diff --git a/include/fggl/phys/types.hpp b/include/fggl/phys/types.hpp new file mode 100644 index 0000000..8ae73b6 --- /dev/null +++ b/include/fggl/phys/types.hpp @@ -0,0 +1,52 @@ +/* + * ${license.title} + * Copyright (C) 2022 ${license.owner} + * ${license.mailto} + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// +// Created by webpigeon on 24/04/22. +// + +#ifndef FGGL_PHYS_TYPES_HPP +#define FGGL_PHYS_TYPES_HPP + +namespace fggl::phys { + constexpr float STATIC_MASS = 0.0F; + + struct RigidBody { + constexpr static const char* name = "phys::Body"; + float mass = 1.0F; + }; + + class PhysicsEngine { + public: + PhysicsEngine() = default; + virtual ~PhysicsEngine() = default; + + // no copy and no move + PhysicsEngine(PhysicsEngine&) = delete; + PhysicsEngine(PhysicsEngine&&) = delete; + PhysicsEngine& operator=(PhysicsEngine&) = delete; + PhysicsEngine& operator=(PhysicsEngine&&) = delete; + + virtual void step() = 0; + }; + +} // namespace fggl::phys + +#endif //FGGL_PHYS_TYPES_HPP diff --git a/include/fggl/scenes/game.hpp b/include/fggl/scenes/game.hpp index fa9cafc..509388f 100644 --- a/include/fggl/scenes/game.hpp +++ b/include/fggl/scenes/game.hpp @@ -26,6 +26,7 @@ #define FGGL_SCENES_GAME_HPP #include "fggl/app.hpp" +#include "fggl/phys/types.hpp" namespace fggl::scenes { @@ -52,6 +53,7 @@ namespace fggl::scenes { private: std::shared_ptr<input::Input> m_input; std::unique_ptr<ecs3::World> m_world; + std::unique_ptr<phys::PhysicsEngine> m_phys; std::string m_previous = "menu"; }; -- GitLab