implement EntitySimulation API in PhysicsEngine

This commit is contained in:
Andrew Meadows 2014-12-10 17:01:20 -08:00
parent ab75150175
commit f9ba4d355c
2 changed files with 76 additions and 156 deletions

View file

@ -8,6 +8,10 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// TODO: make _incomingEntityChanges to be list of MotionState*'s.
// TODO: make MotionState able to clear incoming flags
// TODO: make MotionState::setWorldTransform() put itself on _incomingChanges list
// TODO: give PhysicsEngine instance an _entityPacketSender
#include "PhysicsEngine.h"
#ifdef USE_BULLET_PHYSICS
@ -32,104 +36,32 @@ PhysicsEngine::PhysicsEngine(const glm::vec3& offset)
PhysicsEngine::~PhysicsEngine() {
}
/// \param tree pointer to EntityTree which is stored internally
void PhysicsEngine::setEntityTree(EntityTree* tree) {
assert(_entityTree == NULL);
assert(tree);
_entityTree = tree;
}
// begin EntitySimulation overrides
void PhysicsEngine::updateEntitiesInternal(const quint64& now) {
// relay outgoing changes: from physics engine to EntityItem's
void PhysicsEngine::relayIncomingChangesToSimulation() {
// process incoming changes
QSet<EntityItem*>::iterator item_itr = _incomingPhysics.begin();
while (item_itr != _incomingPhysics.end()) {
EntityItem* entity = *item_itr;
void* physicsInfo = entity->getPhysicsInfo();
if (physicsInfo) {
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
uint32_t preOutgoingFlags = motionState->getOutgoingUpdateFlags();
updateObject(motionState, entity->getUpdateFlags());
uint32_t postOutgoingFlags = motionState->getOutgoingUpdateFlags();
if (preOutgoingFlags && !postOutgoingFlags) {
_outgoingPhysics.remove(entity);
}
}
entity->clearUpdateFlags();
++item_itr;
}
_incomingPhysics.clear();
}
/// \param[out] entitiesToDelete list of entities removed from simulation and should be deleted.
void PhysicsEngine::updateEntities(QSet<EntityItem*>& entitiesToDelete) {
// TODO: Andrew to make this work.
EnitySimulation::updateEntities(entitiesToDelete);
item_itr = _outgoingPhysics.begin();
uint32_t simulationFrame = getSimulationFrame();
QSet<ObjectMotionState*>::iterator stateItr = _outgoingPhysics.begin();
uint32_t frame = getFrameCount();
float subStepRemainder = getSubStepRemainder();
while (item_itr != _outgoingPhysics.end()) {
EntityItem* entity = *item_itr;
void* physicsInfo = entity->getPhysicsInfo();
if (physicsInfo) {
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
if (motionState->shouldSendUpdate(simulationFrame, subStepRemainder)) {
EntityItemProperties properties = entity->getProperties();
motionState->pushToProperties(properties);
/*
properties.setVelocity(newVelocity);
properties.setPosition(newPosition);
properties.setRotation(newRotation);
properties.setAngularVelocity(newAngularVelocity);
properties.setLastEdited(now);
*/
EntityItemID id(entity->getID());
_packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, id, properties);
++itemItr;
} else if (motionState->shouldRemoveFromOutgoingPhysics()) {
itemItr = _outgoingPhysics.erase(itemItr);
} else {
++itemItr;
}
while (stateItr != _outgoingPhysics.end()) {
ObjectMotionState* state = *stateItr;
if (state->shouldSendUpdate(frame, subStepRemainder)) {
state->sendUpdate(_entityPacketSender);
++stateItr;
} else if (state->isAtRest()) {
stateItr = _outgoingPhysics.erase(stateItr);
} else {
++stateItr;
}
}
item_itr = _movedEntities.begin();
AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f);
while (item_itr != _movedEntities.end()) {
EntityItem* entity = *item_itr;
void* physicsInfo = entity->getPhysicsInfo();
if (physicsInfo) {
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
AACube oldCube, newCube;
motionState->getBoundingCubes(oldCube, newCube);
// check to see if this movement has sent the entity outside of the domain.
if (!domainBounds.touches(newCube)) {
//qDebug() << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
entitiesToDelete << entity->getEntityItemID();
clearEntityState(entity);
} else if (newCube != oldCube) {
moveOperator.addEntityToMoveList(entity, oldCube, newCube);
}
}
}
// 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) {
void PhysicsEngine::addEntityInternal(EntityItem* entity) {
assert(entity);
void* physicsInfo = entity->getPhysicsInfo();
if (!physicsInfo) {
assert(!_entities.contains(entity));
_entities.insert(entity);
EntityMotionState* motionState = new EntityMotionState(entity);
_entityMotionStates.insert(motionState);
if (addObject(motionState)) {
entity->setPhysicsInfo(static_cast<void*>(motionState));
} else {
@ -139,36 +71,52 @@ void PhysicsEngine::addEntity(EntityItem* 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) {
void PhysicsEngine::removeEntityInternal(EntityItem* entity) {
assert(entity);
void* physicsInfo = entity->getPhysicsInfo();
if (physicsInfo) {
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
removeObject(motionState);
entity->setPhysicsInfo(NULL);
_entityMotionStates.remove(motionState);
}
_entities.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) {
_incomingPhysics.insert(entity);
void PhysicsEngine::entityChangedInternal(EntityItem* entity) {
// queue incoming changes: from external sources (script, EntityServer, etc) to physics engine
_incomingEntityChanges.insert(entity);
}
void PhysicsEngine::clearEntities() {
void PhysicsEngine::clearEntitiesInternal() {
// For now we assume this would only be called on shutdown in which case we can just let the memory get lost.
QSet<EntityItem*>::const_iterator entityItr = _entities.begin();
for (entityItr = _entities.begin(); entityItr != _entities.end(); ++entityItr) {
void* physicsInfo = (*entityItr)->getPhysicsInfo();
QSet<ObjectMotionState*>::const_iterator stateItr = _entityMotionStates.begin();
for (stateItr = _entityMotionStates.begin(); stateItr != _entityMotionStates.end(); ++stateItr) {
removeObject(*stateItr);
}
_entityMotionStates.clear();
_incomingEntityChanges.clear();
}
// end EntitySimulation overrides
void PhysicsEngine::relayIncomingChangesToSimulation() {
// process incoming changes
QSet<EntityItem*>::iterator itemItr = _incomingEntityChanges.begin();
while (itemItr != _incomingEntityChanges.end()) {
EntityItem* entity = *itemItr;
void* physicsInfo = entity->getPhysicsInfo();
if (physicsInfo) {
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
removeObject(motionState);
uint32_t flags = entity->getDirtyFlags();
if (flags & DIRTY_PHYSICS_FLAGS) {
updateObject(motionState, flags);
// this incoming change will override any outgoing changes to the same parameters
motionState->clearOutgoingDirtyFlags(flags);
}
}
}
_entities.clear();
_incomingPhysics.clear();
entity->clearDirtyFlags();
++itemItr;
}
_incomingEntityChanges.clear();
}
// virtual
@ -353,10 +301,10 @@ bool PhysicsEngine::updateObject(ObjectMotionState* motionState, uint32_t flags)
return false;
}
if (flags & PHYSICS_UPDATE_HARD) {
if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
// a hard update requires the body be pulled out of physics engine, changed, then reinserted
updateObjectHard(body, motionState, flags);
} else if (flags & PHYSICS_UPDATE_EASY) {
} else if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
// an easy update does not require that the body be pulled out of physics engine
updateObjectEasy(body, motionState, flags);
}
@ -370,7 +318,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
// pull body out of physics engine
_dynamicsWorld->removeRigidBody(body);
if (flags & PHYSICS_UPDATE_SHAPE) {
if (flags & EntityItem::DIRTY_SHAPE) {
btCollisionShape* oldShape = body->getCollisionShape();
ShapeInfo info;
motionState->computeShapeInfo(info);
@ -384,9 +332,9 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
_shapeManager.releaseShape(newShape);
}
// MASS bit should be set whenever SHAPE is set
assert(flags & PHYSICS_UPDATE_MASS);
assert(flags & EntityItem::DIRTY_MASS);
}
bool easyUpdate = flags & PHYSICS_UPDATE_EASY;
bool easyUpdate = flags & EASY_DIRTY_PHYSICS_FLAGS;
if (easyUpdate) {
updateObjectEasy(body, motionState, flags);
}
@ -406,7 +354,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
case MOTION_TYPE_DYNAMIC: {
int collisionFlags = body->getCollisionFlags() & ~(btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT);
body->setCollisionFlags(collisionFlags);
if (! (flags & PHYSICS_UPDATE_MASS)) {
if (! (flags & EntityItem::DIRTY_MASS)) {
// always update mass properties when going dynamic (unless it's already been done)
btVector3 inertia(0.0f, 0.0f, 0.0f);
float mass = motionState->getMass();
@ -441,19 +389,19 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
// private
void PhysicsEngine::updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) {
if (flags & PHYSICS_UPDATE_POSITION) {
if (flags & EntityItem::DIRTY_POSITION) {
btTransform transform;
motionState->getWorldTransform(transform);
body->setWorldTransform(transform);
}
if (flags & PHYSICS_UPDATE_VELOCITY) {
if (flags & EntityItem::DIRTY_VELOCITY) {
motionState->applyVelocities();
motionState->applyGravity();
}
body->setRestitution(motionState->_restitution);
body->setFriction(motionState->_friction);
if (flags & PHYSICS_UPDATE_MASS) {
if (flags & EntityItem::DIRTY_MASS) {
float mass = motionState->getMass();
btVector3 inertia(0.0f, 0.0f, 0.0f);
body->getCollisionShape()->calculateLocalInertia(mass, inertia);

View file

@ -12,24 +12,8 @@
#ifndef hifi_PhysicsEngine_h
#define hifi_PhysicsEngine_h
enum PhysicsUpdateFlag {
PHYSICS_UPDATE_POSITION = 0x0001,
PHYSICS_UPDATE_VELOCITY = 0x0002,
PHYSICS_UPDATE_GRAVITY = 0x0004,
PHYSICS_UPDATE_MASS = 0x0008,
PHYSICS_UPDATE_COLLISION_GROUP = 0x0010,
PHYSICS_UPDATE_MOTION_TYPE = 0x0020,
PHYSICS_UPDATE_SHAPE = 0x0040
};
typedef unsigned int uint32_t;
// The update flags trigger two varieties of updates: "hard" which require the body to be pulled
// and re-added to the physics engine and "easy" which just updates the body properties.
const uint32_t PHYSICS_UPDATE_HARD = PHYSICS_UPDATE_MOTION_TYPE | PHYSICS_UPDATE_SHAPE;
const uint32_t PHYSICS_UPDATE_EASY = PHYSICS_UPDATE_POSITION | PHYSICS_UPDATE_VELOCITY |
PHYSICS_UPDATE_GRAVITY | PHYSICS_UPDATE_MASS | PHYSICS_UPDATE_COLLISION_GROUP;
#ifdef USE_BULLET_PHYSICS
#include <QSet>
@ -40,6 +24,7 @@ const uint32_t PHYSICS_UPDATE_EASY = PHYSICS_UPDATE_POSITION | PHYSICS_UPDATE_VE
#include "BulletUtil.h"
#include "ObjectMotionState.h"
#include "ObjectMotionState.h"
#include "PositionHashKey.h"
#include "ShapeManager.h"
#include "ThreadSafeDynamicsWorld.h"
@ -54,30 +39,12 @@ public:
~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();
// overrides from EntitySimulation
void updateEntitiesInternal(const quint64& now);
void addEntityInternal(EntityItem* entity);
void removeEntityInternal(EntityItem* entity);
void entityChangedInternal(EntityItem* entity);
void clearEntitiesInternal();
virtual void init();
@ -112,6 +79,9 @@ public:
/// \return true if entity updated
bool updateObject(ObjectMotionState* motionState, uint32_t flags);
/// process queue of changed from external sources
void relayIncomingChangesToSimulation();
/// \return duration of fixed simulation substep
float getFixedSubStep() const;
@ -122,7 +92,7 @@ public:
// Bullet will extrapolate the positions provided to MotionState::setWorldTransform() in an effort to provide
// smoother visible motion when the render frame rate does not match that of the simulation loop. We provide
// access to this fraction for improved filtering of update packets to interested parties.
float getSubStepRemainder() { _dynamicsWorld->getLocalTimeAccumulation(); }
float getSubStepRemainder() { return _dynamicsWorld->getLocalTimeAccumulation(); }
protected:
void updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags);
@ -141,9 +111,11 @@ private:
btHashMap<PositionHashKey, VoxelObject> _voxels;
// EntitySimulation stuff
QSet<EntityItem*> _entities; // all entities that we track
QSet<EntityItem*> _incomingPhysics; // entities with pending physics changes by script or packet
QSet<EntityItem*> _outgoingPhysics; // entites with pending transform changes by physics simulation
QSet<ObjectMotionState*> _entityMotionStates; // all entities that we track
QSet<EntityItem*> _incomingEntityChanges; // entities with pending physics changes by script or packet
QSet<ObjectMotionState*> _outgoingPhysics; // MotionStates with pending transform changes from physics simulation
EntityEditPacketSender* _entityPacketSender;
uint32_t _frameCount;
};