overte-Armored-Dragon/libraries/physics/src/PhysicsEngine.cpp
2014-12-11 12:07:53 -08:00

414 lines
15 KiB
C++

//
// PhysicsEngine.cpp
// libraries/physcis/src
//
// Created by Andrew Meadows 2014.10.29
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// TODO DONE: make _incomingChanges to be list of MotionState*'s.
// TODO DONE: make MotionState able to clear incoming flags
// TODO: make MotionState::setWorldTransform() put itself on _incomingChanges list
// TODO: make sure incoming and outgoing pipelines are connected
// TODO: give PhysicsEngine instance an _entityPacketSender
// TODO: provide some sort of "reliable" send for "stopped" update
#include "PhysicsEngine.h"
#ifdef USE_BULLET_PHYSICS
#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(),
_frameCount(0) {
}
PhysicsEngine::~PhysicsEngine() {
}
// begin EntitySimulation overrides
void PhysicsEngine::updateEntitiesInternal(const quint64& now) {
QSet<ObjectMotionState*>::iterator stateItr = _outgoingPackets.begin();
uint32_t frame = getFrameCount();
float subStepRemainder = getSubStepRemainder();
while (stateItr != _outgoingPackets.end()) {
ObjectMotionState* state = *stateItr;
if (state->shouldSendUpdate(frame, subStepRemainder)) {
state->sendUpdate(_entityPacketSender);
++stateItr;
} else if (state->isAtRest()) {
stateItr = _outgoingPackets.erase(stateItr);
} else {
++stateItr;
}
}
}
void PhysicsEngine::addEntityInternal(EntityItem* entity) {
assert(entity);
void* physicsInfo = entity->getPhysicsInfo();
if (!physicsInfo) {
EntityMotionState* motionState = new EntityMotionState(entity);
_entityMotionStates.insert(motionState);
if (addObject(motionState)) {
entity->setPhysicsInfo(static_cast<void*>(motionState));
} else {
// We failed to add the object to the simulation. Probably because we couldn't create a shape for it.
delete motionState;
}
}
}
void PhysicsEngine::removeEntityInternal(EntityItem* entity) {
assert(entity);
void* physicsInfo = entity->getPhysicsInfo();
if (physicsInfo) {
EntityMotionState* motionState = static_cast<EntityMotionState*>(physicsInfo);
removeObject(motionState);
entity->setPhysicsInfo(NULL);
_entityMotionStates.remove(motionState);
}
}
void PhysicsEngine::entityChangedInternal(EntityItem* entity) {
// queue incoming changes: from external sources (script, EntityServer, etc) to physics engine
assert(entity);
void* physicsInfo = entity->getPhysicsInfo();
if (physicsInfo) {
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
_incomingChanges.insert(motionState);
}
}
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<EntityMotionState*>::const_iterator stateItr = _entityMotionStates.begin();
for (stateItr = _entityMotionStates.begin(); stateItr != _entityMotionStates.end(); ++stateItr) {
removeObject(*stateItr);
}
_entityMotionStates.clear();
_incomingChanges.clear();
}
// end EntitySimulation overrides
void PhysicsEngine::relayIncomingChangesToSimulation() {
// process incoming changes
QSet<ObjectMotionState*>::iterator stateItr = _incomingChanges.begin();
while (stateItr != _incomingChanges.end()) {
ObjectMotionState* motionState = *stateItr;
uint32_t flags = motionState->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS;
btRigidBody* body = motionState->_body;
if (body) {
if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
// a HARD update requires the body be pulled out of physics engine, changed, then reinserted
// but it also handles all EASY changes
updateObjectHard(body, motionState, flags);
} else if (flags) {
// an EASY update does NOT require that the body be pulled out of physics engine
updateObjectEasy(body, motionState, flags);
}
}
// NOTE: the order of operations is:
// (1) relayIncomingChanges()
// (2) step simulation
// (3) send outgoing packets
// This means incoming changes here (step (1)) should trump corresponding outgoing changes
motionState->clearOutgoingPacketFlags(flags); // trump
motionState->clearIncomingDirtyFlags(flags); // processed
++stateItr;
}
_incomingChanges.clear();
}
// 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 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
_dynamicsWorld->setGravity(btVector3(0.0f, 0.0f, 0.0f));
// GROUND HACK: add a big planar floor (and walls for testing) to catch falling objects
btTransform groundTransform;
groundTransform.setIdentity();
for (int i = 0; i < 3; ++i) {
btVector3 normal(0.0f, 0.0f, 0.0f);
normal[i] = 1.0f;
btCollisionShape* plane = new btStaticPlaneShape(normal, 0.0f);
btCollisionObject* groundObject = new btCollisionObject();
groundObject->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
groundObject->setCollisionShape(plane);
groundObject->setWorldTransform(groundTransform);
_dynamicsWorld->addCollisionObject(groundObject);
}
}
}
const float FIXED_SUBSTEP = 1.0f / 60.0f;
void PhysicsEngine::stepSimulation() {
const int MAX_NUM_SUBSTEPS = 4;
const float MAX_TIMESTEP = (float)MAX_NUM_SUBSTEPS * FIXED_SUBSTEP;
float dt = 1.0e-6f * (float)(_clock.getTimeMicroseconds());
_clock.reset();
float timeStep = btMin(dt, MAX_TIMESTEP);
// TODO: Andrew to build a list of outgoingChanges when motionStates are synched, then send the updates out
int numSubSteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, FIXED_SUBSTEP);
_frameCount += (uint32_t)numSubSteps;
}
bool PhysicsEngine::addVoxel(const glm::vec3& position, float scale) {
glm::vec3 halfExtents = glm::vec3(0.5f * scale);
glm::vec3 trueCenter = position + halfExtents;
PositionHashKey key(trueCenter);
VoxelObject* proxy = _voxels.find(key);
if (!proxy) {
// create a shape
ShapeInfo info;
info.setBox(halfExtents);
btCollisionShape* shape = _shapeManager.getShape(info);
// NOTE: the shape creation will fail when the size of the voxel is out of range
if (shape) {
// create a collisionObject
btCollisionObject* object = new btCollisionObject();
object->setCollisionShape(shape);
btTransform transform;
transform.setIdentity();
// we shift the center into the simulation's frame
glm::vec3 shiftedCenter = (position - _originOffset) + halfExtents;
transform.setOrigin(btVector3(shiftedCenter.x, shiftedCenter.y, shiftedCenter.z));
object->setWorldTransform(transform);
// add to map and world
_voxels.insert(key, VoxelObject(trueCenter, object));
_dynamicsWorld->addCollisionObject(object);
return true;
}
}
return false;
}
bool PhysicsEngine::removeVoxel(const glm::vec3& position, float scale) {
glm::vec3 halfExtents = glm::vec3(0.5f * scale);
glm::vec3 trueCenter = position + halfExtents;
PositionHashKey key(trueCenter);
VoxelObject* proxy = _voxels.find(key);
if (proxy) {
// remove from world
assert(proxy->_object);
_dynamicsWorld->removeCollisionObject(proxy->_object);
// release shape
ShapeInfo info;
info.setBox(halfExtents);
bool released = _shapeManager.releaseShape(info);
assert(released);
// delete object and remove from voxel map
delete proxy->_object;
_voxels.remove(key);
return true;
}
return false;
}
// Bullet collision flags are as follows:
// CF_STATIC_OBJECT= 1,
// CF_KINEMATIC_OBJECT= 2,
// CF_NO_CONTACT_RESPONSE = 4,
// CF_CUSTOM_MATERIAL_CALLBACK = 8,//this allows per-triangle material (friction/restitution)
// CF_CHARACTER_OBJECT = 16,
// CF_DISABLE_VISUALIZE_OBJECT = 32, //disable debug drawing
// CF_DISABLE_SPU_COLLISION_PROCESSING = 64//disable parallel/SPU processing
bool PhysicsEngine::addObject(ObjectMotionState* motionState) {
assert(motionState);
ShapeInfo info;
motionState->computeShapeInfo(info);
btCollisionShape* shape = _shapeManager.getShape(info);
if (shape) {
btVector3 inertia(0.0f, 0.0f, 0.0f);
float mass = 0.0f;
btRigidBody* body = NULL;
switch(motionState->computeMotionType()) {
case MOTION_TYPE_KINEMATIC: {
body = new btRigidBody(mass, motionState, shape, inertia);
body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
body->setActivationState(DISABLE_DEACTIVATION);
body->updateInertiaTensor();
motionState->_body = body;
break;
}
case MOTION_TYPE_DYNAMIC: {
mass = motionState->getMass();
shape->calculateLocalInertia(mass, inertia);
body = new btRigidBody(mass, motionState, shape, inertia);
body->updateInertiaTensor();
motionState->_body = body;
motionState->applyVelocities();
motionState->applyGravity();
break;
}
case MOTION_TYPE_STATIC:
default: {
body = new btRigidBody(mass, motionState, shape, inertia);
body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
body->updateInertiaTensor();
motionState->_body = body;
break;
}
}
// wtf?
body->setFlags(BT_DISABLE_WORLD_GRAVITY);
body->setRestitution(motionState->_restitution);
body->setFriction(motionState->_friction);
_dynamicsWorld->addRigidBody(body);
return true;
}
return false;
}
bool PhysicsEngine::removeObject(ObjectMotionState* motionState) {
assert(motionState);
btRigidBody* body = motionState->_body;
if (body) {
const btCollisionShape* shape = body->getCollisionShape();
ShapeInfo info;
ShapeInfoUtil::collectInfoFromShape(shape, info);
_dynamicsWorld->removeRigidBody(body);
_shapeManager.releaseShape(info);
delete body;
motionState->_body = NULL;
return true;
}
return false;
}
// private
void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) {
MotionType newType = motionState->computeMotionType();
// pull body out of physics engine
_dynamicsWorld->removeRigidBody(body);
if (flags & EntityItem::DIRTY_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 & EntityItem::DIRTY_MASS);
}
bool easyUpdate = flags & EASY_DIRTY_PHYSICS_FLAGS;
if (easyUpdate) {
updateObjectEasy(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 & 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();
body->getCollisionShape()->calculateLocalInertia(mass, inertia);
body->setMassProps(mass, inertia);
body->updateInertiaTensor();
}
body->forceActivationState(ACTIVE_TAG);
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);
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;
}
}
// reinsert body into physics engine
_dynamicsWorld->addRigidBody(body);
body->activate();
}
// private
void PhysicsEngine::updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) {
if (flags & EntityItem::DIRTY_POSITION) {
btTransform transform;
motionState->getWorldTransform(transform);
body->setWorldTransform(transform);
}
if (flags & EntityItem::DIRTY_VELOCITY) {
motionState->applyVelocities();
motionState->applyGravity();
}
body->setRestitution(motionState->_restitution);
body->setFriction(motionState->_friction);
if (flags & EntityItem::DIRTY_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();
// TODO: support collision groups
};
#endif // USE_BULLET_PHYSICS