diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8abbf6314f..c7c5dcb3cd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2021,8 +2021,10 @@ void Application::init() { connect(_myAvatar, &MyAvatar::transformChanged, this, &Application::updateMyAvatarTransform); #ifdef USE_BULLET_PHYSICS -// _physicsEngine.initSafe(_entities.getTree()); -// _entities.getTree()->setSimulation(&_physicsEngine); + EntityTree* tree = _entities.getTree(); + _physicsEngine.setEntityTree(tree); + tree->setSimulation(&_physicsEngine); + _physicsEngine.init(); #endif // USE_BULLET_PHYSICS } diff --git a/interface/src/Application.h b/interface/src/Application.h index ccc7059ef5..7ee82da725 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -504,7 +504,7 @@ private: ViewFrustum _sharedVoxelSystemViewFrustum; #ifdef USE_BULLET_PHYSICS - ThreadSafePhysicsEngine _physicsEngine; + PhysicsEngine _physicsEngine; #endif // USE_BULLET_PHYSICS EntityTreeRenderer _entities; diff --git a/interface/src/Physics.cpp b/interface/src/Physics.cpp index 1f8215f9dd..7efa520571 100644 --- a/interface/src/Physics.cpp +++ b/interface/src/Physics.cpp @@ -9,55 +9,12 @@ // #include - -#include #include -#include #include "Util.h" #include "world.h" #include "Physics.h" -// DynamicsImpl is an implementation of ThreadSafeDynamicsWorld that knows how to lock an EntityTree -class DynamicsImpl : public ThreadSafeDynamicsWorld { -public: - DynamicsImpl( - btDispatcher* dispatcher, - btBroadphaseInterface* pairCache, - btConstraintSolver* constraintSolver, - btCollisionConfiguration* collisionConfiguration, - EntityTree* entities) - : ThreadSafeDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration), _entities(entities) { - assert(entities); - } - - bool tryLock() { - // wait for lock - _entities->lockForRead(); - return true; - } - - void unlock() { - _entities->unlock(); - } -private: - EntityTree* _entities; -}; - -ThreadSafePhysicsEngine::ThreadSafePhysicsEngine(const glm::vec3& offset) : PhysicsEngine(offset) { -} - -void ThreadSafePhysicsEngine::initSafe(EntityTree* entities) { - assert(!_dynamicsWorld); // only call this once - assert(entities); - _collisionConfig = new btDefaultCollisionConfiguration(); - _collisionDispatcher = new btCollisionDispatcher(_collisionConfig); - _broadphaseFilter = new btDbvtBroadphase(); - _constraintSolver = new btSequentialImpulseConstraintSolver; - // ThreadSafePhysicsEngine gets a DynamicsImpl, which derives from ThreadSafeDynamicsWorld - _dynamicsWorld = new DynamicsImpl(_collisionDispatcher, _broadphaseFilter, _constraintSolver, _collisionConfig, entities); -} - // // Applies static friction: maxVelocity is the largest velocity for which there // there is friction, and strength is the amount of friction force applied to reduce diff --git a/interface/src/Physics.h b/interface/src/Physics.h index fd2881184a..97e873d920 100644 --- a/interface/src/Physics.h +++ b/interface/src/Physics.h @@ -12,20 +12,6 @@ #ifndef hifi_Physics_h #define hifi_Physics_h -#include - -class EntityTree; - -class ThreadSafePhysicsEngine : public PhysicsEngine { -public: - ThreadSafePhysicsEngine(const glm::vec3& offset); - - // virtual override from PhysicsEngine - void init() { assert(false); } // call initSafe() instead - - void initSafe(EntityTree* entities); -}; - void applyStaticFriction(float deltaTime, glm::vec3& velocity, float maxVelocity, float strength); void applyDamping(float deltaTime, glm::vec3& velocity, float linearStrength, float squaredStrength); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index d3225daa64..e865b4a3f5 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -35,8 +35,6 @@ class EntityTreeElementExtraEncodeData; #define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0; #define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { }; -class EntityMotionState; - /// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available /// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate /// one directly, instead you must only construct one of it's derived classes with additional features. @@ -303,9 +301,10 @@ public: void clearUpdateFlags() { _updateFlags = 0; } void* getPhysicsInfo() const { return _physicsInfo; } + void setPhysicsInfo(void* data) { _physicsInfo = data; } SimulationState getSimulationState() const { return _simulationState; } - void setSimulationState(SimulationState state) { _simulationState = state; } + protected: virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init @@ -349,8 +348,8 @@ protected: void setRadius(float value); AACubeShape _collisionShape; - void* _physicsInfo; - SimulationState _simulationState; // only set by EntityTree + void* _physicsInfo; // only set by EntitySimulation + SimulationState _simulationState; // only set by EntitySimulation // UpdateFlags are set whenever a property changes that requires the change to be communicated to other // data structures. It is the responsibility of the EntityTree to relay changes entity and clear flags. diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index f91c80d67b..68093eb0de 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -154,9 +154,9 @@ void SimpleEntitySimulation::updateMortalEntities(quint64 now, QSet item_itr = _mortalEntities.erase(item_itr); entity->setSimulationState(EntityItem::Static); } else { - // check to see if this entity is no longer moving EntityItem::SimulationState newState = entity->computeSimulationState(); if (newState != EntityItem::Mortal) { + // check to see if this entity is moving if (newState == EntityItem::Moving) { entity->update(now); _movingEntities.push_back(entity); diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 793633e783..534ef1c135 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -17,7 +17,6 @@ #ifdef USE_BULLET_PHYSICS #include "CustomMotionState.h" #else // USE_BULLET_PHYSICS - // CustomMotionState stubbery class CustomMotionState { public: diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 6962355d5d..8e192e4279 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -12,19 +12,116 @@ #include "PhysicsEngine.h" #ifdef USE_BULLET_PHYSICS +#include "EntityMotionState.h" #include "ShapeInfoUtil.h" +#include "ThreadSafeDynamicsWorld.h" + +class EntityTree; + +PhysicsEngine::PhysicsEngine(const glm::vec3& offset) + : _collisionConfig(NULL), + _collisionDispatcher(NULL), + _broadphaseFilter(NULL), + _constraintSolver(NULL), + _dynamicsWorld(NULL), + _originOffset(offset), + _voxels() { +} PhysicsEngine::~PhysicsEngine() { } +/// \param tree pointer to EntityTree which is stored internally +void PhysicsEngine::setEntityTree(EntityTree* tree) { + assert(_entityTree == NULL); + assert(tree); + _entityTree = tree; +} + +/// \param[out] entitiesToDelete list of entities removed from simulation and should be deleted. +void PhysicsEngine::updateEntities(QSet& entitiesToDelete) { + // relay changes + QSet::iterator item_itr = _changedEntities.begin(); + while (item_itr != _changedEntities.end()) { + EntityItem* entity = *item_itr; + void* physicsInfo = entity->getPhysicsInfo(); + if (physicsInfo) { + CustomMotionState* motionState = static_cast(physicsInfo); + updateObject(motionState, entity->getUpdateFlags()); + } + entity->clearUpdateFlags(); + // TODO: implement this + ++item_itr; + } + + // hunt for entities who have expired + // TODO: make EntityItems use an expiry to make this work faster. + item_itr = _mortalEntities.begin(); + while (item_itr != _mortalEntities.end()) { + EntityItem* entity = *item_itr; + // always check to see if the lifetime has expired, for immortal entities this is always false + if (entity->lifetimeHasExpired()) { + qDebug() << "Lifetime has expired for entity:" << entity->getEntityItemID(); + entitiesToDelete.insert(entity); + // remove entity from the list + item_itr = _mortalEntities.erase(item_itr); + } else if (entity->isImmortal()) { + // remove entity from the list + item_itr = _mortalEntities.erase(item_itr); + } else { + ++item_itr; + } + } + // TODO: check for entities that have exited the world boundaries +} + +/// \param entity pointer to EntityItem to add to the simulation +/// \sideeffect the EntityItem::_simulationState member may be updated to indicate membership to internal list +void PhysicsEngine::addEntity(EntityItem* entity) { + assert(entity); + void* physicsInfo = entity->getPhysicsInfo(); + if (!physicsInfo) { + EntityMotionState* motionState = new EntityMotionState(entity); + entity->setPhysicsInfo(static_cast(motionState)); + if (entity->isMortal()) { + _mortalEntities.insert(entity); + } + } +} + +/// \param entity pointer to EntityItem to removed from the simulation +/// \sideeffect the EntityItem::_simulationState member may be updated to indicate non-membership to internal list +void PhysicsEngine::removeEntity(EntityItem* entity) { + assert(entity); + void* physicsInfo = entity->getPhysicsInfo(); + if (physicsInfo) { + CustomMotionState* motionState = static_cast(physicsInfo); + removeObject(motionState); + entity->setPhysicsInfo(NULL); + } + _mortalEntities.remove(entity); +} + +/// \param entity pointer to EntityItem to that may have changed in a way that would affect its simulation +void PhysicsEngine::entityChanged(EntityItem* entity) { + _changedEntities.insert(entity); +} + +void PhysicsEngine::clearEntities() { + // For now we assume this would only be called on shutdown in which case we can just let the memory get lost. +} + // virtual void PhysicsEngine::init() { + // _entityTree should be set prior to the init() call + assert(_entityTree); + if (!_dynamicsWorld) { _collisionConfig = new btDefaultCollisionConfiguration(); _collisionDispatcher = new btCollisionDispatcher(_collisionConfig); _broadphaseFilter = new btDbvtBroadphase(); _constraintSolver = new btSequentialImpulseConstraintSolver; - _dynamicsWorld = new btDiscreteDynamicsWorld(_collisionDispatcher, _broadphaseFilter, _constraintSolver, _collisionConfig); + _dynamicsWorld = new ThreadSafeDynamicsWorld(_collisionDispatcher, _broadphaseFilter, _constraintSolver, _collisionConfig, _entityTree); // default gravity of the world is zero, so each object must specify its own gravity // TODO: set up gravity zones diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index dfac1b17e8..4915475c83 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -32,31 +32,53 @@ const uint32_t PHYSICS_UPDATE_EASY = PHYSICS_UPDATE_POSITION | PHYSICS_UPDATE_VE #ifdef USE_BULLET_PHYSICS +#include #include +#include +#include + #include "BulletUtil.h" #include "CustomMotionState.h" #include "PositionHashKey.h" #include "ShapeManager.h" +#include "ThreadSafeDynamicsWorld.h" #include "VoxelObject.h" const float HALF_SIMULATION_EXTENT = 512.0f; // meters -class PhysicsEngine { +class PhysicsEngine : public EntitySimulation { public: - PhysicsEngine(const glm::vec3& offset) - : _collisionConfig(NULL), - _collisionDispatcher(NULL), - _broadphaseFilter(NULL), - _constraintSolver(NULL), - _dynamicsWorld(NULL), - _originOffset(offset), - _voxels() { - } + PhysicsEngine(const glm::vec3& offset); ~PhysicsEngine(); + // override from EntitySimulation + /// \param tree pointer to EntityTree which is stored internally + void setEntityTree(EntityTree* tree); + + // override from EntitySimulation + /// \param[out] entitiesToDelete list of entities removed from simulation and should be deleted. + void updateEntities(QSet& entitiesToDelete); + + // override from EntitySimulation + /// \param entity pointer to EntityItem to add to the simulation + /// \sideeffect the EntityItem::_simulationState member may be updated to indicate membership to internal list + void addEntity(EntityItem* entity); + + // override from EntitySimulation + /// \param entity pointer to EntityItem to removed from the simulation + /// \sideeffect the EntityItem::_simulationState member may be updated to indicate non-membership to internal list + void removeEntity(EntityItem* entity); + + // override from EntitySimulation + /// \param entity pointer to EntityItem to that may have changed in a way that would affect its simulation + void entityChanged(EntityItem* entity); + + // override from EntitySimulation + void clearEntities(); + virtual void init(); void stepSimulation(); @@ -99,12 +121,16 @@ protected: btCollisionDispatcher* _collisionDispatcher; btBroadphaseInterface* _broadphaseFilter; btSequentialImpulseConstraintSolver* _constraintSolver; - btDiscreteDynamicsWorld* _dynamicsWorld; + ThreadSafeDynamicsWorld* _dynamicsWorld; ShapeManager _shapeManager; private: glm::vec3 _originOffset; btHashMap _voxels; + + // EntitySimulation stuff + QSet _changedEntities; + QSet _mortalEntities; }; #else // USE_BULLET_PHYSICS diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index 54bea0fff5..4660e02c2f 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -15,21 +15,25 @@ * Copied and modified from btDiscreteDynamicsWorld.cpp by AndrewMeadows on 2014.11.12. * */ +#include + #include "ThreadSafeDynamicsWorld.h" ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( btDispatcher* dispatcher, btBroadphaseInterface* pairCache, btConstraintSolver* constraintSolver, - btCollisionConfiguration* collisionConfiguration) + btCollisionConfiguration* collisionConfiguration, + EntityTree* entities) : btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration) { + assert(entities); + _entities = entities; } void ThreadSafeDynamicsWorld::synchronizeMotionStates() { - if (tryLock()) { - btDiscreteDynamicsWorld::synchronizeMotionStates(); - unlock(); - } + _entities->lockForWrite(); + btDiscreteDynamicsWorld::synchronizeMotionStates(); + _entities->unlock(); } int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep) { @@ -77,6 +81,8 @@ int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, } } + // We only sync motion states once at the end of all substeps. + // This is to avoid placing multiple, repeated thread locks on _entities. synchronizeMotionStates(); clearForces(); return subSteps; diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.h b/libraries/physics/src/ThreadSafeDynamicsWorld.h index 0f7961044e..980ba7ae88 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.h +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.h @@ -20,6 +20,8 @@ #include +class EntityTree; + ATTRIBUTE_ALIGNED16(class) ThreadSafeDynamicsWorld : public btDiscreteDynamicsWorld { public: @@ -29,16 +31,15 @@ public: btDispatcher* dispatcher, btBroadphaseInterface* pairCache, btConstraintSolver* constraintSolver, - btCollisionConfiguration* collisionConfiguration); + btCollisionConfiguration* collisionConfiguration, + EntityTree* entities); - // virtual overrides of btDiscreteDynamicsWorld + // virtual overrides from btDiscreteDynamicsWorld int stepSimulation( btScalar timeStep, int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.)); void synchronizeMotionStates(); - /// \return true if lock succeeds - virtual bool tryLock() = 0; - - virtual void unlock() = 0; +private: + EntityTree* _entities; }; #endif // hifi_ThreadSafeDynamicsWorld_h