route more entity updates into physics engine

This commit is contained in:
Andrew Meadows 2014-11-24 11:21:40 -08:00
parent eda779b434
commit ec1f11c1a8
13 changed files with 290 additions and 147 deletions

View file

@ -50,7 +50,9 @@ void EntityCollisionSystem::update() {
if (_entities->tryLockForRead()) {
QList<EntityItem*>& movingEntities = _entities->getMovingEntities();
foreach (EntityItem* entity, movingEntities) {
checkEntity(entity);
if (!entity->getMotionState()) {
checkEntity(entity);
}
}
_entities->unlock();
}

View file

@ -91,9 +91,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) {
_lastEditedFromRemoteInRemoteTime = 0;
_lastUpdated = 0;
_created = 0;
#ifdef USE_BULLET_PHYSICS
_motionState = NULL;
#endif // USE_BULLET_PHYSICS
_updateFlags = 0;
_changedOnServer = 0;
initFromEntityItemID(entityItemID);
@ -107,9 +105,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemPropert
_lastEditedFromRemoteInRemoteTime = 0;
_lastUpdated = 0;
_created = properties.getCreated();
#ifdef USE_BULLET_PHYSICS
_motionState = NULL;
#endif // USE_BULLET_PHYSICS
_updateFlags = 0;
_changedOnServer = 0;
initFromEntityItemID(entityItemID);
@ -118,11 +114,9 @@ EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemPropert
}
EntityItem::~EntityItem() {
#ifdef USE_BULLET_PHYSICS
// Make sure the EntityItem has been removed from the physics engine AND
// that its _motionState has been destroyed BEFORE you get here.
assert(_motionState == NULL);
#endif // USE_BULLET_PHYSICS
}
EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
@ -519,7 +513,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY_SETTER(PROP_GRAVITY, glm::vec3, updateGravity);
READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping);
READ_ENTITY_PROPERTY_SETTER(PROP_LIFETIME, float, updateLifetime);
READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT,setScript);
READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT, updateScript);
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, _registrationPoint);
READ_ENTITY_PROPERTY_SETTER(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, _angularDamping);
@ -797,23 +791,23 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc
}
}
SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPositionInMeters); // this will call recalculate collision shape if needed
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, setDimensionsInMeters); // NOTE: radius is obsolete
SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, setRotation);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(mass, setMass);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, setVelocityInMeters);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, setGravityInMeters);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, updatePositionInMeters); // this will call recalculate collision shape if needed
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, updateDimensionsInMeters); // NOTE: radius is obsolete
SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, updateRotation);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(mass, updateMass);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, updateVelocityInMeters);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, updateGravityInMeters);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, setDamping);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, setLifetime);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, updateScript);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(registrationPoint, setRegistrationPoint);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularVelocity, setAngularVelocity);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularVelocity, updateAngularVelocity);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularDamping, setAngularDamping);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignoreForCollisions, setIgnoreForCollisions);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, setCollisionsWillMove);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignoreForCollisions, updateIgnoreForCollisions);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
@ -985,7 +979,7 @@ void EntityItem::updateDimensions(const glm::vec3& value) {
if (_dimensions != value) {
_dimensions = value;
recalculateCollisionShape();
_updateFlags |= EntityItem::UPDATE_SHAPE;
_updateFlags |= (EntityItem::UPDATE_SHAPE | EntityItem::UPDATE_MASS);
}
}
@ -994,7 +988,7 @@ void EntityItem::updateDimensionsInMeters(const glm::vec3& value) {
if (_dimensions != dimensions) {
_dimensions = dimensions;
recalculateCollisionShape();
_updateFlags |= EntityItem::UPDATE_SHAPE;
_updateFlags |= (EntityItem::UPDATE_SHAPE | EntityItem::UPDATE_MASS);
}
}
@ -1031,7 +1025,7 @@ void EntityItem::updateVelocityInMeters(const glm::vec3& value) {
void EntityItem::updateGravity(const glm::vec3& value) {
if (_gravity != value) {
_gravity = value;
_updateFlags |= EntityItem::UPDATE_VELOCITY;
_updateFlags |= EntityItem::UPDATE_GRAVITY;
}
}
@ -1039,7 +1033,7 @@ void EntityItem::updateGravityInMeters(const glm::vec3& value) {
glm::vec3 gravity = value / (float) TREE_SCALE;
if (_gravity != gravity) {
_gravity = gravity;
_updateFlags |= EntityItem::UPDATE_VELOCITY;
_updateFlags |= EntityItem::UPDATE_GRAVITY;
}
}
@ -1071,12 +1065,17 @@ void EntityItem::updateLifetime(float value) {
}
}
#ifdef USE_BULLET_PHYSICS
void EntityItem::updateScript(const QString& value) {
if (_script != value) {
_script = value;
_updateFlags |= EntityItem::UPDATE_SCRIPT;
}
}
void EntityItem::destroyMotionState() {
if (_motionState) {
delete _motionState;
_motionState = NULL;
}
}
#endif // USE_BULLET_PHYSICS

View file

@ -22,6 +22,7 @@
#include <Octree.h> // for EncodeBitstreamParams class
#include <OctreeElement.h> // for OctreeElement::AppendState
#include <OctreePacketData.h>
#include <PhysicsEngine.h>
#include <VoxelDetail.h>
#include "EntityItemID.h"
@ -35,9 +36,7 @@ class EntityTreeElementExtraEncodeData;
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { };
#ifdef USE_BULLET_PHYSICS
class EntityMotionState;
#endif // USE_BULLET_PHYSICS
/// 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
@ -46,14 +45,21 @@ class EntityItem {
public:
enum EntityUpdateFlags {
UPDATE_POSITION = 0x0001,
UPDATE_VELOCITY = 0x0002,
UPDATE_MASS = 0x0004,
UPDATE_COLLISION_GROUP = 0x0008,
UPDATE_MOTION_TYPE = 0x0010,
UPDATE_SHAPE = 0x0020,
UPDATE_LIFETIME = 0x0040
//UPDATE_APPEARANCE = 0x8000,
// flags for things that need to be relayed to physics engine
UPDATE_POSITION = PHYSICS_UPDATE_POSITION, //0x0001,
UPDATE_VELOCITY = PHYSICS_UPDATE_VELOCITY, //0x0002,
UPDATE_GRAVITY = PHYSICS_UPDATE_GRAVITY, //0x0004,
UPDATE_MASS = PHYSICS_UPDATE_MASS, //0x0008,
UPDATE_COLLISION_GROUP = PHYSICS_UPDATE_COLLISION_GROUP, //0x0010,
UPDATE_MOTION_TYPE = PHYSICS_UPDATE_MOTION_TYPE, //0x0020,
UPDATE_SHAPE = PHYSICS_UPDATE_SHAPE, //0x0040,
//...
// add new flags here in the middle
//...
// non-physics stuff
UPDATE_SCRIPT = 0x2000,
UPDATE_LIFETIME = 0x4000,
UPDATE_APPEARANCE = 0x8000
};
DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly
@ -292,19 +298,18 @@ public:
void updateIgnoreForCollisions(bool value);
void updateCollisionsWillMove(bool value);
void updateLifetime(float value);
void updateScript(const QString& value);
uint32_t getUpdateFlags() const { return _updateFlags; }
void clearUpdateFlags() { _updateFlags = 0; }
#ifdef USE_BULLET_PHYSICS
EntityMotionState* getMotionState() const { return _motionState; }
virtual EntityMotionState* createMotionState() { return NULL; }
void destroyMotionState();
#endif // USE_BULLET_PHYSICS
SimulationState getSimulationState() const { return _simulationState; }
protected:
friend class EntityTree;
void clearUpdateFlags() { _updateFlags = 0; }
void setSimulationState(SimulationState state) { _simulationState = state; }
virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init
@ -348,9 +353,7 @@ protected:
void setRadius(float value);
AACubeShape _collisionShape;
#ifdef USE_BULLET_PHYSICS
EntityMotionState* _motionState;
#endif // USE_BULLET_PHYSICS
SimulationState _simulationState; // only set by EntityTree
// UpdateFlags are set whenever a property changes that requires the change to be communicated to other

View file

@ -11,6 +11,7 @@
#ifdef USE_BULLET_PHYSICS
#include <BulletUtil.h>
#endif // USE_BULLET_PHYSICS
#include "EntityItem.h"
#include "EntityMotionState.h"
@ -41,9 +42,10 @@ MotionType EntityMotionState::getMotionType() const {
// HACK: According to EntityTree the meaning of "static" is "not moving" whereas
// to Bullet it means "can't move". For demo purposes we temporarily interpret
// Entity::weightless to mean Bullet::static.
return _entity->hasGravity() ? MOTION_TYPE_DYNAMIC : MOTION_TYPE_STATIC;
return _entity->getCollisionsWillMove() ? MOTION_TYPE_DYNAMIC : MOTION_TYPE_STATIC;
}
#ifdef USE_BULLET_PHYSICS
// This callback is invoked by the physics simulation in two cases:
// (1) when the RigidBody is first added to the world
// (irregardless of MotionType: STATIC, DYNAMIC, or KINEMATIC)
@ -64,34 +66,54 @@ void EntityMotionState::getWorldTransform (btTransform &worldTrans) const {
// This callback is invoked by the physics simulation at the end of each simulation frame...
// iff the corresponding RigidBody is DYNAMIC and has moved.
void EntityMotionState::setWorldTransform (const btTransform &worldTrans) {
glm::vec3 pos;
bulletToGLM(worldTrans.getOrigin(), pos);
_entity->setPositionInMeters(pos + _cachedWorldOffset);
uint32_t updateFlags = _entity->getUpdateFlags();
if (! (updateFlags & PHYSICS_UPDATE_POSITION)) {
glm::vec3 pos;
bulletToGLM(worldTrans.getOrigin(), pos);
_entity->setPositionInMeters(pos + _cachedWorldOffset);
glm::quat rot;
bulletToGLM(worldTrans.getRotation(), rot);
_entity->setRotation(rot);
}
glm::quat rot;
bulletToGLM(worldTrans.getRotation(), rot);
_entity->setRotation(rot);
glm::vec3 v;
getVelocity(v);
_entity->setVelocityInMeters(v);
getAngularVelocity(v);
_entity->setAngularVelocity(v);
if (! (updateFlags & PHYSICS_UPDATE_VELOCITY)) {
glm::vec3 v;
getVelocity(v);
_entity->setVelocityInMeters(v);
getAngularVelocity(v);
_entity->setAngularVelocity(v);
}
}
#endif // USE_BULLET_PHYSICS
void EntityMotionState::applyVelocities() const {
#ifdef USE_BULLET_PHYSICS
if (_body) {
setVelocity(_entity->getVelocityInMeters());
setAngularVelocity(_entity->getAngularVelocity());
_body->setActivationState(ACTIVE_TAG);
}
#endif // USE_BULLET_PHYSICS
}
void EntityMotionState::applyGravity() const {
#ifdef USE_BULLET_PHYSICS
if (_body) {
setGravity(_entity->getGravityInMeters());
_body->setActivationState(ACTIVE_TAG);
}
#endif // USE_BULLET_PHYSICS
}
void EntityMotionState::computeShapeInfo(ShapeInfo& info) {
#ifdef USE_BULLET_PHYSICS
// HACK: for now we make everything a box.
glm::vec3 halfExtents = _entity->getDimensionsInMeters();
glm::vec3 halfExtents = 0.5f * _entity->getDimensionsInMeters();
btVector3 bulletHalfExtents;
glmToBullet(halfExtents, bulletHalfExtents);
info.setBox(bulletHalfExtents);
#endif // USE_BULLET_PHYSICS
}
void EntityMotionState::getBoundingCubes(AACube& oldCube, AACube& newCube) {
@ -100,4 +122,3 @@ void EntityMotionState::getBoundingCubes(AACube& oldCube, AACube& newCube) {
_oldBoundingCube = newCube;
}
#endif // USE_BULLET_PHYSICS

View file

@ -12,10 +12,16 @@
#ifndef hifi_EntityMotionState_h
#define hifi_EntityMotionState_h
#ifdef USE_BULLET_PHYSICS
#include <AACube.h>
#ifdef USE_BULLET_PHYSICS
#include <CustomMotionState.h>
#else // USE_BULLET_PHYSICS
// CustomMotionState stubbery
class CustomMotionState {
public:
bool _foo;
};
#endif // USE_BULLET_PHYSICS
class EntityItem;
@ -29,9 +35,12 @@ public:
MotionType getMotionType() const;
#ifdef USE_BULLET_PHYSICS
void getWorldTransform (btTransform &worldTrans) const;
void setWorldTransform (const btTransform &worldTrans);
#endif // USE_BULLET_PHYSICS
void applyVelocities() const;
void applyGravity() const;
void computeShapeInfo(ShapeInfo& info);
@ -42,5 +51,4 @@ protected:
AACube _oldBoundingCube;
};
#endif // USE_BULLET_PHYSICS
#endif // hifi_EntityMotionState_h

View file

@ -77,12 +77,13 @@ EntityItem* EntityTree::getOrCreateEntityItem(const EntityItemID& entityID, cons
}
/// Adds a new entity item to the tree
void EntityTree::addEntityItem(EntityItem* entity) {
void EntityTree::addEntityInternal(EntityItem* entity) {
// You should not call this on existing entities that are already part of the tree! Call updateEntity()
EntityItemID entityID = entity->getEntityItemID();
EntityTreeElement* containingElement = getContainingElement(entityID);
if (containingElement) {
qDebug() << "UNEXPECTED!!!! don't call addEntityItem() on existing EntityItems. entityID=" << entityID;
// should probably assert here
qDebug() << "UNEXPECTED!!!! don't call addEntityInternal() on existing EntityItems. entityID=" << entityID;
return;
}
@ -90,17 +91,39 @@ void EntityTree::addEntityItem(EntityItem* entity) {
AddEntityOperator theOperator(this, entity);
recurseTreeWithOperator(&theOperator);
updateEntityState(entity);
emitAddingEntity(entity);
}
void EntityTree::emitAddingEntity(EntityItem* entity) {
// add entity to proper internal lists
EntityItem::SimulationState newState = entity->computeSimulationState();
switch (newState) {
case EntityItem::Moving:
_movingEntities.push_back(entity);
break;
case EntityItem::Mortal:
_mortalEntities.push_back(entity);
break;
default:
break;
}
entity->setSimulationState(newState);
entity->clearUpdateFlags();
// add entity to physics engine
if (_physicsEngine && !entity->getMotionState()) {
addEntityToPhysicsEngine(entity);
}
_isDirty = true;
// finally emit the signal
emit addingEntity(entity->getEntityItemID());
}
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
// You should not call this on existing entities that are already part of the tree! Call updateEntity()
EntityTreeElement* containingElement = getContainingElement(entityID);
if (!containingElement) {
qDebug() << "UNEXPECTED!!!! EntityTree::updateEntity() entityID doesn't exist!!! entityID=" << entityID;
@ -127,20 +150,9 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
}
}
} else {
// check to see if we need to simulate this entity...
QString entityScriptBefore = existingEntity->getScript();
UpdateEntityOperator theOperator(this, containingElement, existingEntity, properties);
recurseTreeWithOperator(&theOperator);
_isDirty = true;
updateEntityState(existingEntity);
QString entityScriptAfter = existingEntity->getScript();
if (entityScriptBefore != entityScriptAfter) {
emitEntityScriptChanging(entityID); // the entity script has changed
}
}
containingElement = getContainingElement(entityID);
@ -177,8 +189,7 @@ EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItem
if (result) {
// this does the actual adding of the entity
addEntityItem(result);
emitAddingEntity(entityID);
addEntityInternal(result);
}
return result;
}
@ -204,14 +215,6 @@ void EntityTree::setPhysicsEngine(PhysicsEngine* engine) {
_physicsEngine = engine;
}
void EntityTree::emitAddingEntity(const EntityItemID& entityItemID) {
emit addingEntity(entityItemID);
}
void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID) {
emit entityScriptChanging(entityItemID);
}
void EntityTree::deleteEntity(const EntityItemID& entityID) {
emit deletingEntity(entityID);
@ -619,13 +622,6 @@ void EntityTree::updateEntityState(EntityItem* entity) {
break;
}
entity->setSimulationState(newState);
if (_physicsEngine) {
EntityMotionState* motionState = entity->getMotionState();
if (motionState) {
_physicsEngine->updateEntityMotionType(motionState);
}
}
}
}
@ -701,8 +697,15 @@ void EntityTree::updateChangedEntities(quint64 now, QSet<EntityItemID>& entities
entitiesToDelete << thisEntity->getEntityItemID();
clearEntityState(thisEntity);
} else {
// TODO: Andrew to push changes to physics engine (when we know how to sort the changes)
uint32_t flags = thisEntity->getUpdateFlags();
if (flags & EntityItem::UPDATE_SCRIPT) {
emit entityScriptChanging(thisEntity->getEntityItemID());
}
updateEntityState(thisEntity);
if (_physicsEngine && thisEntity->getMotionState()) {
_physicsEngine->updateEntity(thisEntity->getMotionState(), flags);
}
}
thisEntity->clearUpdateFlags();
}
@ -742,7 +745,7 @@ void EntityTree::updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesT
qDebug() << "Entity " << thisEntity->getEntityItemID() << " moved out of domain bounds.";
entitiesToDelete << thisEntity->getEntityItemID();
clearEntityState(thisEntity);
} else {
} else if (newCube != oldCube) {
moveOperator.addEntityToMoveList(thisEntity, oldCube, newCube);
updateEntityState(thisEntity);
}

View file

@ -79,7 +79,8 @@ public:
// The newer API...
EntityItem* getOrCreateEntityItem(const EntityItemID& entityID, const EntityItemProperties& properties);
void addEntityItem(EntityItem* entityItem);
void addEntityInternal(EntityItem* entityItem);
void emitAddingEntity(EntityItem* entityItem);
EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
@ -147,9 +148,6 @@ public:
void trackDeletedEntity(const EntityItemID& entityID);
void emitAddingEntity(const EntityItemID& entityItemID);
void emitEntityScriptChanging(const EntityItemID& entityItemID);
QList<EntityItem*>& getMovingEntities() { return _movingEntities; }
void setPhysicsEngine(PhysicsEngine* engine);

View file

@ -742,12 +742,13 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
// TODO: Do we need to also do this?
// 3) remember the old cube for the entity so we can mark it as dirty
if (entityItem) {
QString entityScriptBefore = entityItem->getScript();
uint32_t oldFlags = entityItem->getUpdateFlags();
bool bestFitBefore = bestFitEntityBounds(entityItem);
EntityTreeElement* currentContainingElement = _myTree->getContainingElement(entityItemID);
bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args);
if (entityItem->getUpdateFlags()) {
if (!oldFlags && entityItem->getUpdateFlags()) {
// this entity is not yet on the changed list and needs to be added
_myTree->entityChanged(entityItem);
}
bool bestFitAfter = bestFitEntityBounds(entityItem);
@ -763,21 +764,14 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
}
}
}
QString entityScriptAfter = entityItem->getScript();
if (entityScriptBefore != entityScriptAfter) {
_myTree->emitEntityScriptChanging(entityItemID); // the entity script has changed
}
} else {
entityItem = EntityTypes::constructEntityItem(dataAt, bytesLeftToRead, args);
if (entityItem) {
bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args);
addEntityItem(entityItem); // add this new entity to this elements entities
_myTree->emitAddingEntity(entityItem);
entityItemID = entityItem->getEntityItemID();
_myTree->setContainingElement(entityItemID, this);
_myTree->updateEntityState(entityItem);
_myTree->emitAddingEntity(entityItemID); // we just added an entity
}
}
// Move the buffer forward to read more entities

View file

@ -299,14 +299,25 @@ bool UpdateEntityOperator::preRecursion(OctreeElement* element) {
}
// set the entity properties and mark our element as changed.
_existingEntity->setProperties(_properties);
uint32_t oldUpdateFlags = _existingEntity->getUpdateFlags();
bool somethingChanged = _existingEntity->setProperties(_properties);
uint32_t newUpdateFlags = _existingEntity->getUpdateFlags();
if (somethingChanged && !oldUpdateFlags && _existingEntity->getUpdateFlags()) {
// this entity hasn't yet been added to changed list
_tree->entityChanged(_existingEntity);
}
if (_wantDebug) {
qDebug() << " *** set properties ***";
}
} else {
// otherwise, this is an add case.
entityTreeElement->addEntityItem(_existingEntity);
_existingEntity->setProperties(_properties); // still need to update the properties!
uint32_t oldUpdateFlags = _existingEntity->getUpdateFlags();
bool somethingChanged = _existingEntity->setProperties(_properties); // still need to update the properties!
if (somethingChanged && !oldUpdateFlags && _existingEntity->getUpdateFlags()) {
// this entity hasn't yet been added to changed list
_tree->entityChanged(_existingEntity);
}
_tree->setContainingElement(_entityItemID, entityTreeElement);
if (_wantDebug) {
qDebug() << " *** ADDING ENTITY to ELEMENT and MAP and SETTING PROPERTIES ***";

View file

@ -72,6 +72,12 @@ void CustomMotionState::setAngularVelocity(const glm::vec3& velocity) const {
_body->setAngularVelocity(v);
}
void CustomMotionState::setGravity(const glm::vec3& gravity) const {
btVector3 g;
glmToBullet(gravity, g);
_body->setGravity(g);
}
void CustomMotionState::getVelocity(glm::vec3& velocityOut) const {
bulletToGLM(_body->getLinearVelocity(), velocityOut);
}

View file

@ -35,6 +35,7 @@ public:
//virtual void setWorldTransform (const btTransform &worldTrans);
virtual void applyVelocities() const = 0;
virtual void applyGravity() const = 0;
virtual void computeShapeInfo(ShapeInfo& info) = 0;
@ -49,6 +50,7 @@ public:
void setVelocity(const glm::vec3& velocity) const;
void setAngularVelocity(const glm::vec3& velocity) const;
void setGravity(const glm::vec3& gravity) const;
void getVelocity(glm::vec3& velocityOut) const;
void getAngularVelocity(glm::vec3& angularVelocityOut) const;

View file

@ -32,7 +32,7 @@ void PhysicsEngine::init() {
// NOTE: we don't care about memory leaking groundShape and groundObject -->
// they'll exist until the executable exits.
const float halfSide = 200.0f;
const float halfHeight = 10.0f;
const float halfHeight = 1.0f;
btCollisionShape* groundShape = new btBoxShape(btVector3(halfSide, halfHeight, halfSide));
btTransform groundTransform;
@ -40,6 +40,7 @@ void PhysicsEngine::init() {
groundTransform.setOrigin(btVector3(halfSide, -halfHeight, halfSide));
btCollisionObject* groundObject = new btCollisionObject();
groundObject->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
groundObject->setCollisionShape(groundShape);
groundObject->setWorldTransform(groundTransform);
_dynamicsWorld->addCollisionObject(groundObject);
@ -131,7 +132,7 @@ bool PhysicsEngine::addEntity(CustomMotionState* motionState) {
btVector3 inertia(0.0f, 0.0f, 0.0f);
float mass = 0.0f;
btRigidBody* body = NULL;
switch(motionState->_motionType) {
switch(motionState->getMotionType()) {
case MOTION_TYPE_KINEMATIC: {
body = new btRigidBody(mass, motionState, shape, inertia);
body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
@ -147,6 +148,7 @@ bool PhysicsEngine::addEntity(CustomMotionState* motionState) {
body->updateInertiaTensor();
motionState->_body = body;
motionState->applyVelocities();
motionState->applyGravity();
break;
}
case MOTION_TYPE_STATIC:
@ -158,6 +160,8 @@ bool PhysicsEngine::addEntity(CustomMotionState* motionState) {
break;
}
}
// wtf?
body->setFlags(BT_DISABLE_WORLD_GRAVITY);
body->setRestitution(motionState->_restitution);
body->setFriction(motionState->_friction);
_dynamicsWorld->addRigidBody(body);
@ -182,60 +186,132 @@ bool PhysicsEngine::removeEntity(CustomMotionState* motionState) {
return false;
}
bool PhysicsEngine::updateEntityMotionType(CustomMotionState* motionState) {
bool PhysicsEngine::updateEntity(CustomMotionState* motionState, uint32_t flags) {
btRigidBody* body = motionState->_body;
if (!body) {
return false;
}
if (flags & PHYSICS_UPDATE_HARD) {
// a hard update requires the body be pulled out of physics engine, changed, then reinserted
updateEntityHard(body, motionState, flags);
} else if (flags & PHYSICS_UPDATE_EASY) {
// an easy update does not require that the body be pulled out of physics engine
updateEntityEasy(body, motionState, flags);
}
return true;
}
// private
void PhysicsEngine::updateEntityHard(btRigidBody* body, CustomMotionState* motionState, uint32_t flags) {
MotionType newType = motionState->getMotionType();
MotionType oldType = MOTION_TYPE_DYNAMIC;
if (body->isStaticObject()) {
oldType = MOTION_TYPE_STATIC;
} else if (body->isKinematicObject()) {
oldType = MOTION_TYPE_KINEMATIC;
}
MotionType newType = motionState->getMotionType();
if (oldType != newType) {
// pull body out of physics engine
_dynamicsWorld->removeRigidBody(body);
// update various properties and flags
switch (newType) {
case MOTION_TYPE_KINEMATIC: {
body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
body->setActivationState(DISABLE_DEACTIVATION);
body->updateInertiaTensor();
break;
}
case MOTION_TYPE_DYNAMIC: {
// pull body out of physics engine
_dynamicsWorld->removeRigidBody(body);
if (flags & PHYSICS_UPDATE_SHAPE) {
btCollisionShape* oldShape = body->getCollisionShape();
ShapeInfo info;
motionState->computeShapeInfo(info);
btCollisionShape* newShape = _shapeManager.getShape(info);
if (newShape != oldShape) {
body->setCollisionShape(newShape);
_shapeManager.releaseShape(oldShape);
} else {
// whoops, shape hasn't changed after all so we must release the reference
// that was created when looking it up
_shapeManager.releaseShape(newShape);
}
// MASS bit should be set whenever SHAPE is set
assert(flags & PHYSICS_UPDATE_MASS);
}
bool easyUpdate = flags & PHYSICS_UPDATE_EASY;
if (easyUpdate) {
updateEntityEasy(body, motionState, flags);
}
// update the motion parameters
switch (newType) {
case MOTION_TYPE_KINEMATIC: {
int collisionFlags = body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT;
collisionFlags &= ~(btCollisionObject::CF_STATIC_OBJECT);
body->setCollisionFlags(collisionFlags);
body->forceActivationState(DISABLE_DEACTIVATION);
body->setMassProps(0.0f, btVector3(0.0f, 0.0f, 0.0f));
body->updateInertiaTensor();
break;
}
case MOTION_TYPE_DYNAMIC: {
int collisionFlags = body->getCollisionFlags() & ~(btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT);
body->setCollisionFlags(collisionFlags);
if (! (flags & PHYSICS_UPDATE_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();
body->getCollisionShape()->calculateLocalInertia(mass, inertia);
body->setMassProps(mass, inertia);
body->updateInertiaTensor();
motionState->applyVelocities();
break;
}
default: {
// MOTION_TYPE_STATIC
body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
body->updateInertiaTensor();
break;
}
bool forceActivation = true;
body->activate(forceActivation);
break;
}
default: {
// MOTION_TYPE_STATIC
int collisionFlags = body->getCollisionFlags() | btCollisionObject::CF_STATIC_OBJECT;
collisionFlags &= ~(btCollisionObject::CF_KINEMATIC_OBJECT);
body->setCollisionFlags(collisionFlags);
body->forceActivationState(DISABLE_SIMULATION);
// reinsert body into physics engine
body->setRestitution(motionState->_restitution);
body->setFriction(motionState->_friction);
_dynamicsWorld->addRigidBody(body);
return true;
body->setMassProps(0.0f, btVector3(0.0f, 0.0f, 0.0f));
body->updateInertiaTensor();
body->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f));
body->setAngularVelocity(btVector3(0.0f, 0.0f, 0.0f));
break;
}
}
return false;
// reinsert body into physics engine
_dynamicsWorld->addRigidBody(body);
body->activate();
}
bool PhysicsEngine::updateEntityMassProperties(CustomMotionState* motionState, float mass, const glm::vec3& inertiaEigenValues) {
// TODO: implement this
assert(motionState);
return false;
}
// private
void PhysicsEngine::updateEntityEasy(btRigidBody* body, CustomMotionState* motionState, uint32_t flags) {
if (flags & PHYSICS_UPDATE_POSITION) {
btTransform transform;
motionState->getWorldTransform(transform);
body->setWorldTransform(transform);
}
if (flags & PHYSICS_UPDATE_VELOCITY) {
motionState->applyVelocities();
motionState->applyGravity();
}
body->setRestitution(motionState->_restitution);
body->setFriction(motionState->_friction);
if (flags & PHYSICS_UPDATE_MASS) {
float mass = motionState->getMass();
btVector3 inertia(0.0f, 0.0f, 0.0f);
body->getCollisionShape()->calculateLocalInertia(mass, inertia);
body->setMassProps(mass, inertia);
body->updateInertiaTensor();
}
body->activate();
btVector3 v = body->getLinearVelocity();
btVector3 g = body->getGravity();
// TODO: support collision groups
};
#endif // USE_BULLET_PHYSICS

View file

@ -12,6 +12,24 @@
#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 <btBulletDynamicsCommon.h>
@ -68,12 +86,14 @@ public:
bool removeEntity(CustomMotionState* motionState);
/// \param motionState pointer to Entity's MotionState
/// \param flags set of bits indicating what categories of properties need to be updated
/// \return true if entity updated
bool updateEntityMotionType(CustomMotionState* motionState);
bool updateEntityMassProperties(CustomMotionState* motionState, float mass, const glm::vec3& inertiaEigenValues);
bool updateEntity(CustomMotionState* motionState, uint32_t flags);
protected:
void updateEntityHard(btRigidBody* body, CustomMotionState* motionState, uint32_t flags);
void updateEntityEasy(btRigidBody* body, CustomMotionState* motionState, uint32_t flags);
btClock _clock;
btDefaultCollisionConfiguration* _collisionConfig;
btCollisionDispatcher* _collisionDispatcher;