Merge pull request #4157 from AndrewMeadows/isentropic

client-side kinematic motion for non-physical objects
This commit is contained in:
Brad Hefta-Gaub 2015-01-23 15:00:01 -08:00
commit 494c189094
12 changed files with 189 additions and 261 deletions

View file

@ -520,7 +520,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData);
recalculateCollisionShape();
if (overwriteLocalData && (getDirtyFlags() & EntityItem::DIRTY_POSITION)) {
if (overwriteLocalData && (getDirtyFlags() & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY))) {
_lastSimulated = now;
}
}
@ -607,11 +607,6 @@ bool EntityItem::isRestingOnSurface() const {
}
void EntityItem::simulate(const quint64& now) {
if (_physicsInfo) {
// we rely on bullet for simulation, so bail
return;
}
bool wantDebug = false;
if (_lastSimulated == 0) {
@ -661,9 +656,13 @@ void EntityItem::simulate(const quint64& now) {
qDebug() << " ********** EntityItem::simulate() .... SETTING _lastSimulated=" << _lastSimulated;
}
if (hasAngularVelocity()) {
glm::quat rotation = getRotation();
simulateKinematicMotion(timeElapsed);
_lastSimulated = now;
}
void EntityItem::simulateKinematicMotion(float timeElapsed) {
bool wantDebug = false;
if (hasAngularVelocity()) {
// angular damping
glm::vec3 angularVelocity = getAngularVelocity();
if (_angularDamping > 0.0f) {
@ -679,6 +678,9 @@ void EntityItem::simulate(const quint64& now) {
const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.1f; //
if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) {
if (angularSpeed > 0.0f) {
_dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE;
}
setAngularVelocity(ENTITY_ITEM_ZERO_VEC3);
} else {
// NOTE: angularSpeed is currently in degrees/sec!!!
@ -686,7 +688,7 @@ void EntityItem::simulate(const quint64& now) {
float angle = timeElapsed * glm::radians(angularSpeed);
glm::vec3 axis = _angularVelocity / angularSpeed;
glm::quat dQ = glm::angleAxis(angle, axis);
rotation = glm::normalize(dQ * rotation);
glm::quat rotation = glm::normalize(dQ * getRotation());
setRotation(rotation);
}
}
@ -722,87 +724,31 @@ void EntityItem::simulate(const quint64& now) {
position = newPosition;
// handle bounces off the ground... We bounce at the distance to the bottom of our entity
if (position.y <= getDistanceToBottomOfEntity()) {
velocity = velocity * glm::vec3(1,-1,1);
position.y = getDistanceToBottomOfEntity();
}
// apply gravity
if (hasGravity()) {
// handle resting on surface case, this is definitely a bit of a hack, and it only works on the
// "ground" plane of the domain, but for now it's what we've got
if (isRestingOnSurface()) {
velocity.y = 0.0f;
position.y = getDistanceToBottomOfEntity();
} else {
velocity += getGravity() * timeElapsed;
}
}
// NOTE: we don't zero out very small velocities --> we rely on a remote Bullet simulation
// to tell us when the entity has stopped.
// NOTE: the simulation should NOT set any DirtyFlags on this entity
setPosition(position); // this will automatically recalculate our collision shape
setVelocity(velocity);
if (wantDebug) {
qDebug() << " new position:" << position;
qDebug() << " new velocity:" << velocity;
qDebug() << " new AACube:" << getMaximumAACube();
qDebug() << " old getAABox:" << getAABox();
}
}
_lastSimulated = now;
}
void EntityItem::simulateSimpleKinematicMotion(float timeElapsed) {
if (hasAngularVelocity()) {
// angular damping
glm::vec3 angularVelocity = getAngularVelocity();
if (_angularDamping > 0.0f) {
angularVelocity *= powf(1.0f - _angularDamping, timeElapsed);
setAngularVelocity(angularVelocity);
}
float angularSpeed = glm::length(_angularVelocity);
const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.1f; //
if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) {
setAngularVelocity(ENTITY_ITEM_ZERO_VEC3);
} else {
// NOTE: angularSpeed is currently in degrees/sec!!!
// TODO: Andrew to convert to radians/sec
float angle = timeElapsed * glm::radians(angularSpeed);
glm::vec3 axis = _angularVelocity / angularSpeed;
glm::quat dQ = glm::angleAxis(angle, axis);
glm::quat rotation = getRotation();
rotation = glm::normalize(dQ * rotation);
setRotation(rotation);
}
}
if (hasVelocity()) {
// linear damping
glm::vec3 velocity = getVelocity();
if (_damping > 0.0f) {
velocity *= powf(1.0f - _damping, timeElapsed);
}
// integrate position forward
glm::vec3 position = getPosition() + (velocity * timeElapsed);
// apply gravity
if (hasGravity()) {
// handle resting on surface case, this is definitely a bit of a hack, and it only works on the
// "ground" plane of the domain, but for now it's what we've got
velocity += getGravity() * timeElapsed;
}
// NOTE: the simulation should NOT set any DirtyFlags on this entity
setPosition(position); // this will automatically recalculate our collision shape
setVelocity(velocity);
float speed = glm::length(velocity);
const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f / (float)TREE_SCALE; // 1mm/sec
if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) {
setVelocity(ENTITY_ITEM_ZERO_VEC3);
if (speed > 0.0f) {
_dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE;
}
} else {
setPosition(position);
setVelocity(velocity);
}
if (wantDebug) {
qDebug() << " new position:" << position;
qDebug() << " new velocity:" << velocity;
qDebug() << " new AACube:" << getMaximumAACube();
qDebug() << " old getAABox:" << getAABox();
}
}
}
@ -886,7 +832,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
if (_created != UNKNOWN_CREATED_TIME) {
setLastEdited(now);
}
if (getDirtyFlags() & EntityItem::DIRTY_POSITION) {
if (getDirtyFlags() & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) {
_lastSimulated = now;
}
}

View file

@ -82,6 +82,7 @@ public:
void recordCreationTime(); // set _created to 'now'
quint64 getLastSimulated() const { return _lastSimulated; } /// Last simulated time of this entity universal usecs
void setLastSimulated(quint64 now) { _lastSimulated = now; }
/// Last edited time of this entity universal usecs
quint64 getLastEdited() const { return _lastEdited; }
@ -128,9 +129,8 @@ public:
// perform linear extrapolation for SimpleEntitySimulation
void simulate(const quint64& now);
void simulateKinematicMotion(float timeElapsed);
void simulateSimpleKinematicMotion(float timeElapsed);
virtual bool needsToCallUpdate() const { return false; }
virtual void debugDump() const;

View file

@ -14,7 +14,7 @@
#include "BulletUtil.h"
#include "EntityMotionState.h"
#include "SimpleEntityKinematicController.h"
#include "PhysicsEngine.h"
QSet<EntityItem*>* _outgoingEntityList;
@ -41,8 +41,6 @@ EntityMotionState::~EntityMotionState() {
assert(_entity);
_entity->setPhysicsInfo(NULL);
_entity = NULL;
delete _kinematicController;
_kinematicController = NULL;
}
MotionType EntityMotionState::computeMotionType() const {
@ -52,13 +50,16 @@ MotionType EntityMotionState::computeMotionType() const {
return _entity->isMoving() ? MOTION_TYPE_KINEMATIC : MOTION_TYPE_STATIC;
}
void EntityMotionState::addKinematicController() {
if (!_kinematicController) {
_kinematicController = new SimpleEntityKinematicController(_entity);
_kinematicController->start();
} else {
_kinematicController->start();
}
void EntityMotionState::updateKinematicState(uint32_t substep) {
setKinematic(_entity->isMoving(), substep);
}
void EntityMotionState::stepKinematicSimulation(quint64 now) {
assert(_isKinematic);
// NOTE: this is non-physical kinematic motion which steps to real run-time (now)
// which is different from physical kinematic motion (inside getWorldTransform())
// which steps in physics simulation time.
_entity->simulate(now);
}
// This callback is invoked by the physics simulation in two cases:
@ -67,8 +68,16 @@ void EntityMotionState::addKinematicController() {
// (2) at the beginning of each simulation frame for KINEMATIC RigidBody's --
// it is an opportunity for outside code to update the object's simulation position
void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
if (_kinematicController && _kinematicController->isRunning()) {
_kinematicController->stepForward();
if (_isKinematic) {
// This is physical kinematic motion which steps strictly by the subframe count
// of the physics simulation.
uint32_t substep = PhysicsEngine::getNumSubsteps();
float dt = (substep - _lastKinematicSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP;
_entity->simulateKinematicMotion(dt);
_entity->setLastSimulated(usecTimestampNow());
// bypass const-ness so we can remember the substep
const_cast<EntityMotionState*>(this)->_lastKinematicSubstep = substep;
}
worldTrans.setOrigin(glmToBullet(_entity->getPositionInMeters() - ObjectMotionState::getWorldOffset()));
worldTrans.setRotation(glmToBullet(_entity->getRotation()));
@ -229,12 +238,14 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
uint32_t EntityMotionState::getIncomingDirtyFlags() const {
uint32_t dirtyFlags = _entity->getDirtyFlags();
// we add DIRTY_MOTION_TYPE if the body's motion type disagrees with entity velocity settings
int bodyFlags = _body->getCollisionFlags();
bool isMoving = _entity->isMoving();
if (((bodyFlags & btCollisionObject::CF_STATIC_OBJECT) && isMoving) ||
(bodyFlags & btCollisionObject::CF_KINEMATIC_OBJECT && !isMoving)) {
dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE;
if (_body) {
// we add DIRTY_MOTION_TYPE if the body's motion type disagrees with entity velocity settings
int bodyFlags = _body->getCollisionFlags();
bool isMoving = _entity->isMoving();
if (((bodyFlags & btCollisionObject::CF_STATIC_OBJECT) && isMoving) ||
(bodyFlags & btCollisionObject::CF_KINEMATIC_OBJECT && !isMoving)) {
dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE;
}
}
return dirtyFlags;
}

View file

@ -14,7 +14,6 @@
#include <AACube.h>
#include "KinematicController.h"
#include "ObjectMotionState.h"
class EntityItem;
@ -39,8 +38,8 @@ public:
/// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem
MotionType computeMotionType() const;
// virtual override for ObjectMotionState
void addKinematicController();
void updateKinematicState(uint32_t substep);
void stepKinematicSimulation(quint64 now);
// this relays incoming position/rotation to the RigidBody
void getWorldTransform(btTransform& worldTrans) const;

View file

@ -1,22 +0,0 @@
//
// KinematicController.cpp
// libraries/physcis/src
//
// Created by Andrew Meadows 2015.01.13
// Copyright 2015 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
//
#include "KinematicController.h"
#include "PhysicsEngine.h"
KinematicController::KinematicController() {
_lastSubstep = PhysicsEngine::getNumSubsteps();
}
void KinematicController::start() {
_enabled = true;
_lastSubstep = PhysicsEngine::getNumSubsteps();
}

View file

@ -1,36 +0,0 @@
//
// KinematicController.h
// libraries/physcis/src
//
// Created by Andrew Meadows 2015.01.13
// Copyright 2015 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
//
#ifndef hifi_KinematicController_h
#define hifi_KinematicController_h
#include <stdint.h>
/// KinematicController defines an API for derived classes.
class KinematicController {
public:
KinematicController();
virtual ~KinematicController() {}
virtual void stepForward() = 0;
void start();
void stop() { _enabled = false; }
bool isRunning() const { return _enabled; }
protected:
bool _enabled = false;
uint32_t _lastSubstep;
};
#endif // hifi_KinematicController_h

View file

@ -12,7 +12,6 @@
#include <math.h>
#include "BulletUtil.h"
#include "KinematicController.h"
#include "ObjectMotionState.h"
#include "PhysicsEngine.h"
@ -56,10 +55,6 @@ ObjectMotionState::ObjectMotionState() :
ObjectMotionState::~ObjectMotionState() {
// NOTE: you MUST remove this MotionState from the world before you call the dtor.
assert(_body == NULL);
if (_kinematicController) {
delete _kinematicController;
_kinematicController = NULL;
}
}
void ObjectMotionState::setFriction(float friction) {
@ -108,7 +103,6 @@ bool ObjectMotionState::doesNotNeedToSendUpdate() const {
bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) {
assert(_body);
// if we've never checked before, our _sentFrame will be 0, and we need to initialize our state
if (_sentFrame == 0) {
_sentPosition = bulletToGLM(_body->getWorldTransform().getOrigin());
@ -117,7 +111,7 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) {
_sentFrame = simulationFrame;
return false;
}
float dt = (float)(simulationFrame - _sentFrame) * PHYSICS_ENGINE_FIXED_SUBSTEP;
_sentFrame = simulationFrame;
bool isActive = _body->isActive();
@ -174,13 +168,6 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) {
return (fabsf(glm::dot(actualRotation, _sentRotation)) < MIN_ROTATION_DOT);
}
void ObjectMotionState::removeKinematicController() {
if (_kinematicController) {
delete _kinematicController;
_kinematicController = NULL;
}
}
void ObjectMotionState::setRigidBody(btRigidBody* body) {
// give the body a (void*) back-pointer to this ObjectMotionState
if (_body != body) {
@ -193,3 +180,8 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) {
}
}
}
void ObjectMotionState::setKinematic(bool kinematic, uint32_t substep) {
_isKinematic = kinematic;
_lastKinematicSubstep = substep;
}

View file

@ -46,7 +46,6 @@ const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_POSITION | Entit
class OctreeEditPacketSender;
class KinematicController;
class ObjectMotionState : public btMotionState {
public:
@ -93,11 +92,15 @@ public:
virtual MotionType computeMotionType() const = 0;
virtual void addKinematicController() = 0;
virtual void removeKinematicController();
virtual void updateKinematicState(uint32_t substep) = 0;
btRigidBody* getRigidBody() const { return _body; }
bool isKinematic() const { return _isKinematic; }
void setKinematic(bool kinematic, uint32_t substep);
virtual void stepKinematicSimulation(quint64 now) = 0;
friend class PhysicsEngine;
protected:
void setRigidBody(btRigidBody* body);
@ -114,6 +117,9 @@ protected:
btRigidBody* _body;
bool _isKinematic = false;
uint32_t _lastKinematicSubstep = 0;
bool _sentMoving; // true if last update was moving
int _numNonMovingUpdates; // RELIABLE_SEND_HACK for "not so reliable" resends of packets for non-moving objects
@ -124,8 +130,6 @@ protected:
glm::vec3 _sentVelocity;
glm::vec3 _sentAngularVelocity; // radians per second
glm::vec3 _sentAcceleration;
KinematicController* _kinematicController = NULL;
};
#endif // hifi_ObjectMotionState_h

View file

@ -62,7 +62,13 @@ void PhysicsEngine::addEntityInternal(EntityItem* entity) {
entity->setPhysicsInfo(static_cast<void*>(motionState));
_entityMotionStates.insert(motionState);
addObject(shapeInfo, shape, motionState);
} else {
} else if (entity->isMoving()) {
EntityMotionState* motionState = new EntityMotionState(entity);
entity->setPhysicsInfo(static_cast<void*>(motionState));
_entityMotionStates.insert(motionState);
motionState->setKinematic(true, _numSubsteps);
_nonPhysicalKinematicObjects.insert(motionState);
// We failed to add the entity to the simulation. Probably because we couldn't create a shape for it.
//qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine";
}
@ -74,10 +80,16 @@ void PhysicsEngine::removeEntityInternal(EntityItem* entity) {
void* physicsInfo = entity->getPhysicsInfo();
if (physicsInfo) {
EntityMotionState* motionState = static_cast<EntityMotionState*>(physicsInfo);
removeObject(motionState);
if (motionState->getRigidBody()) {
removeObject(motionState);
} else {
// only need to hunt in this list when there is no RigidBody
_nonPhysicalKinematicObjects.remove(motionState);
}
_entityMotionStates.remove(motionState);
_incomingChanges.remove(motionState);
_outgoingPackets.remove(motionState);
// NOTE: EntityMotionState dtor will remove its backpointer from EntityItem
delete motionState;
}
}
@ -117,6 +129,7 @@ void PhysicsEngine::clearEntitiesInternal() {
delete (*stateItr);
}
_entityMotionStates.clear();
_nonPhysicalKinematicObjects.clear();
_incomingChanges.clear();
_outgoingPackets.clear();
}
@ -127,19 +140,75 @@ void PhysicsEngine::relayIncomingChangesToSimulation() {
QSet<ObjectMotionState*>::iterator stateItr = _incomingChanges.begin();
while (stateItr != _incomingChanges.end()) {
ObjectMotionState* motionState = *stateItr;
++stateItr;
uint32_t flags = motionState->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS;
bool removeMotionState = false;
btRigidBody* body = motionState->getRigidBody();
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);
bool success = updateObjectHard(body, motionState, flags);
if (!success) {
// NOTE: since updateObjectHard() failed we know that motionState has been removed
// from simulation and body has been deleted. Depending on what else has changed
// we might need to remove motionState altogether...
if (flags & EntityItem::DIRTY_VELOCITY) {
motionState->updateKinematicState(_numSubsteps);
if (motionState->isKinematic()) {
// all is NOT lost, we still need to move this object around kinematically
_nonPhysicalKinematicObjects.insert(motionState);
} else {
// no need to keep motionState around
removeMotionState = true;
}
} else {
// no need to keep motionState around
removeMotionState = true;
}
}
} else if (flags) {
// an EASY update does NOT require that the body be pulled out of physics engine
// hence the MotionState has all the knowledge and authority to perform the update.
motionState->updateObjectEasy(flags, _numSubsteps);
}
} else {
// the only way we should ever get here (motionState exists but no body) is when the object
// is undergoing non-physical kinematic motion.
assert(_nonPhysicalKinematicObjects.contains(motionState));
// it is possible that the changes are such that the object can now be added to the physical simulation
if (flags & EntityItem::DIRTY_SHAPE) {
ShapeInfo shapeInfo;
motionState->computeShapeInfo(shapeInfo);
btCollisionShape* shape = _shapeManager.getShape(shapeInfo);
if (shape) {
addObject(shapeInfo, shape, motionState);
_nonPhysicalKinematicObjects.remove(motionState);
} else if (flags & EntityItem::DIRTY_VELOCITY) {
// although we couldn't add the object to the simulation, might need to update kinematic motion...
motionState->updateKinematicState(_numSubsteps);
if (!motionState->isKinematic()) {
_nonPhysicalKinematicObjects.remove(motionState);
removeMotionState = true;
}
}
} else if (flags & EntityItem::DIRTY_VELOCITY) {
// although we still can't add to physics simulation, might need to update kinematic motion...
motionState->updateKinematicState(_numSubsteps);
if (!motionState->isKinematic()) {
_nonPhysicalKinematicObjects.remove(motionState);
removeMotionState = true;
}
}
}
if (removeMotionState) {
// if we get here then there is no need to keep this motionState around (no physics or kinematics)
_outgoingPackets.remove(motionState);
// NOTE: motionState will clean up its own backpointers in the Object
delete motionState;
continue;
}
// NOTE: the grand order of operations is:
@ -152,7 +221,6 @@ void PhysicsEngine::relayIncomingChangesToSimulation() {
// outgoing changes at this point.
motionState->clearOutgoingPacketFlags(flags); // clear outgoing flags that were trumped
motionState->clearIncomingDirtyFlags(flags); // clear incoming flags that were processed
++stateItr;
}
_incomingChanges.clear();
}
@ -213,6 +281,7 @@ void PhysicsEngine::stepSimulation() {
// This is step (2).
int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP);
_numSubsteps += (uint32_t)numSubsteps;
stepNonPhysicalKinematics(usecTimestampNow());
unlock();
if (numSubsteps > 0) {
@ -234,6 +303,17 @@ void PhysicsEngine::stepSimulation() {
}
}
void PhysicsEngine::stepNonPhysicalKinematics(const quint64& now) {
QSet<ObjectMotionState*>::iterator stateItr = _nonPhysicalKinematicObjects.begin();
while (stateItr != _nonPhysicalKinematicObjects.end()) {
ObjectMotionState* motionState = *stateItr;
motionState->stepKinematicSimulation(now);
++stateItr;
}
}
// TODO?: need to occasionally scan for stopped non-physical kinematics objects
void PhysicsEngine::computeCollisionEvents() {
// update all contacts every frame
int numManifolds = _collisionDispatcher->getNumManifolds();
@ -332,7 +412,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap
body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
body->updateInertiaTensor();
motionState->setRigidBody(body);
motionState->addKinematicController();
motionState->setKinematic(true, _numSubsteps);
const float KINEMATIC_LINEAR_VELOCITY_THRESHOLD = 0.01f; // 1 cm/sec
const float KINEMATIC_ANGULAR_VELOCITY_THRESHOLD = 0.01f; // ~1 deg/sec
body->setSleepingThresholds(KINEMATIC_LINEAR_VELOCITY_THRESHOLD, KINEMATIC_ANGULAR_VELOCITY_THRESHOLD);
@ -344,6 +424,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap
body = new btRigidBody(mass, motionState, shape, inertia);
body->updateInertiaTensor();
motionState->setRigidBody(body);
motionState->setKinematic(false, _numSubsteps);
motionState->updateObjectVelocities();
// NOTE: Bullet will deactivate any object whose velocity is below these thresholds for longer than 2 seconds.
// (the 2 seconds is determined by: static btRigidBody::gDeactivationTime
@ -358,6 +439,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap
body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
body->updateInertiaTensor();
motionState->setRigidBody(body);
motionState->setKinematic(false, _numSubsteps);
break;
}
}
@ -368,7 +450,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap
_dynamicsWorld->addRigidBody(body);
}
bool PhysicsEngine::removeObject(ObjectMotionState* motionState) {
void PhysicsEngine::removeObject(ObjectMotionState* motionState) {
assert(motionState);
btRigidBody* body = motionState->getRigidBody();
if (body) {
@ -379,16 +461,14 @@ bool PhysicsEngine::removeObject(ObjectMotionState* motionState) {
_shapeManager.releaseShape(shapeInfo);
delete body;
motionState->setRigidBody(NULL);
motionState->removeKinematicController();
motionState->setKinematic(false, _numSubsteps);
removeContacts(motionState);
return true;
}
return false;
}
// private
void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) {
bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) {
MotionType newType = motionState->computeMotionType();
// pull body out of physics engine
@ -403,7 +483,16 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
ShapeInfo shapeInfo;
motionState->computeShapeInfo(shapeInfo);
btCollisionShape* newShape = _shapeManager.getShape(shapeInfo);
if (newShape != oldShape) {
if (!newShape) {
// FAIL! we are unable to support these changes!
_shapeManager.releaseShape(oldShape);
delete body;
motionState->setRigidBody(NULL);
motionState->setKinematic(false, _numSubsteps);
removeContacts(motionState);
return false;
} else if (newShape != oldShape) {
// BUG: if shape doesn't change but density does then we won't compute new mass properties
// TODO: fix this BUG by replacing DIRTY_MASS with DIRTY_DENSITY and then fix logic accordingly.
body->setCollisionShape(newShape);
@ -436,7 +525,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
body->setMassProps(0.0f, btVector3(0.0f, 0.0f, 0.0f));
body->updateInertiaTensor();
motionState->addKinematicController();
motionState->setKinematic(true, _numSubsteps);
break;
}
case MOTION_TYPE_DYNAMIC: {
@ -453,7 +542,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
body->updateInertiaTensor();
}
body->forceActivationState(ACTIVE_TAG);
motionState->removeKinematicController();
motionState->setKinematic(false, _numSubsteps);
break;
}
default: {
@ -468,7 +557,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
body->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f));
body->setAngularVelocity(btVector3(0.0f, 0.0f, 0.0f));
motionState->removeKinematicController();
motionState->setKinematic(false, _numSubsteps);
break;
}
}
@ -477,4 +566,5 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
_dynamicsWorld->addRigidBody(body);
body->activate();
return true;
}

View file

@ -67,6 +67,7 @@ public:
virtual void init(EntityEditPacketSender* packetSender);
void stepSimulation();
void stepNonPhysicalKinematics(const quint64& now);
void computeCollisionEvents();
@ -81,15 +82,16 @@ public:
void addObject(const ShapeInfo& shapeInfo, btCollisionShape* shape, ObjectMotionState* motionState);
/// \param motionState pointer to Object's MotionState
/// \return true if Object removed
bool removeObject(ObjectMotionState* motionState);
void removeObject(ObjectMotionState* motionState);
/// process queue of changed from external sources
void relayIncomingChangesToSimulation();
private:
void removeContacts(ObjectMotionState* motionState);
void updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags);
// return 'true' of update was successful
bool updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags);
void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags);
btClock _clock;
@ -104,6 +106,7 @@ private:
// EntitySimulation stuff
QSet<EntityMotionState*> _entityMotionStates; // all entities that we track
QSet<ObjectMotionState*> _nonPhysicalKinematicObjects; // not in physics simulation, but still need kinematic simulation
QSet<ObjectMotionState*> _incomingChanges; // entities with pending physics changes by script or packet
QSet<ObjectMotionState*> _outgoingPackets; // MotionStates with pending changes that need to be sent over wire

View file

@ -1,21 +0,0 @@
//
// SimpleEntityKinematicController.cpp
// libraries/physcis/src
//
// Created by Andrew Meadows 2015.01.13
// Copyright 2015 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
//
#include "PhysicsEngine.h"
#include "SimpleEntityKinematicController.h"
void SimpleEntityKinematicController:: stepForward() {
uint32_t substep = PhysicsEngine::getNumSubsteps();
float dt = (substep - _lastSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP;
_entity->simulateSimpleKinematicMotion(dt);
_lastSubstep = substep;
}

View file

@ -1,38 +0,0 @@
//
// SimpleEntityKinematicController.h
// libraries/physcis/src
//
// Created by Andrew Meadows 2015.01.13
// Copyright 2015 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
//
#ifndef hifi_SimpleEntityKinematicController_h
#define hifi_SimpleEntityKinematicController_h
/// SimpleKinematicConstroller performs simple exrapolation of velocities.
#include <assert.h>
#include <glm/glm.hpp>
#include <EntityItem.h>
#include "KinematicController.h"
class SimpleEntityKinematicController : public KinematicController {
public:
SimpleEntityKinematicController() = delete; // prevent compiler from making a default ctor
SimpleEntityKinematicController(EntityItem* entity) : KinematicController(), _entity(entity) { assert(entity); }
~SimpleEntityKinematicController() { _entity = NULL; }
void stepForward();
private:
EntityItem* _entity;
};
#endif // hifi_SimpleEntityKinematicController_h