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)); } };