From c4c3169e2a9b355c51869232551a39484744d038 Mon Sep 17 00:00:00 2001 From: Jacob Jensen Date: Sun, 3 Nov 2013 23:42:00 +0100 Subject: [PATCH] Add Collision system for handling custom events upon collisions. squashed commits: Implementation of custom collision events. Some questionable design choices within. (Untested) Updated collision system to new structure. Now uses CollisionHandlerComponent and CollisionSystem. Manifolds used instead of global callback. Fixed previous compile error - a new one has taken it's place (uintptr_tr not defined) Basic working state achieved. m_collisionCallbackKey assignment from lua does not seem to take effect. Also storage and load still needs implementing, and agentabsorber system needs refactoring. Storage and load implemented. AgentAbsorber system & component refactored to use this system. Acceptance tests performed Collision filters added, not fully functional Collision functionality nearly updated. Needs cleanup, testing and integration with agentabsorber. Working CollisionFilter class. Attempt at exposing CollisionFilter to lua. Not successful. Fixes to improve usage of CollisionFilter. Now uses Collision struct instead of typedef to pair of entityIds. Better model for exposure to lua (still doesn't quite work). Cleaned up documentation and header includes. Fixed minor compile error Collision system finalized. - Added a member to Collision to represent the duration of collision represented since last clearCollisions(). This member will allow more fine grained control of collision handling (damage every 0.5 second, etc). - Cleaned up documentation and debugging code. Renamed CollisionHandlerComponent to CollisionComponent. Added removeCollisionGroup to CollisionComponent. Added constructor taking a collision group to CollisionComponent Refactoring of CollisionSystem. Stopped using mutable keyword in collision class and instead uses unordered map. Added a vector mirroring the contents of the unordered_map to allow iteration in lua. Changed to using boost::map_values for iteration. Rebased onto master. Fixed setup.lua Various relevant fixes. Code is currently not functioning due to strange bug. Implementation of custom collision events. Some questionable design choices within. (Untested) Updated collision system to new structure. Now uses CollisionHandlerComponent and CollisionSystem. Manifolds used instead of global callback. Fixed previous compile error - a new one has taken it's place (uintptr_tr not defined) Basic working state achieved. m_collisionCallbackKey assignment from lua does not seem to take effect. Also storage and load still needs implementing, and agentabsorber system needs refactoring. Fix typo in Lua script The previous version crashed because the game state passed to CollisionFilter's constructor was nil in Lua and consequently nullptr in C++. Simplify GameState::findSystem No need for complicated iteration. Also, dynamic_cast can only throw an exception if a reference type is cast, not for pointer types. Refactor signature handling of CollisionFilter Returning a pair of references is a little tricky because it's not obvious how the std::pair template will handle it. Returning a reference to a pair, however, is more predictable. Use static initializers when possible Minor refactoring * Use typedefs (i.e. CollisionFilter::Signature) instead of repeating the type * Use emplace when possible, it's more readable than constructing the collection item manually * Rename permutation -> combination Do not register collision filter in Implementation constructor At that point, the m_impl field of the CollisionFilter is not initialized yet (because the Implementation object is not fully constructed). The CollisionSystem asks the filter for its signature, which is saved in the m_impl object. Since this doesn't point to anything useful yet, it crashes. Added CollisionFilter.init and CollisionFilter.shutdown to allow users to create their CollisionFilters in constructors. Removed debugging code. Minor refactorings for CollisionFilter and CollisionSystem Removed typedefs in collision_filter.cpp. Removed minor error in rebasing --- res/scripts/microbe_stage/microbe.lua | 11 +- res/scripts/microbe_stage/setup.lua | 8 +- src/bullet/CMakeLists.txt | 4 + src/bullet/collision_filter.cpp | 129 +++++++++++++ src/bullet/collision_filter.h | 172 +++++++++++++++++ src/bullet/collision_system.cpp | 237 +++++++++++++++++++++++ src/bullet/collision_system.h | 265 ++++++++++++++++++++++++++ src/bullet/rigid_body_system.cpp | 4 +- src/bullet/rigid_body_system.h | 8 +- src/bullet/script_bindings.cpp | 12 +- src/engine/engine.cpp | 11 +- src/engine/engine.h | 1 + src/engine/game_state.cpp | 4 + src/engine/game_state.h | 43 +++-- src/engine/typedefs.h | 1 + src/microbe_stage/agent.cpp | 34 ++-- src/util/pair_hash.h | 4 +- 17 files changed, 903 insertions(+), 45 deletions(-) create mode 100644 src/bullet/collision_filter.cpp create mode 100644 src/bullet/collision_filter.h create mode 100644 src/bullet/collision_system.cpp create mode 100644 src/bullet/collision_system.h diff --git a/res/scripts/microbe_stage/microbe.lua b/res/scripts/microbe_stage/microbe.lua index 998dd10fdbc..0943fd47219 100644 --- a/res/scripts/microbe_stage/microbe.lua +++ b/res/scripts/microbe_stage/microbe.lua @@ -74,10 +74,15 @@ function Microbe.createMicrobeEntity(name) rigidBody.properties.linearFactor = Vector3(1, 1, 0) rigidBody.properties.angularFactor = Vector3(0, 0, 1) rigidBody.properties:touch() + + local reactionHandler = CollisionComponent() + reactionHandler:addCollisionGroup("microbe") + local components = { AgentAbsorberComponent(), OgreSceneNodeComponent(), MicrobeComponent(), + reactionHandler, rigidBody } for _, component in ipairs(components) do @@ -93,6 +98,7 @@ Microbe.COMPONENTS = { microbe = MicrobeComponent.TYPE_ID, rigidBody = RigidBodyComponent.TYPE_ID, sceneNode = OgreSceneNodeComponent.TYPE_ID, + collisionHandler = CollisionComponent.TYPE_ID } @@ -391,7 +397,8 @@ function MicrobeSystem:__init() AgentAbsorberComponent, MicrobeComponent, OgreSceneNodeComponent, - RigidBodyComponent + RigidBodyComponent, + CollisionComponent }, true ) @@ -401,7 +408,7 @@ end function MicrobeSystem:init(gameState) System.init(self, gameState) - self.entities:init(gameState) + self.entities:init(gameState) end diff --git a/res/scripts/microbe_stage/setup.lua b/res/scripts/microbe_stage/setup.lua index 1628a14b11a..340522e7cea 100644 --- a/res/scripts/microbe_stage/setup.lua +++ b/res/scripts/microbe_stage/setup.lua @@ -111,6 +111,9 @@ local function setupEmitter() ) rigidBody.properties:touch() entity:addComponent(rigidBody) + local reactionHandler = CollisionComponent() + reactionHandler:addCollisionGroup("emitter") + entity:addComponent(reactionHandler) -- Scene node local sceneNode = OgreSceneNodeComponent() sceneNode.meshName = "molecule.mesh" @@ -278,6 +281,7 @@ local function createMicrobeStage(name) UpdatePhysicsSystem(), RigidBodyOutputSystem(), BulletToOgreSystem(), + CollisionSystem(), -- Graphics OgreAddSceneNodeSystem(), OgreUpdateSceneNodeSystem(), @@ -301,5 +305,5 @@ end GameState.MICROBE = createMicrobeStage("microbe") GameState.MICROBE_ALTERNATE = createMicrobeStage("microbe_alternate") - -Engine:setCurrentGameState(GameState.MICROBE) + +Engine:setCurrentGameState(GameState.MICROBE) \ No newline at end of file diff --git a/src/bullet/CMakeLists.txt b/src/bullet/CMakeLists.txt index 540d6f53fec..541b17f8066 100644 --- a/src/bullet/CMakeLists.txt +++ b/src/bullet/CMakeLists.txt @@ -12,5 +12,9 @@ add_sources( ${CMAKE_CURRENT_SOURCE_DIR}/script_bindings.h ${CMAKE_CURRENT_SOURCE_DIR}/update_physics_system.cpp ${CMAKE_CURRENT_SOURCE_DIR}/update_physics_system.h + ${CMAKE_CURRENT_SOURCE_DIR}/collision_system.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/collision_system.h + ${CMAKE_CURRENT_SOURCE_DIR}/collision_filter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/collision_filter.h ) diff --git a/src/bullet/collision_filter.cpp b/src/bullet/collision_filter.cpp new file mode 100644 index 00000000000..c2d94415ff0 --- /dev/null +++ b/src/bullet/collision_filter.cpp @@ -0,0 +1,129 @@ +#include "collision_filter.h" +#include +#include "engine/game_state.h" +#include "scripting/luabind.h" + + + +using namespace thrive; + +struct CollisionFilter::Implementation { + + Implementation( + const std::string& collisionGroup1, + const std::string& collisionGroup2 + ) : m_signature(collisionGroup1, collisionGroup2) + { + } + + CollisionMap m_collisions; + + Signature m_signature; + + CollisionSystem* m_collisionSystem = nullptr; + +}; + + +luabind::scope +CollisionFilter::luaBindings() { + using namespace luabind; + return class_("CollisionFilter") + .def(constructor()) + .def("init", &CollisionFilter::init) + .def("shutdown", &CollisionFilter::shutdown) + .def("collisions", &CollisionFilter::collisions, return_stl_iterator) + .def("clearCollisions", &CollisionFilter::clearCollisions) + ; +} + + +CollisionFilter::CollisionFilter( + const std::string& collisionGroup1, + const std::string& collisionGroup2 +) : m_impl(new Implementation(collisionGroup1, collisionGroup2)) +{ +} + +CollisionFilter::~CollisionFilter(){} + +void +CollisionFilter::init( + GameState* gameState +) { + m_impl->m_collisionSystem = gameState->findSystem(); + m_impl->m_collisionSystem->registerCollisionFilter(*this); +} + +void +CollisionFilter::shutdown() { + m_impl->m_collisionSystem->unregisterCollisionFilter(*this); + m_impl->m_collisionSystem = nullptr; +} + +const CollisionFilter::CollisionIterator +CollisionFilter::collisions() { + return m_impl->m_collisions | boost::adaptors::map_values; +} + + +void +CollisionFilter::addCollision( + Collision collision +) { + CollisionMap::iterator foundCollision = m_impl->m_collisions.find(CollisionId(collision.entityId1, collision.entityId2)); + if (foundCollision != m_impl->m_collisions.end()) + foundCollision->second.addedCollisionDuration += collision.addedCollisionDuration; //Add collision time. + else + { + CollisionId key(collision.entityId1, collision.entityId2); + m_impl->m_collisions.emplace(key, collision); + } + +} + + +typename CollisionFilter::CollisionIterator::iterator +CollisionFilter::begin() const { + return (m_impl->m_collisions | boost::adaptors::map_values).begin(); +} + + +typename CollisionFilter::CollisionIterator::iterator +CollisionFilter::end() const { + return (m_impl->m_collisions | boost::adaptors::map_values).end(); +} + + +void +CollisionFilter::clearCollisions() { + m_impl->m_collisions.clear(); +} + + +const CollisionFilter::Signature& +CollisionFilter::getCollisionSignature() const { + return m_impl->m_signature; +} + + +size_t +CollisionFilter::IdHash::operator() ( + const CollisionId& collisionId +) const { + std::size_t hash1 = std::hash()(collisionId.first); + std::size_t hash2 = std::hash()(collisionId.second); + // Hash needs to be symmetric so that hash(entityId1, entityId2) == hash(entityId2, entityId1) + return hash1 ^ hash2; +} + + +bool +CollisionFilter::IdEquals::operator() ( + const CollisionId& lhs, + const CollisionId& rhs +) const{ + // Equality needs to be symmetric for collisions + return (lhs.first == rhs.first && lhs.second == rhs.second) || + (lhs.first == rhs.second && lhs.second == rhs.first); +} diff --git a/src/bullet/collision_filter.h b/src/bullet/collision_filter.h new file mode 100644 index 00000000000..4e68da0ccd1 --- /dev/null +++ b/src/bullet/collision_filter.h @@ -0,0 +1,172 @@ +#pragma once + +#include + +#include "bullet/collision_system.h" + +#include +#include + +#include +#include + + +namespace luabind { +class scope; +} + +namespace thrive { + +class Collision; +class CollisionSystem; + +/** +* @brief Filters for collisions that contain specific collision groups +* +* Collision filter makes it easy for systems and other peices of code to get easy +* access to the right collisions +* +*/ + +class CollisionFilter { + +public: + + using CollisionId = std::pair; + + using Signature = std::pair; + + struct IdHash { + std::size_t + operator() ( + const CollisionId& collisionId + ) const; + }; + + struct IdEquals { + bool + operator() ( + const CollisionId& lhs, + const CollisionId& rhs + ) const; + }; + + using CollisionMap = std::unordered_map; + using CollisionIterator = boost::range_detail::select_second_mutable_range; + + /** + * @brief Constructor + * + * @param collisionGroup1 + * The first collision group to monitor + * + * @param collisionGroup2 + * The second collision group to monitor + * + */ + CollisionFilter( + const std::string& collisionGroup1, + const std::string& collisionGroup2 + ); + + /** + * @brief Destructor + */ + ~CollisionFilter(); + + /** + * @brief Initialized the collision filter + * + * @param gameState + * The gamestate the filter belongs in. + */ + void + init( + GameState* gameState + ); + + /** + * @brief Shuts down the filter. + */ + void + shutdown(); + + /** + * @brief Lua bindings + * + * Exposes the following \b constructors: + * - CollisionFilter(const std::string&, const std::string&) + * - CollisionFilter::init(GameState*) + * - CollisionFilter::shutdown() + * - CollisionFilter::collisions() + * - CollisionFilter::clearCollisions() + */ + static luabind::scope + luaBindings(); + + /** + * @brief Returns the collisions that has occoured + * + * Is only reset when clearCollisions() is called + */ + const CollisionIterator + collisions(); + + /** + * @brief Clears the collisions + */ + void + clearCollisions(); + + /** + * @brief Adds a collision + * + * @param collision + * Collision to add + */ + void + addCollision(Collision collision); + + /** + * @brief Iterator + * + * Equivalent to + * \code + * collisions().cbegin() + * \endcode + * + * @return An iterator to the first collision + */ + typename CollisionIterator::iterator + begin() const; + + /** + * @brief Iterator + * + * Equivalent to + * \code + * collisions().cend() + * \endcode + * + * @return An iterator to the end of the collisions + */ + typename CollisionIterator::iterator + end() const; + + /** + * @brief Returns the signature of the collision filter + * + * @return + * A pair of the two collision group strings. + */ + const Signature& + getCollisionSignature() const; + +private: + + struct Implementation; + std::unique_ptr m_impl; + +}; + +} diff --git a/src/bullet/collision_system.cpp b/src/bullet/collision_system.cpp new file mode 100644 index 00000000000..e463faf70ca --- /dev/null +++ b/src/bullet/collision_system.cpp @@ -0,0 +1,237 @@ +#include "bullet/collision_system.h" + +#include "scripting/luabind.h" + +#include "bullet/collision_filter.h" +#include "engine/component_factory.h" +#include "engine/engine.h" +#include "engine/entity.h" +#include "engine/entity_manager.h" +#include "engine/serialization.h" +#include "bullet/rigid_body_system.h" +#include + +#include "util/pair_hash.h" + + +using namespace thrive; + +//////////////////////////////////////////////////////////////////////////////// +// CollisionComponent +//////////////////////////////////////////////////////////////////////////////// + +CollisionComponent::CollisionComponent(){} + + +CollisionComponent::CollisionComponent( + const std::string& collisionGroup +) : m_collisionGroups({collisionGroup}) +{ +} + +luabind::scope +CollisionComponent::luaBindings() { + using namespace luabind; + return class_("CollisionComponent") + .enum_("ID") [ + value("TYPE_ID", CollisionComponent::TYPE_ID) + ] + .scope [ + def("TYPE_NAME", &CollisionComponent::TYPE_NAME) + ] + .def(constructor<>()) + .def(constructor()) + .def("addCollisionGroup", &CollisionComponent::addCollisionGroup) + ; +} + +void +CollisionComponent::addCollisionGroup( + const std::string& group +) { + m_collisionGroups.push_back(group); +} + +void +CollisionComponent::removeCollisionGroup( + const std::string& group +) { + m_collisionGroups.erase(std::remove(m_collisionGroups.begin(), m_collisionGroups.end(), group), m_collisionGroups.end()); +} + +const std::vector& +CollisionComponent::getCollisionGroups() { + return m_collisionGroups; +} + +void +CollisionComponent::load( + const StorageContainer& storage +) { + Component::load(storage); + StorageList collisionGroups = storage.get("collisionGroups"); + m_collisionGroups.reserve(collisionGroups.size()); + for (const StorageContainer& container : collisionGroups) { + std::string collisionGroup = container.get("collisionGroup"); + m_collisionGroups.push_back(collisionGroup); + } +} + + +StorageContainer +CollisionComponent::storage() const { + StorageContainer storage = Component::storage(); + + StorageList collisionGroups; + collisionGroups.reserve(m_collisionGroups.size()); + for (std::string collisionGroup : m_collisionGroups) { + StorageContainer container; + container.set("collisionGroup", collisionGroup); + collisionGroups.append(container); + } + storage.set("collisionGroups", collisionGroups); + return storage; +} + +REGISTER_COMPONENT(CollisionComponent) + + +//////////////////////////////////////////////////////////////////////////////// +// Collision +//////////////////////////////////////////////////////////////////////////////// + + +Collision::Collision( + EntityId entityId1, + EntityId entityId2, + int addedCollisionDuration +) : entityId1(entityId1), + entityId2(entityId2), + addedCollisionDuration(addedCollisionDuration) +{ +} + +luabind::scope +Collision::luaBindings() { + using namespace luabind; + return class_("Collision") + .def(constructor()) + .def_readonly("entityId1", &Collision::entityId1) + .def_readonly("entityId2", &Collision::entityId2) + .def_readonly("addedCollisionDuration", &Collision::addedCollisionDuration) + ; +} + + +//////////////////////////////////////////////////////////////////////////////// +// CollisionSystem +//////////////////////////////////////////////////////////////////////////////// + + +struct CollisionSystem::Implementation { + + btDiscreteDynamicsWorld* m_world = nullptr; + + std::unordered_multimap m_collisionFilterMap; + +}; + + +CollisionSystem::CollisionSystem() + : m_impl(new Implementation()) +{ +} + + +CollisionSystem::~CollisionSystem() {} + + +luabind::scope +CollisionSystem::luaBindings() { + using namespace luabind; + return class_("CollisionSystem") + .def(constructor<>()) + ; +} + + +void +CollisionSystem::init( + GameState* gameState +) { + System::init(gameState); + m_impl->m_world = gameState->physicsWorld(); +} + + +void +CollisionSystem::shutdown() { + System::shutdown(); + m_impl->m_world = nullptr; +} + +void +CollisionSystem::update(int milliseconds) { + auto dispatcher = m_impl->m_world->getDispatcher(); + int numManifolds = dispatcher->getNumManifolds(); + + for (int i=0;igetManifoldByIndexInternal(i); + auto objectA = static_cast(contactManifold->getBody0()); + auto objectB = static_cast(contactManifold->getBody1()); + EntityId entityId1 = (reinterpret_cast(objectA->getUserPointer())); + EntityId entityId2 = (reinterpret_cast(objectB->getUserPointer())); + CollisionComponent* collisionComponent1 = static_cast( + System::gameState()->entityManager().getComponent(entityId1, CollisionComponent::TYPE_ID) + ); + CollisionComponent* collisionComponent2 = static_cast( + System::gameState()->entityManager().getComponent(entityId2, CollisionComponent::TYPE_ID) + ); + if (collisionComponent1 && collisionComponent2) + { + std::vector collisionGroups1 = collisionComponent1->getCollisionGroups(); + std::vector collisionGroups2 = collisionComponent2->getCollisionGroups(); + std::vector collisionGroupCombinations(collisionGroups1.size() * collisionGroups2.size()); + for(std::string collisionGroup1 : collisionGroups1) + { + for(std::string collisionGroup2 : collisionGroups2) + { + collisionGroupCombinations.emplace_back( + collisionGroup1, + collisionGroup2 + ); + } + } + for(CollisionFilter::Signature collisionSignature : collisionGroupCombinations) + { + auto filterIterators = m_impl->m_collisionFilterMap.equal_range(collisionSignature);//Get iterators for group of relevant CollisionFilters + for(auto it = filterIterators.first; it != filterIterators.second; ++it) // Foreach CollisionFilter object + { + Collision collision = Collision(entityId1, entityId2, milliseconds); + it->second.addCollision(collision); + } + } + } + contactManifold->clearManifold(); + } +} + + +void +CollisionSystem::registerCollisionFilter( + CollisionFilter& collisionFilter +) { + m_impl->m_collisionFilterMap.emplace( + collisionFilter.getCollisionSignature(), + collisionFilter + ); +} + +void +CollisionSystem::unregisterCollisionFilter( + CollisionFilter& collisionFilter +) { + m_impl->m_collisionFilterMap.erase(collisionFilter.getCollisionSignature()); +} diff --git a/src/bullet/collision_system.h b/src/bullet/collision_system.h new file mode 100644 index 00000000000..75ee9e144f6 --- /dev/null +++ b/src/bullet/collision_system.h @@ -0,0 +1,265 @@ +#pragma once + +#include "engine/component.h" +#include "engine/system.h" +#include "engine/typedefs.h" + +#include +#include +#include + +namespace luabind { +class scope; +} + +namespace thrive { + +class CollisionFilter; + +/** +* @brief A component for a collision reactive entity +*/ +class CollisionComponent : public Component { + COMPONENT(CollisionComponent) + +public: + + /** + * @brief Constructor + */ + CollisionComponent(); + + /** + * @brief Constructor + * + * @param collisionGroup + * Initial collision group that the containing entity should belong to. + * Collision groups determine which CollisionFilter objects are notified + * when a collision involving this object occours. + * More collision groups can be added with addCollisionGroup(group) + */ + CollisionComponent( + const std::string& collisionGroup + ); + + /** + * @brief Lua bindings + * + * Exposes: + * - RigidBodyComponent() + * - m_collisionCallbackKey + * + * @return + */ + static luabind::scope + luaBindings(); + + /** + * @brief Add a collision group + * Collision groups determine which CollisionFilter objects are notified + * when a collision involving this object occours. + * + * @param group + * The collision group to add. + */ + void + addCollisionGroup( + const std::string& group + ); + + /** + * @brief Remove a collision group + * + * @param group + * The collision group to remove. + */ + void + removeCollisionGroup( + const std::string& group + ); + + const std::vector& + getCollisionGroups(); + + /** + * @brief Loads the component + * + * @param storage + */ + void + load( + const StorageContainer& storage + ) override; + + + /** + * @brief Serializes the component + * + * @return + */ + StorageContainer + storage() const override; + +private: + + std::vector m_collisionGroups; + +}; + + +struct Collision { + + /** + * @brief Constructor + */ + Collision( + EntityId entityId1, + EntityId entityId2, + int addedCollisionDuration + ); + + + /** + * @brief Lua bindings + * + * Exposes: + * - Collision::entityId1 + * - Collision::entityId2 + * + * @return + */ + static luabind::scope + luaBindings(); + + /** + * @brief First collided entity + */ + EntityId entityId1; + + /** + * @brief Second collided entity + */ + EntityId entityId2; + + /** + * @brief The amount of time the collision represent + * + * An iteration of the main game loop may differ in time, and so users + * may want to know how long a period of time this collision represent. + * Inaccuracies may occour as the collision may not have been active for + * the entirety of this duration which must be taken into account or ignored. + * This time will keep accumalating until clearCollisions() is called. + */ + int addedCollisionDuration; + +}; + +}//namespace thrive +namespace std { + using namespace thrive; + template<> + struct hash> { + std::size_t + operator() ( + const std::pair& pair + ) const; + }; + template <> + struct equal_to> //Comparator for Collision + { + bool operator()( + const std::pair& collisionKey1, + const std::pair& collisionKey2 + ) const; + }; +} + +namespace std { + using namespace thrive; + +} +namespace thrive{ + + +class CollisionSystem : public System { + +public: + + /** + * @brief Constructor + */ + CollisionSystem(); + + /** + * @brief Destructor + */ + ~CollisionSystem(); + + /** + * @brief Lua bindings + * + * Exposes: + * - CollisionSystem() + * + * @return + */ + static luabind::scope + luaBindings(); + + /** + * @brief Initializes the engine + * + * @param engine + */ + void init( + GameState* gameState + ) override; + + /** + * @brief Shuts the system down + */ + void shutdown() override; + + /** + * @brief Updates the system + * + * @param milliSeconds + */ + void update( + int milliSeconds + ) override; + + /** + * @brief Register a collision filter. + * + * Once a collision filter is registered it will automatically receive new relevant collisions. + * + * @param collisionFilter + * The filter to register + */ + void + registerCollisionFilter( + CollisionFilter& collisionFilter + ); + + /** + * @brief Unregisters a collision filter. + * + * Necessary if CollisionSystem might be deleted before CollisionFilter + * + * @param collisionFilter + * The filter to unregister + */ + void + unregisterCollisionFilter( + CollisionFilter& collisionFilter + ); + +private: + + struct Implementation; + std::unique_ptr m_impl; + +}; + +} diff --git a/src/bullet/rigid_body_system.cpp b/src/bullet/rigid_body_system.cpp index d1a04c95e1e..d0411863a07 100644 --- a/src/bullet/rigid_body_system.cpp +++ b/src/bullet/rigid_body_system.cpp @@ -261,13 +261,13 @@ RigidBodyInputSystem::update(int milliseconds) { localInertia ); body->setMassProps( - properties.mass, + properties.mass, localInertia ); body->setLinearFactor(ogreToBullet(properties.linearFactor)); body->setAngularFactor(ogreToBullet(properties.angularFactor)); body->setDamping( - properties.linearDamping, + properties.linearDamping, properties.angularDamping ); body->setRestitution(properties.restitution); diff --git a/src/bullet/rigid_body_system.h b/src/bullet/rigid_body_system.h index e0eb41c3719..9e099e35029 100644 --- a/src/bullet/rigid_body_system.h +++ b/src/bullet/rigid_body_system.h @@ -88,7 +88,7 @@ class RigidBodyComponent : public Component, public btMotionState { bool hasContactResponse = true; /** - * @brief Whether this body is kinematic + * @brief Whether this body is kinematic */ bool kinematic = false; @@ -183,7 +183,7 @@ class RigidBodyComponent : public Component, public btMotionState { * @param impulse * The impulse * @param relativePosition - * The attack point, relative to the center of mass + * The attack point, relative to the center of mass */ void applyImpulse( @@ -261,7 +261,7 @@ class RigidBodyComponent : public Component, public btMotionState { /** * @brief Serializes the component * - * @return + * @return */ StorageContainer storage() const override; @@ -365,7 +365,7 @@ class RigidBodyInputSystem : public System { /** * @brief Updates the RigidBodyComponent with new data from the simulation * -* Copies the data from the simulation into +* Copies the data from the simulation into * RigidBodyComponent::m_dynamicOutputProperties. * */ diff --git a/src/bullet/script_bindings.cpp b/src/bullet/script_bindings.cpp index ea38704d792..e67bbc8d577 100644 --- a/src/bullet/script_bindings.cpp +++ b/src/bullet/script_bindings.cpp @@ -2,12 +2,15 @@ #include "bullet/bullet_ogre_conversion.h" #include "bullet/bullet_to_ogre_system.h" +#include "bullet/collision_filter.h" #include "bullet/collision_shape.h" +#include "bullet/collision_system.h" #include "bullet/debug_drawing.h" #include "bullet/rigid_body_system.h" #include "bullet/update_physics_system.h" #include "scripting/luabind.h" +#include #include #include #include @@ -15,6 +18,7 @@ using namespace luabind; using namespace thrive; + luabind::scope thrive::BulletBindings::luaBindings() { return ( @@ -29,12 +33,16 @@ thrive::BulletBindings::luaBindings() { SphereShape::luaBindings(), // Components RigidBodyComponent::luaBindings(), + CollisionComponent::luaBindings(), // Systems BulletToOgreSystem::luaBindings(), RigidBodyInputSystem::luaBindings(), RigidBodyOutputSystem::luaBindings(), BulletDebugDrawSystem::luaBindings(), - UpdatePhysicsSystem::luaBindings() + UpdatePhysicsSystem::luaBindings(), + CollisionSystem::luaBindings(), + // Other + CollisionFilter::luaBindings(), + Collision::luaBindings() ); } - diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 9525ae1d74f..1b24a2cc1ff 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -13,6 +13,7 @@ #include "bullet/bullet_to_ogre_system.h" #include "bullet/rigid_body_system.h" #include "bullet/update_physics_system.h" +#include "bullet/collision_system.h" // Ogre #include "ogre/camera_system.h" @@ -234,7 +235,7 @@ struct Engine::Implementation : public Ogre::WindowEventListener { } savegame.set("gameStates", std::move(gameStates)); std::ofstream stream( - m_serialization.saveFile, + m_serialization.saveFile, std::ofstream::trunc | std::ofstream::binary ); m_serialization.saveFile = ""; @@ -312,7 +313,6 @@ struct Engine::Implementation : public Ogre::WindowEventListener { initializeLua(m_luaState); } - void shutdownInputManager() { if (not m_input.inputManager) { @@ -388,7 +388,6 @@ struct Engine::Implementation : public Ogre::WindowEventListener { std::string saveFile; } m_serialization; - }; @@ -407,7 +406,7 @@ Engine_createGameState( ); systems.emplace_back(system); } - // We can't just capture the luaInitializer in the lambda here, because + // We can't just capture the luaInitializer in the lambda here, because // luabind::object's call operator is not const auto initializer = std::bind( [](luabind::object luaInitializer) { @@ -416,7 +415,7 @@ Engine_createGameState( luaInitializer ); return self->createGameState( - name, + name, std::move(systems), initializer ); @@ -472,7 +471,7 @@ Engine::createGameState( )); GameState* rawGameState = gameState.get(); m_impl->m_gameStates.insert(std::make_pair( - name, + name, std::move(gameState) )); return rawGameState; diff --git a/src/engine/engine.h b/src/engine/engine.h index f3349552a12..c01e4fec932 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -31,6 +31,7 @@ class EntityManager; class Keyboard; class Mouse; class OgreViewportSystem; +class CollisionSystem; class System; class RNG; diff --git a/src/engine/game_state.cpp b/src/engine/game_state.cpp index 306cfd3d003..04ab0398cda 100644 --- a/src/engine/game_state.cpp +++ b/src/engine/game_state.cpp @@ -147,6 +147,10 @@ GameState::init() { m_impl->m_initializer(); } +const std::vector>& +GameState::systems() const { + return m_impl->m_systems; +} void GameState::load( diff --git a/src/engine/game_state.h b/src/engine/game_state.h index f940aad2419..3d2e6ef0e10 100644 --- a/src/engine/game_state.h +++ b/src/engine/game_state.h @@ -2,7 +2,7 @@ #include #include - +#include class btDiscreteDynamicsWorld; namespace luabind { @@ -25,15 +25,17 @@ class System; * * The game has to switch between different states. Examples of a state are * "main menu", "microbe gameplay" or "microbe editor". These states usually -* share very few entities and even fewer systems, so it is sensible to +* share very few entities and even fewer systems, so it is sensible to * separate them completely (and, if necessary, share data over other channels). * -* Each GameState has its own EntityManager and its own set of systems. Game +* Each GameState has its own EntityManager and its own set of systems. Game * states are identified by their name, a unique string. * -* GameStates cannot be created directly. Use Engine::createGameState to create +* GameStates cannot be created directly. Use Engine::createGameState to create * new GameStates. */ + + class GameState { public: @@ -49,7 +51,7 @@ class GameState { * Exposes: * - GameState::name() * - * @return + * @return */ static luabind::scope luaBindings(); @@ -74,7 +76,7 @@ class GameState { /** * @brief Returns the engine this game state belongs to * - * @return + * @return */ Engine& engine(); @@ -82,7 +84,7 @@ class GameState { /** * @brief Returns the engine this game state belongs to * - * @return + * @return */ const Engine& engine() const; @@ -90,7 +92,7 @@ class GameState { /** * @brief Returns the game state's entity manager * - * @return + * @return */ EntityManager& entityManager(); @@ -98,7 +100,7 @@ class GameState { /** * @brief Returns the game state's entity manager * - * @return + * @return */ const EntityManager& entityManager() const; @@ -106,7 +108,7 @@ class GameState { /** * @brief The game state's name * - * @return + * @return */ std::string name() const; @@ -123,6 +125,18 @@ class GameState { Ogre::SceneManager* sceneManager() const; + template + S* + findSystem() { + for (const auto& system : this->systems()) { + S* foundSystem = dynamic_cast(system.get()); + if (foundSystem) { + return foundSystem; + } + } + return nullptr; + } + private: friend class Engine; @@ -142,7 +156,7 @@ class GameState { * The game state's systems. The game state takes ownership of them. * * @param initializer - * A function that is called after initializing the game + * A function that is called after initializing the game * state. You can set up basic entities in this callback. */ GameState( @@ -172,6 +186,9 @@ class GameState { void init(); + const std::vector>& + systems() const; + /** * @brief Called by the engine during loading of a savegame * @@ -195,7 +212,7 @@ class GameState { /** * @brief Called by the engine during savegame creation * - * @return + * @return * * @see GameState::load() */ @@ -219,7 +236,7 @@ class GameState { struct Implementation; std::unique_ptr m_impl; - + }; } diff --git a/src/engine/typedefs.h b/src/engine/typedefs.h index 6b95d927ea1..5646a7f4ee6 100644 --- a/src/engine/typedefs.h +++ b/src/engine/typedefs.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace thrive { diff --git a/src/microbe_stage/agent.cpp b/src/microbe_stage/agent.cpp index 73a0939efd9..61226282259 100644 --- a/src/microbe_stage/agent.cpp +++ b/src/microbe_stage/agent.cpp @@ -1,5 +1,7 @@ #include "microbe_stage/agent.h" +#include "bullet/collision_filter.h" +#include "bullet/collision_system.h" #include "bullet/rigid_body_system.h" #include "engine/component_factory.h" #include "engine/engine.h" @@ -9,9 +11,9 @@ #include "engine/rng.h" #include "ogre/scene_node_system.h" #include "scripting/luabind.h" - #include #include +#include "util/make_unique.h" using namespace thrive; @@ -429,11 +431,15 @@ AgentEmitterSystem::update(int milliseconds) { agentComponent->m_velocity = emissionVelocity; agentComponent->m_agentId = emitterComponent->m_agentId; agentComponent->m_potency = emitterComponent->m_potencyPerParticle; + // Collision handler component + auto collisionComponent = make_unique(); + collisionComponent->addCollisionGroup("agent"); // Build component list std::list> components; components.emplace_back(std::move(agentSceneNodeComponent)); components.emplace_back(std::move(agentComponent)); components.emplace_back(std::move(agentRigidBodyComponent)); + components.emplace_back(std::move(collisionComponent)); for (auto& component : components) { this->entityManager()->addComponent( agentEntityId, @@ -458,9 +464,13 @@ AgentAbsorberSystem::luaBindings() { ; } - struct AgentAbsorberSystem::Implementation { + Implementation() + : m_agentCollisions("microbe", "agent") + { + } + EntityFilter< AgentAbsorberComponent > m_absorbers; @@ -471,6 +481,8 @@ struct AgentAbsorberSystem::Implementation { btDiscreteDynamicsWorld* m_world = nullptr; + CollisionFilter m_agentCollisions; + }; @@ -491,6 +503,7 @@ AgentAbsorberSystem::init( m_impl->m_absorbers.setEntityManager(&gameState->entityManager()); m_impl->m_agents.setEntityManager(&gameState->entityManager()); m_impl->m_world = gameState->physicsWorld(); + m_impl->m_agentCollisions.init(gameState); } @@ -499,6 +512,7 @@ AgentAbsorberSystem::shutdown() { m_impl->m_absorbers.setEntityManager(nullptr); m_impl->m_agents.setEntityManager(nullptr); m_impl->m_world = nullptr; + m_impl->m_agentCollisions.shutdown(); System::shutdown(); } @@ -509,14 +523,11 @@ AgentAbsorberSystem::update(int) { AgentAbsorberComponent* absorber = std::get<0>(entry.second); absorber->m_absorbedAgents.clear(); } - auto dispatcher = m_impl->m_world->getDispatcher(); - int numManifolds = dispatcher->getNumManifolds(); - for (int i = 0; i < numManifolds; i++) { - btPersistentManifold* contactManifold = dispatcher->getManifoldByIndexInternal(i); - auto objectA = static_cast(contactManifold->getBody0()); - auto objectB = static_cast(contactManifold->getBody1()); - EntityId entityA = reinterpret_cast(objectA->getUserPointer()); - EntityId entityB = reinterpret_cast(objectB->getUserPointer()); + for (Collision collision : m_impl->m_agentCollisions) + { + EntityId entityA = collision.entityId1; + EntityId entityB = collision.entityId2; + AgentAbsorberComponent* absorber = nullptr; AgentComponent* agent = nullptr; if ( @@ -546,6 +557,7 @@ AgentAbsorberSystem::update(int) { agent->m_timeToLive = 0; } } + m_impl->m_agentCollisions.clearCollisions(); } @@ -640,5 +652,3 @@ AgentRegistry::getAgentId( } return agentId; } - - diff --git a/src/util/pair_hash.h b/src/util/pair_hash.h index f78da41eadc..18868969f32 100644 --- a/src/util/pair_hash.h +++ b/src/util/pair_hash.h @@ -15,7 +15,7 @@ struct hash> { * @param pair * The pair to hash * - * @return + * @return * The hash */ std::size_t @@ -23,7 +23,7 @@ struct hash> { const std::pair& pair ) const { std::size_t hashA = std::hash()(pair.first); - std::size_t hashB = std::hash()(pair.first); + std::size_t hashB = std::hash()(pair.second); return hashA ^ (hashB + 0x9e3779b9 + (hashA << 6) + (hashA >> 2)); } };