PhysicsEngine is now an EntitySimulation

This commit is contained in:
Andrew Meadows 2014-12-02 15:29:41 -08:00
parent 919214b7cb
commit c9ea6885c1
11 changed files with 163 additions and 90 deletions

View file

@ -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
}

View file

@ -504,7 +504,7 @@ private:
ViewFrustum _sharedVoxelSystemViewFrustum;
#ifdef USE_BULLET_PHYSICS
ThreadSafePhysicsEngine _physicsEngine;
PhysicsEngine _physicsEngine;
#endif // USE_BULLET_PHYSICS
EntityTreeRenderer _entities;

View file

@ -9,55 +9,12 @@
//
#include <glm/glm.hpp>
#include <EntityTree.h>
#include <SharedUtil.h>
#include <ThreadSafeDynamicsWorld.h>
#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

View file

@ -12,20 +12,6 @@
#ifndef hifi_Physics_h
#define hifi_Physics_h
#include <PhysicsEngine.h>
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);

View file

@ -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.

View file

@ -154,9 +154,9 @@ void SimpleEntitySimulation::updateMortalEntities(quint64 now, QSet<EntityItem*>
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);

View file

@ -17,7 +17,6 @@
#ifdef USE_BULLET_PHYSICS
#include "CustomMotionState.h"
#else // USE_BULLET_PHYSICS
// CustomMotionState stubbery
class CustomMotionState {
public:

View file

@ -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<EntityItem*>& entitiesToDelete) {
// relay changes
QSet<EntityItem*>::iterator item_itr = _changedEntities.begin();
while (item_itr != _changedEntities.end()) {
EntityItem* entity = *item_itr;
void* physicsInfo = entity->getPhysicsInfo();
if (physicsInfo) {
CustomMotionState* motionState = static_cast<CustomMotionState*>(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<void*>(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<CustomMotionState*>(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

View file

@ -32,31 +32,53 @@ const uint32_t PHYSICS_UPDATE_EASY = PHYSICS_UPDATE_POSITION | PHYSICS_UPDATE_VE
#ifdef USE_BULLET_PHYSICS
#include <QSet>
#include <btBulletDynamicsCommon.h>
#include <EntityItem.h>
#include <EntitySimulation.h>
#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<EntityItem*>& 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<PositionHashKey, VoxelObject> _voxels;
// EntitySimulation stuff
QSet<EntityItem*> _changedEntities;
QSet<EntityItem*> _mortalEntities;
};
#else // USE_BULLET_PHYSICS

View file

@ -15,21 +15,25 @@
* Copied and modified from btDiscreteDynamicsWorld.cpp by AndrewMeadows on 2014.11.12.
* */
#include <EntityTree.h>
#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;

View file

@ -20,6 +20,8 @@
#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
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