diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 7809c3d07c..60e5286ad8 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -15,13 +15,11 @@ Script.include("../libraries/utils.js"); -//////////////////////////////////////////////////////////// // // add lines where the hand ray picking is happening // var WANT_DEBUG = false; -///////////////////////////////////////////////////////////////// // // these tune time-averaging and "on" value for analog trigger // @@ -30,7 +28,6 @@ var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value var TRIGGER_ON_VALUE = 0.4; var TRIGGER_OFF_VALUE = 0.15; -///////////////////////////////////////////////////////////////// // // distant manipulation // @@ -44,7 +41,6 @@ var LINE_ENTITY_DIMENSIONS = { x: 1000, y: 1000,z: 1000}; var LINE_LENGTH = 500; var PICK_MAX_DISTANCE = 500; // max length of pick-ray -///////////////////////////////////////////////////////////////// // // near grabbing // @@ -55,8 +51,8 @@ var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object +var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed -///////////////////////////////////////////////////////////////// // // other constants // @@ -79,6 +75,18 @@ var ACTION_TTL_REFRESH = 5; var PICKS_PER_SECOND_PER_HAND = 5; var MSECS_PER_SEC = 1000.0; + +var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js +var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js + +var DEFAULT_GRABBABLE_DATA = { + grabbable: true, + invertSolidWhileHeld: false +}; + +var disabledHand ='none'; + + // states for the state machine var STATE_OFF = 0; var STATE_SEARCHING = 1; @@ -92,15 +100,35 @@ var STATE_FAR_GRABBING_NON_COLLIDING = 8; var STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING = 9; var STATE_RELEASE = 10; -var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js -var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js -var DEFAULT_GRABBABLE_DATA = { - grabbable: true, - invertSolidWhileHeld: false -}; +function stateToName(state) { + switch (state) { + case STATE_OFF: + return "off"; + case STATE_SEARCHING: + return "searching"; + case STATE_DISTANCE_HOLDING: + return "distance_holding"; + case STATE_CONTINUE_DISTANCE_HOLDING: + return "continue_distance_holding"; + case STATE_NEAR_GRABBING: + return "near_grabbing"; + case STATE_CONTINUE_NEAR_GRABBING: + return "continue_near_grabbing"; + case STATE_NEAR_GRABBING_NON_COLLIDING: + return "near_grabbing_non_colliding"; + case STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING: + return "continue_near_grabbing_non_colliding"; + case STATE_FAR_GRABBING_NON_COLLIDING: + return "far_grabbing_non_colliding"; + case STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING: + return "continue_far_grabbing_non_colliding"; + case STATE_RELEASE: + return "release"; + } -var disabledHand ='none'; + return "unknown"; +} function getTag() { return "grab-" + MyAvatar.sessionUUID; @@ -196,7 +224,7 @@ function MyController(hand, triggerAction) { this.setState = function(newState) { if (WANT_DEBUG) { - print("STATE: " + this.state + " --> " + newState); + print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand); } this.state = newState; } @@ -353,10 +381,11 @@ function MyController(hand, triggerAction) { } if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { // the hand is very close to the intersected object. go into close-grabbing mode. - if (intersection.properties.collisionsWillMove === 1) { - this.setState(STATE_NEAR_GRABBING); - } else { + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + if (grabbableData.wantsTrigger) { this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); + } else { + this.setState(STATE_NEAR_GRABBING); } } else { // don't allow two people to distance grab the same object @@ -397,22 +426,22 @@ function MyController(hand, triggerAction) { } if (this.grabbedEntity === null) { return; - } else if (props.locked === 0 && props.collisionsWillMove === 1) { - this.setState(STATE_NEAR_GRABBING); - } else if (props.collisionsWillMove === 0 && grabbableData.wantsTrigger) { - // We have grabbed a non-physical object, so we want to trigger a non-colliding event as opposed to a grab event + } + if (grabbableData.wantsTrigger) { this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); + } else if (props.locked === 0) { + this.setState(STATE_NEAR_GRABBING); } } }; this.distanceHolding = function() { - var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation", - "gravity", "ignoreForCollisions"]); + "gravity", "ignoreForCollisions", + var now = Date.now(); // add the action and initialize some variables @@ -557,8 +586,14 @@ function MyController(hand, triggerAction) { this.lineOff(); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, - ["position", "rotation", "gravity", "ignoreForCollisions"]); + ["position", "rotation", "gravity", + "ignoreForCollisions", "collisionsWillMove"]); this.activateEntity(this.grabbedEntity, grabbedProperties); + if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { + Entities.editEntity(this.grabbedEntity, { + collisionsWillMove: false + }); + } var handRotation = this.getHandRotation(); var handPosition = this.getHandPosition(); @@ -587,7 +622,9 @@ function MyController(hand, triggerAction) { timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: this.offsetPosition, relativeRotation: this.offsetRotation, - ttl: ACTION_TTL + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true }); if (this.actionID === NULL_ACTION_ID) { this.actionID = null; @@ -639,7 +676,9 @@ function MyController(hand, triggerAction) { timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: this.offsetPosition, relativeRotation: this.offsetRotation, - ttl: ACTION_TTL + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true }); this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); } @@ -808,12 +847,13 @@ function MyController(hand, triggerAction) { Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); } + this.deactivateEntity(this.grabbedEntity); + // the action will tend to quickly bring an object's velocity to zero. now that // the action is gone, set the objects velocity to something the holder might expect. Entities.editEntity(this.grabbedEntity, { velocity: this.grabbedVelocity }); - this.deactivateEntity(this.grabbedEntity); this.grabbedVelocity = ZERO_VEC; this.grabbedEntity = null; @@ -836,6 +876,7 @@ function MyController(hand, triggerAction) { if (data["refCount"] == 1) { data["gravity"] = grabbedProperties.gravity; data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions; + data["collisionsWillMove"] = grabbedProperties.collisionsWillMove; var whileHeldProperties = {gravity: {x:0, y:0, z:0}}; if (invertSolidWhileHeld) { whileHeldProperties["ignoreForCollisions"] = ! grabbedProperties.ignoreForCollisions; @@ -853,7 +894,8 @@ function MyController(hand, triggerAction) { if (data["refCount"] < 1) { Entities.editEntity(entityID, { gravity: data["gravity"], - ignoreForCollisions: data["ignoreForCollisions"] + ignoreForCollisions: data["ignoreForCollisions"], + collisionsWillMove: data["collisionsWillMove"] }); data = null; } diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 238e48d2fd..e7d030e17a 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -119,6 +119,8 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { worldTrans.setRotation(glmToBullet(_rotationalTarget)); rigidBody->setWorldTransform(worldTrans); + motionState->dirtyInternalKinematicChanges(); + _previousPositionalTarget = _positionalTarget; _previousRotationalTarget = _rotationalTarget; _previousSet = true; @@ -224,6 +226,8 @@ QVariantMap AvatarActionHold::getArguments() { arguments["relativeRotation"] = glmToQMap(_relativeRotation); arguments["timeScale"] = _linearTimeScale; arguments["hand"] = _hand; + arguments["kinematic"] = _kinematic; + arguments["kinematicSetVelocity"] = _kinematicSetVelocity; }); return arguments; } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 6832b8daff..42aaea33c2 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -146,7 +146,7 @@ MotionType EntityMotionState::computeObjectMotionType() const { if (_entity->getCollisionsWillMove()) { return MOTION_TYPE_DYNAMIC; } - return _entity->isMoving() ? MOTION_TYPE_KINEMATIC : MOTION_TYPE_STATIC; + return (_entity->isMoving() || _entity->hasActions()) ? MOTION_TYPE_KINEMATIC : MOTION_TYPE_STATIC; } bool EntityMotionState::isMoving() const { @@ -184,6 +184,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { if (!_entity) { return; } + assert(entityTreeIsLocked()); measureBodyAcceleration(); _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset()); diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index ff8382a143..3188283f68 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -16,7 +16,6 @@ ObjectAction::ObjectAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) : btActionInterface(), EntityActionInterface(type, id), - _active(false), _ownerEntity(ownerEntity) { } @@ -35,7 +34,9 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta if (ownerEntityExpired) { qDebug() << "warning -- action with no entity removing self from btCollisionWorld."; btDynamicsWorld* dynamicsWorld = static_cast(collisionWorld); - dynamicsWorld->removeAction(this); + if (dynamicsWorld) { + dynamicsWorld->removeAction(this); + } return; } @@ -120,6 +121,17 @@ QVariantMap ObjectAction::getArguments() { arguments["ttl"] = (float)(_expires - now) / (float)USECS_PER_SECOND; } arguments["tag"] = _tag; + + EntityItemPointer entity = _ownerEntity.lock(); + if (entity) { + ObjectMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (motionState) { + arguments["::active"] = motionState->isActive(); + arguments["::motion-type"] = motionTypeToString(motionState->getMotionType()); + } else { + arguments["::no-motion-state"] = true; + } + } }); return arguments; } diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index fca446aec4..45b40a9fb3 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -50,6 +50,8 @@ public: virtual quint64 getExpires() { return _expires; } protected: + quint64 localTimeToServerTime(quint64 timeValue) const; + quint64 serverTimeToLocalTime(quint64 timeValue) const; virtual btRigidBody* getRigidBody(); virtual glm::vec3 getPosition(); @@ -62,14 +64,10 @@ protected: virtual void setAngularVelocity(glm::vec3 angularVelocity); virtual void activateBody(); - bool _active; EntityItemWeakPointer _ownerEntity; - - quint64 _expires; // in seconds since epoch QString _tag; - - quint64 localTimeToServerTime(quint64 timeValue) const; - quint64 serverTimeToLocalTime(quint64 timeValue) const; + quint64 _expires { 0 }; // in seconds since epoch + bool _active { false }; private: int getEntityServerClockSkew() const; diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 4f3d0396c6..be0edafff5 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -20,17 +20,17 @@ // origin of physics simulation in world-frame glm::vec3 _worldOffset(0.0f); -// static +// static void ObjectMotionState::setWorldOffset(const glm::vec3& offset) { _worldOffset = offset; } -// static +// static const glm::vec3& ObjectMotionState::getWorldOffset() { return _worldOffset; } -// static +// static uint32_t worldSimulationStep = 0; void ObjectMotionState::setWorldSimulationStep(uint32_t step) { assert(step > worldSimulationStep); @@ -41,7 +41,7 @@ uint32_t ObjectMotionState::getWorldSimulationStep() { return worldSimulationStep; } -// static +// static ShapeManager* shapeManager = nullptr; void ObjectMotionState::setShapeManager(ShapeManager* manager) { assert(manager); @@ -85,7 +85,7 @@ glm::vec3 ObjectMotionState::getBodyLinearVelocity() const { glm::vec3 ObjectMotionState::getBodyLinearVelocityGTSigma() const { // NOTE: the threshold to use here relates to the linear displacement threshold (dX) for sending updates - // to objects that are tracked server-side (e.g. entities which use dX = 2mm). Hence an object moving + // to objects that are tracked server-side (e.g. entities which use dX = 2mm). Hence an object moving // just under this velocity threshold would trigger an update about V/dX times per second. const float MIN_LINEAR_SPEED_SQUARED = 0.0036f; // 6 mm/sec diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 450ac34a90..992bdd11d7 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -29,18 +29,27 @@ enum MotionType { MOTION_TYPE_KINEMATIC // keyframed motion }; +inline QString motionTypeToString(MotionType motionType) { + switch(motionType) { + case MOTION_TYPE_STATIC: return QString("static"); + case MOTION_TYPE_DYNAMIC: return QString("dynamic"); + case MOTION_TYPE_KINEMATIC: return QString("kinematic"); + } + return QString("unknown"); +} + enum MotionStateType { MOTIONSTATE_TYPE_INVALID, MOTIONSTATE_TYPE_ENTITY, MOTIONSTATE_TYPE_AVATAR }; -// The update flags trigger two varieties of updates: "hard" which require the body to be pulled +// 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 HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_SHAPE | +const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_SHAPE | Simulation::DIRTY_COLLISION_GROUP); const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES | - Simulation::DIRTY_MASS | Simulation::DIRTY_MATERIAL | + Simulation::DIRTY_MASS | Simulation::DIRTY_MATERIAL | Simulation::DIRTY_SIMULATOR_ID | Simulation::DIRTY_SIMULATOR_OWNERSHIP); // These are the set of incoming flags that the PhysicsEngine needs to hear about: @@ -57,7 +66,7 @@ class PhysicsEngine; class ObjectMotionState : public btMotionState { public: // These poroperties of the PhysicsEngine are "global" within the context of all ObjectMotionStates - // (assuming just one PhysicsEngine). They are cached as statics for fast calculations in the + // (assuming just one PhysicsEngine). They are cached as statics for fast calculations in the // ObjectMotionState context. static void setWorldOffset(const glm::vec3& offset); static const glm::vec3& getWorldOffset(); @@ -112,7 +121,7 @@ public: virtual float getObjectFriction() const = 0; virtual float getObjectLinearDamping() const = 0; virtual float getObjectAngularDamping() const = 0; - + virtual glm::vec3 getObjectPosition() const = 0; virtual glm::quat getObjectRotation() const = 0; virtual glm::vec3 getObjectLinearVelocity() const = 0; @@ -131,6 +140,11 @@ public: bool isActive() const { return _body ? _body->isActive() : false; } + bool hasInternalKinematicChanges() const { return _hasInternalKinematicChanges; } + + void dirtyInternalKinematicChanges() { _hasInternalKinematicChanges = true; } + void clearInternalKinematicChanges() { _hasInternalKinematicChanges = false; } + friend class PhysicsEngine; protected: @@ -151,6 +165,7 @@ protected: float _mass; uint32_t _lastKinematicStep; + bool _hasInternalKinematicChanges { false }; }; typedef QSet SetOfMotionStates; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 1e652b75c4..f3ef855e50 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -82,12 +82,12 @@ void PhysicsEngine::addObject(ObjectMotionState* motionState) { btCollisionShape* shape = motionState->getShape(); assert(shape); body = new btRigidBody(mass, motionState, shape, inertia); + motionState->setRigidBody(body); } else { body->setMassProps(mass, inertia); } body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); body->updateInertiaTensor(); - motionState->setRigidBody(body); motionState->updateBodyVelocities(); const float KINEMATIC_LINEAR_VELOCITY_THRESHOLD = 0.01f; // 1 cm/sec const float KINEMATIC_ANGULAR_VELOCITY_THRESHOLD = 0.01f; // ~1 deg/sec @@ -101,12 +101,15 @@ void PhysicsEngine::addObject(ObjectMotionState* motionState) { shape->calculateLocalInertia(mass, inertia); if (!body) { body = new btRigidBody(mass, motionState, shape, inertia); + motionState->setRigidBody(body); } else { body->setMassProps(mass, inertia); } + body->setCollisionFlags(body->getCollisionFlags() & ~(btCollisionObject::CF_KINEMATIC_OBJECT | + btCollisionObject::CF_STATIC_OBJECT)); body->updateInertiaTensor(); - motionState->setRigidBody(body); motionState->updateBodyVelocities(); + // 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 const float DYNAMIC_LINEAR_VELOCITY_THRESHOLD = 0.05f; // 5 cm/sec @@ -123,12 +126,12 @@ void PhysicsEngine::addObject(ObjectMotionState* motionState) { if (!body) { assert(motionState->getShape()); body = new btRigidBody(mass, motionState, motionState->getShape(), inertia); + motionState->setRigidBody(body); } else { body->setMassProps(mass, inertia); } body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT); body->updateInertiaTensor(); - motionState->setRigidBody(body); break; } } diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index 94d6315705..d06a9b8e07 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -4,8 +4,8 @@ * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from the use of this software. - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it freely, + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it freely, * subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. @@ -75,7 +75,7 @@ int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep } } - // NOTE: We do NOT call synchronizeMotionState() after each substep (to avoid multiple locks on the + // NOTE: We do NOT call synchronizeMotionStates() after each substep (to avoid multiple locks on the // object data outside of the physics engine). A consequence of this is that the transforms of the // external objects only ever update at the end of the full step. @@ -87,6 +87,33 @@ int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep return subSteps; } +// call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState() +void ThreadSafeDynamicsWorld::synchronizeMotionState(btRigidBody* body) { + btAssert(body); + if (body->getMotionState() && !body->isStaticObject()) { + //we need to call the update at least once, even for sleeping objects + //otherwise the 'graphics' transform never updates properly + ///@todo: add 'dirty' flag + //if (body->getActivationState() != ISLAND_SLEEPING) + { + if (body->isKinematicObject()) { + ObjectMotionState* objectMotionState = static_cast(body->getMotionState()); + if (objectMotionState->hasInternalKinematicChanges()) { + objectMotionState->clearInternalKinematicChanges(); + body->getMotionState()->setWorldTransform(body->getWorldTransform()); + } + return; + } + btTransform interpolatedTransform; + btTransformUtil::integrateTransform(body->getInterpolationWorldTransform(), + body->getInterpolationLinearVelocity(),body->getInterpolationAngularVelocity(), + (m_latencyMotionStateInterpolation && m_fixedTimeStep) ? m_localTime - m_fixedTimeStep : m_localTime*body->getHitFraction(), + interpolatedTransform); + body->getMotionState()->setWorldTransform(interpolatedTransform); + } + } +} + void ThreadSafeDynamicsWorld::synchronizeMotionStates() { _changedMotionStates.clear(); BT_PROFILE("synchronizeMotionStates"); @@ -97,22 +124,22 @@ void ThreadSafeDynamicsWorld::synchronizeMotionStates() { btRigidBody* body = btRigidBody::upcast(colObj); if (body) { if (body->getMotionState()) { - synchronizeSingleMotionState(body); + synchronizeMotionState(body); _changedMotionStates.push_back(static_cast(body->getMotionState())); } } } - } else { + } else { //iterate over all active rigid bodies for (int i=0;iisActive()) { if (body->getMotionState()) { - synchronizeSingleMotionState(body); + synchronizeMotionState(body); _changedMotionStates.push_back(static_cast(body->getMotionState())); } } } - } -} + } +} diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.h b/libraries/physics/src/ThreadSafeDynamicsWorld.h index 26954832cf..de37554f56 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.h +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.h @@ -4,8 +4,8 @@ * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from the use of this software. - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it freely, + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it freely, * subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. @@ -43,13 +43,16 @@ public: void synchronizeMotionStates(); // btDiscreteDynamicsWorld::m_localTime is the portion of real-time that has not yet been simulated - // but is used for MotionState::setWorldTransform() extrapolation (a feature that Bullet uses to provide + // but is used for MotionState::setWorldTransform() extrapolation (a feature that Bullet uses to provide // smoother rendering of objects when the physics simulation loop is ansynchronous to the render loop). float getLocalTimeAccumulation() const { return m_localTime; } VectorOfMotionStates& getChangedMotionStates() { return _changedMotionStates; } private: + // call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState() + void synchronizeMotionState(btRigidBody* body); + VectorOfMotionStates _changedMotionStates; };