diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index f968244ab3..6c09569037 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -65,7 +65,13 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) : _marketplaceID(ENTITY_ITEM_DEFAULT_MARKETPLACE_ID), _physicsInfo(NULL), _dirtyFlags(0), - _element(NULL) + _element(NULL), + _previousPositionFromServer(ENTITY_ITEM_ZERO_VEC3), + _previousRotationFromServer(ENTITY_ITEM_DEFAULT_ROTATION), + _previousVelocityFromServer(ENTITY_ITEM_ZERO_VEC3), + _previousAngularVelocityFromServer(ENTITY_ITEM_ZERO_VEC3), + _previousGravityFromServer(ENTITY_ITEM_ZERO_VEC3), + _previousAccelerationFromServer(ENTITY_ITEM_ZERO_VEC3) { quint64 now = usecTimestampNow(); _lastSimulated = now; @@ -592,7 +598,10 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef #ifdef WANT_DEBUG qCDebug(entities) << "skipTimeForward:" << skipTimeForward; #endif - simulateKinematicMotion(skipTimeForward); + + // we want to extrapolate the motion forward to compensate for packet travel time, but + // we don't want the side effect of flag setting. + simulateKinematicMotion(skipTimeForward, false); } _lastSimulated = now; } @@ -725,7 +734,7 @@ void EntityItem::simulate(const quint64& now) { _lastSimulated = now; } -void EntityItem::simulateKinematicMotion(float timeElapsed) { +void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { if (hasAngularVelocity()) { // angular damping if (_angularDamping > 0.0f) { @@ -740,7 +749,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed) { const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { - if (angularSpeed > 0.0f) { + if (setFlags && angularSpeed > 0.0f) { _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; } _angularVelocity = ENTITY_ITEM_ZERO_VEC3; @@ -802,7 +811,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed) { const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f; // 1mm/sec if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) { setVelocity(ENTITY_ITEM_ZERO_VEC3); - if (speed > 0.0f) { + if (setFlags && speed > 0.0f) { _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; } } else { @@ -1089,8 +1098,11 @@ void EntityItem::updatePositionInDomainUnits(const glm::vec3& value) { } void EntityItem::updatePosition(const glm::vec3& value) { - if (glm::distance(_position, value) > MIN_POSITION_DELTA) { + if (value == _previousPositionFromServer) { _position = value; + } else if (glm::distance(_position, value) > MIN_POSITION_DELTA) { + _position = value; + _previousPositionFromServer = value; _dirtyFlags |= EntityItem::DIRTY_POSITION; } } @@ -1108,8 +1120,11 @@ void EntityItem::updateDimensions(const glm::vec3& value) { } void EntityItem::updateRotation(const glm::quat& rotation) { - if (glm::dot(_rotation, rotation) < MIN_ALIGNMENT_DOT) { - _rotation = rotation; + if (rotation == _previousRotationFromServer) { + _rotation = rotation; + } else if (glm::abs(glm::dot(_rotation, rotation)) < MIN_ALIGNMENT_DOT) { + _rotation = rotation; + _previousRotationFromServer = rotation; _dirtyFlags |= EntityItem::DIRTY_POSITION; } } @@ -1144,12 +1159,19 @@ void EntityItem::updateVelocityInDomainUnits(const glm::vec3& value) { } void EntityItem::updateVelocity(const glm::vec3& value) { - if (glm::distance(_velocity, value) > MIN_VELOCITY_DELTA) { + if (value == _previousVelocityFromServer) { if (glm::length(value) < MIN_VELOCITY_DELTA) { _velocity = ENTITY_ITEM_ZERO_VEC3; } else { _velocity = value; } + } else if (glm::distance(_velocity, value) > MIN_VELOCITY_DELTA) { + if (glm::length(value) < MIN_VELOCITY_DELTA) { + _velocity = ENTITY_ITEM_ZERO_VEC3; + } else { + _velocity = value; + } + _previousVelocityFromServer = value; _dirtyFlags |= EntityItem::DIRTY_VELOCITY; } } @@ -1167,20 +1189,38 @@ void EntityItem::updateGravityInDomainUnits(const glm::vec3& value) { } void EntityItem::updateGravity(const glm::vec3& value) { - if ( glm::distance(_gravity, value) > MIN_GRAVITY_DELTA) { + if (value == _previousGravityFromServer) { _gravity = value; + } else if (glm::distance(_gravity, value) > MIN_GRAVITY_DELTA) { + _gravity = value; + _previousGravityFromServer = value; _dirtyFlags |= EntityItem::DIRTY_VELOCITY; } } void EntityItem::updateAcceleration(const glm::vec3& value) { - _acceleration = value; - _dirtyFlags |= EntityItem::DIRTY_VELOCITY; + if (value == _previousAccelerationFromServer) { + _acceleration = value; + } else if (glm::distance(_acceleration, value) > MIN_ACCELERATION_DELTA) { + _acceleration = value; + _previousAccelerationFromServer = value; + _dirtyFlags |= EntityItem::DIRTY_VELOCITY; + } } void EntityItem::updateAngularVelocity(const glm::vec3& value) { - if (glm::distance(_angularVelocity, value) > MIN_SPIN_DELTA) { - _angularVelocity = value; + if (value == _previousAngularVelocityFromServer) { + if (glm::length(value) < MIN_SPIN_DELTA) { + _angularVelocity = ENTITY_ITEM_ZERO_VEC3; + } else { + _angularVelocity = value; + } + } else if (glm::distance(_angularVelocity, value) > MIN_SPIN_DELTA) { + if (glm::length(value) < MIN_SPIN_DELTA) { + _angularVelocity = ENTITY_ITEM_ZERO_VEC3; + } else { + _angularVelocity = value; + } _dirtyFlags |= EntityItem::DIRTY_VELOCITY; } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index fda8167564..fa6efd72a1 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -132,7 +132,7 @@ public: // perform linear extrapolation for SimpleEntitySimulation void simulate(const quint64& now); - void simulateKinematicMotion(float timeElapsed); + void simulateKinematicMotion(float timeElapsed, bool setFlags=true); virtual bool needsToCallUpdate() const { return false; } @@ -368,6 +368,13 @@ protected: uint32_t _dirtyFlags; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation EntityTreeElement* _element; // back pointer to containing Element + + glm::vec3 _previousPositionFromServer; + glm::quat _previousRotationFromServer; + glm::vec3 _previousVelocityFromServer; + glm::vec3 _previousAngularVelocityFromServer; + glm::vec3 _previousGravityFromServer; + glm::vec3 _previousAccelerationFromServer; }; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index d1571fbcc5..0f4bf05fb4 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -189,21 +189,21 @@ float EntityMotionState::computeMass(const ShapeInfo& shapeInfo) const { } bool EntityMotionState::shouldSendUpdate(uint32_t simulationFrame) { + if (getShouldClaimSimulationOwnership()) { + return true; + } + bool baseResult = this->ObjectMotionState::shouldSendUpdate(simulationFrame); if (!baseResult) { return false; } - if (getShouldClaimSimulationOwnership()) { - return true; - } - auto nodeList = DependencyManager::get(); const QUuid& myNodeID = nodeList->getSessionUUID(); const QUuid& simulatorID = _entity->getSimulatorID(); - if (simulatorID != myNodeID) { + if (simulatorID != myNodeID && !simulatorID.isNull()) { // some other Node owns the simulating of this, so don't broadcast the results of local simulation. return false; } @@ -286,15 +286,18 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ QUuid myNodeID = nodeList->getSessionUUID(); QUuid simulatorID = _entity->getSimulatorID(); - if (getShouldClaimSimulationOwnership()) { - _entity->setSimulatorID(myNodeID); - properties.setSimulatorID(myNodeID); - setShouldClaimSimulationOwnership(false); + if (simulatorID.isNull() && !(zeroSpeed && zeroSpin)) { + // the entity is moving and no node has claimed simulation ownership. try to claim it. + setShouldClaimSimulationOwnership(true); } - if (simulatorID == myNodeID && zeroSpeed && zeroSpin) { - // we are the simulator and the object has stopped. give up "simulator" status - _entity->setSimulatorID(QUuid()); + if (getShouldClaimSimulationOwnership()) { + // _entity->setSimulatorID(myNodeID); + properties.setSimulatorID(myNodeID); + setShouldClaimSimulationOwnership(false); + } else if (simulatorID == myNodeID && zeroSpeed && zeroSpin) { + // we are the simulator and the entity has stopped. give up "simulator" status + // _entity->setSimulatorID(QUuid()); properties.setSimulatorID(QUuid()); } diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 45f3c97e30..9d50b71514 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -119,8 +119,10 @@ void PhysicsEngine::entityChangedInternal(EntityItem* entity) { assert(entity); void* physicsInfo = entity->getPhysicsInfo(); if (physicsInfo) { - ObjectMotionState* motionState = static_cast(physicsInfo); - _incomingChanges.insert(motionState); + if ((entity->getDirtyFlags() & (HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS)) > 0) { + ObjectMotionState* motionState = static_cast(physicsInfo); + _incomingChanges.insert(motionState); + } } else { // try to add this entity again (maybe something changed such that it will work this time) addEntity(entity); @@ -366,15 +368,46 @@ void PhysicsEngine::stepNonPhysicalKinematics(const quint64& now) { } } -void PhysicsEngine::computeCollisionEvents() { - BT_PROFILE("computeCollisionEvents"); + +void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB) { + if (!objectA || !objectB) { + return; + } auto nodeList = DependencyManager::get(); QUuid myNodeID = nodeList->getSessionUUID(); - const btCollisionObject* characterCollisionObject = _characterController ? _characterController->getCollisionObject() : NULL; + ObjectMotionState* a = static_cast(objectA->getUserPointer()); + ObjectMotionState* b = static_cast(objectB->getUserPointer()); + EntityItem* entityA = a ? a->getEntity() : NULL; + EntityItem* entityB = b ? b->getEntity() : NULL; + bool aIsDynamic = entityA && !objectA->isStaticOrKinematicObject(); + bool bIsDynamic = entityB && !objectB->isStaticOrKinematicObject(); + + // collisions cause infectious spread of simulation-ownership. we also attempt to take + // ownership of anything that collides with our avatar. + if ((aIsDynamic && entityA->getSimulatorID() == myNodeID) || + (a && a->getShouldClaimSimulationOwnership()) || + (objectA == characterCollisionObject)) { + if (bIsDynamic) { + b->setShouldClaimSimulationOwnership(true); + } + } + if ((bIsDynamic && entityB->getSimulatorID() == myNodeID) || + (b && b->getShouldClaimSimulationOwnership()) || + (objectB == characterCollisionObject)) { + if (aIsDynamic) { + a->setShouldClaimSimulationOwnership(true); + } + } +} + + +void PhysicsEngine::computeCollisionEvents() { + BT_PROFILE("computeCollisionEvents"); + // update all contacts every frame int numManifolds = _collisionDispatcher->getNumManifolds(); for (int i = 0; i < numManifolds; ++i) { @@ -393,31 +426,12 @@ void PhysicsEngine::computeCollisionEvents() { ObjectMotionState* a = static_cast(objectA->getUserPointer()); ObjectMotionState* b = static_cast(objectB->getUserPointer()); - EntityItem* entityA = a ? a->getEntity() : NULL; - EntityItem* entityB = b ? b->getEntity() : NULL; - bool aIsDynamic = entityA && !objectA->isStaticOrKinematicObject(); - bool bIsDynamic = entityB && !objectB->isStaticOrKinematicObject(); - if (a || b) { // the manifold has up to 4 distinct points, but only extract info from the first _contactMap[ContactKey(a, b)].update(_numContactFrames, contactManifold->getContactPoint(0), _originOffset); } - // collisions cause infectious spread of simulation-ownership. we also attempt to take - // ownership of anything that collides with our avatar. - if ((aIsDynamic && entityA->getSimulatorID() == myNodeID) || - (a && a->getShouldClaimSimulationOwnership()) || - (objectA == characterCollisionObject)) { - if (bIsDynamic) { - b->setShouldClaimSimulationOwnership(true); - } - } - if ((bIsDynamic && entityB->getSimulatorID() == myNodeID) || - (b && b->getShouldClaimSimulationOwnership()) || - (objectB == characterCollisionObject)) { - if (aIsDynamic) { - a->setShouldClaimSimulationOwnership(true); - } - } + + doOwnershipInfection(objectA, objectB); } } @@ -453,6 +467,16 @@ void PhysicsEngine::computeCollisionEvents() { if (type == CONTACT_EVENT_TYPE_END) { ContactMap::iterator iterToDelete = contactItr; ++contactItr; + + // const ContactKey& contactKey = (*iterToDelete).first; + // const ObjectMotionState* objectMotionStateA = static_cast(contactKey._a); + // const ObjectMotionState* objectMotionStateB = static_cast(contactKey._b); + // const btCollisionObject* objectA = + // objectMotionStateA ? static_cast(objectMotionStateA->getRigidBody()) : NULL; + // const btCollisionObject* objectB = + // objectMotionStateB ? static_cast(objectMotionStateB->getRigidBody()) : NULL; + // doOwnershipInfection(objectA, objectB); + _contactMap.erase(iterToDelete); } else { ++contactItr; @@ -576,11 +600,16 @@ void PhysicsEngine::removeObjectFromBullet(ObjectMotionState* motionState) { assert(motionState); btRigidBody* body = motionState->getRigidBody(); - // set the about-to-be-deleted entity active in order to wake up the island it's part of. this is done - // so that anything resting on top of it will fall. - // body->setActivationState(ACTIVE_TAG); - EntityItem* entity = static_cast(motionState)->getEntity(); - bump(entity); + // activate this before deleting it so that anything resting on it will begin to fall. + // + // body->activate(); + // + // motionState->setShouldClaimSimulationOwnership(true); + // computeCollisionEvents(); + // + EntityItem* entityItem = motionState ? motionState->getEntity() : NULL; + bump(entityItem); + if (body) { const btCollisionShape* shape = body->getCollisionShape(); diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 148261c6d2..9fe632d462 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -89,16 +89,19 @@ public: void dumpNextStats() { _dumpNextStats = true; } + void bump(EntityItem* bumpEntity); + private: /// \param motionState pointer to Object's MotionState void removeObjectFromBullet(ObjectMotionState* motionState); void removeContacts(ObjectMotionState* motionState); + void doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB); + // return 'true' of update was successful bool updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); - void bump(EntityItem* bumpEntity); btClock _clock; btDefaultCollisionConfiguration* _collisionConfig = NULL;