From e3351c05a27579e6b53d987e26c801a0f93d661d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 2 Feb 2016 14:26:59 -0800 Subject: [PATCH 1/8] make some methods const --- libraries/entities/src/EntityItem.cpp | 2 +- libraries/entities/src/EntityItem.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 99dff84407..b2102ed740 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -987,7 +987,7 @@ EntityTreePointer EntityItem::getTree() const { return tree; } -bool EntityItem::wantTerseEditLogging() { +bool EntityItem::wantTerseEditLogging() const { EntityTreePointer tree = getTree(); return tree ? tree->wantTerseEditLogging() : false; } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index c8bf62f83e..8271aedb15 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -104,7 +104,7 @@ public: quint64 getLastBroadcast() const { return _lastBroadcast; } void setLastBroadcast(quint64 lastBroadcast) { _lastBroadcast = lastBroadcast; } - void markAsChangedOnServer() { _changedOnServer = usecTimestampNow(); } + void markAsChangedOnServer() { _changedOnServer = usecTimestampNow(); } quint64 getLastChangedOnServer() const { return _changedOnServer; } // TODO: eventually only include properties changed since the params.lastViewFrustumSent time @@ -351,14 +351,14 @@ public: void setPhysicsInfo(void* data) { _physicsInfo = data; } EntityTreeElementPointer getElement() const { return _element; } EntityTreePointer getTree() const; - bool wantTerseEditLogging(); + bool wantTerseEditLogging() const; glm::mat4 getEntityToWorldMatrix() const; glm::mat4 getWorldToEntityMatrix() const; glm::vec3 worldToEntity(const glm::vec3& point) const; glm::vec3 entityToWorld(const glm::vec3& point) const; - quint64 getLastEditedFromRemote() { return _lastEditedFromRemote; } + quint64 getLastEditedFromRemote() const { return _lastEditedFromRemote; } void getAllTerseUpdateProperties(EntityItemProperties& properties) const; From 05fb866bb5cfdacb46a33d83bf1a3210d764d960 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 2 Feb 2016 14:27:14 -0800 Subject: [PATCH 2/8] fix spelling typo in comment --- libraries/entities/src/EntityItemProperties.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 0a72789485..8e0983f62a 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -910,7 +910,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem success = packetData->startSubTree(octcode); delete[] octcode; - // assuming we have rome to fit our octalCode, proceed... + // assuming we have room to fit our octalCode, proceed... if (success) { // Now add our edit content details... From 381049acb3d0393c7b52f3e31ccbf7498927799f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 2 Feb 2016 14:28:59 -0800 Subject: [PATCH 3/8] clear simulation ownership when owners vanish --- .../entities/src/SimpleEntitySimulation.cpp | 44 ++++++++++++------- .../entities/src/SimpleEntitySimulation.h | 1 + 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index 120b536161..f0461abb97 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -15,34 +15,46 @@ #include "SimpleEntitySimulation.h" #include "EntitiesLogging.h" -const quint64 AUTO_REMOVE_SIMULATION_OWNER_USEC = 2 * USECS_PER_SECOND; +const quint64 MIN_SIMULATION_OWNERSHIP_UPDATE_PERIOD = 2 * USECS_PER_SECOND; void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) { - // If an Entity has a simulation owner and we don't get an update for some amount of time, - // clear the owner. This guards against an interface failing to release the Entity when it - // has finished simulating it. - auto nodeList = DependencyManager::get(); + if (_entitiesWithSimulator.size() == 0) { + return; + } + + if (now < _nextSimulationExpiry) { + // nothing has expired yet + return; + } + + // If an Entity has a simulation owner but there has been no update for a while: clear the owner. + // If an Entity goes ownerless for too long: zero velocity and remove from _entitiesWithSimulator. + _nextSimulationExpiry = now + MIN_SIMULATION_OWNERSHIP_UPDATE_PERIOD; QMutexLocker lock(&_mutex); SetOfEntities::iterator itemItr = _entitiesWithSimulator.begin(); while (itemItr != _entitiesWithSimulator.end()) { EntityItemPointer entity = *itemItr; - if (entity->getSimulatorID().isNull()) { - itemItr = _entitiesWithSimulator.erase(itemItr); - } else if (now - entity->getLastChangedOnServer() >= AUTO_REMOVE_SIMULATION_OWNER_USEC) { - SharedNodePointer ownerNode = nodeList->nodeWithUUID(entity->getSimulatorID()); - if (ownerNode.isNull() || !ownerNode->isAlive()) { - qCDebug(entities) << "auto-removing simulation owner" << entity->getSimulatorID(); - entity->clearSimulationOwnership(); - itemItr = _entitiesWithSimulator.erase(itemItr); + quint64 expiry = entity->getLastChangedOnServer() + MIN_SIMULATION_OWNERSHIP_UPDATE_PERIOD; + if (expiry < now) { + if (entity->getSimulatorID().isNull()) { + // no simulators are volunteering // zero the velocity on this entity so that it doesn't drift far away entity->setVelocity(glm::vec3(0.0f)); + // remove from list + itemItr = _entitiesWithSimulator.erase(itemItr); + continue; } else { - ++itemItr; + // the simulator has stopped updating this object + // clear ownership and restart timer, giving nearby simulators time to volunteer + qCDebug(entities) << "auto-removing simulation owner " << entity.getSimulatorID(); + entity->clearSimulationOwnership(); + entity->markAsChangedOnServer(); } - } else { - ++itemItr; + } else if (expiry < _nextSimulationExpiry) { + _nextSimulationExpiry = expiry; } + ++itemItr; } } diff --git a/libraries/entities/src/SimpleEntitySimulation.h b/libraries/entities/src/SimpleEntitySimulation.h index 83c51525a8..53a7574bf2 100644 --- a/libraries/entities/src/SimpleEntitySimulation.h +++ b/libraries/entities/src/SimpleEntitySimulation.h @@ -29,6 +29,7 @@ protected: virtual void clearEntitiesInternal() override; SetOfEntities _entitiesWithSimulator; + quint64 _nextSimulationExpiry { 0 }; }; #endif // hifi_SimpleEntitySimulation_h From e51edaa1170f5532d706feb7f6887f1feb60c534 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 2 Feb 2016 16:46:07 -0800 Subject: [PATCH 4/8] update some comments --- libraries/physics/src/EntityMotionState.cpp | 29 +++++++++---------- libraries/physics/src/EntityMotionState.h | 6 ++-- .../physics/src/PhysicalEntitySimulation.cpp | 3 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 84d2282e7a..52151b7136 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -104,25 +104,25 @@ bool EntityMotionState::handleEasyChanges(uint32_t& flags) { if (flags & Simulation::DIRTY_SIMULATOR_ID) { _loopsWithoutOwner = 0; if (_entity->getSimulatorID().isNull()) { - // simulation ownership is being removed - // remove the ACTIVATION flag because this object is coming to rest - // according to a remote simulation and we don't want to wake it up again + // simulation ownership has been removed by an external simulator + // --> clear the ACTIVATION flag and outgoing priority because this object is coming to rest flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION; - // hint to Bullet that the object is deactivating _body->setActivationState(WANTS_DEACTIVATION); _outgoingPriority = NO_PRORITY; - } else { + } else { + // this entity's simulation is owned by someone, so we push its ownership expiry into the future _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; if (Physics::getSessionUUID() == _entity->getSimulatorID() || _entity->getSimulationPriority() >= _outgoingPriority) { - // we own the simulation or our priority looses to (or ties with) remote + // either we already own the simulation or our old outgoing priority momentarily looses to current owner + // so we clear it _outgoingPriority = NO_PRORITY; } } } if (flags & Simulation::DIRTY_SIMULATOR_OWNERSHIP) { - // (DIRTY_SIMULATOR_OWNERSHIP really means "we should bid for ownership with SCRIPT priority") - // we're manipulating this object directly via script, so we artificially - // manipulate the logic to trigger an immediate bid for ownership + // The DIRTY_SIMULATOR_OWNERSHIP bit really means "we should bid for ownership at SCRIPT priority". + // Since that bit is set there must be a local script that is updating the physics properties of the objects + // therefore we upgrade _outgoingPriority to trigger a bid for ownership. setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY); } if ((flags & Simulation::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) { @@ -203,7 +203,6 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { _loopsWithoutOwner++; if (_loopsWithoutOwner > LOOPS_FOR_SIMULATION_ORPHAN && usecTimestampNow() > _nextOwnershipBid) { - //qDebug() << "Warning -- claiming something I saw moving." << getName(); setOutgoingPriority(VOLUNTEER_SIMULATION_PRIORITY); } } @@ -235,9 +234,8 @@ btCollisionShape* EntityMotionState::computeNewShape() { } bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const { - if (!_body || !_entity) { - return false; - } + assert(_body); + assert(_entity); assert(entityTreeIsLocked()); return _outgoingPriority != NO_PRORITY || sessionID == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit(); } @@ -374,10 +372,11 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s } if (_entity->getSimulatorID() != sessionID) { - // we don't own the simulation, but maybe we should... + // we don't own the simulation if (_outgoingPriority != NO_PRORITY) { + // but we would like to own it if (_outgoingPriority < _entity->getSimulationPriority()) { - // our priority loses to remote, so we don't bother to bid + // but our priority loses to remote, so we don't bother trying _outgoingPriority = NO_PRORITY; return false; } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 2c999d0aca..7e350a4bae 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -106,7 +106,7 @@ protected: // Meanwhile we also keep a raw EntityItem* for internal stuff where the pointer is guaranteed valid. EntityItem* _entity; - bool _sentInactive; // true if body was inactive when we sent last update + bool _triedToReleaseOwnership; // true if we tried to release ownership // these are for the prediction of the remote server's simple extrapolation uint32_t _lastStep; // last step of server extrapolation @@ -124,8 +124,8 @@ protected: float _measuredDeltaTime; quint8 _accelerationNearlyGravityCount; - quint64 _nextOwnershipBid = NO_PRORITY; - uint32_t _loopsWithoutOwner; + quint64 _nextOwnershipBid { 0 }; + quint64 _orphanExpiry { 0 }; quint8 _outgoingPriority = NO_PRORITY; }; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index d2c91f29dd..d5e8d972fe 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -249,6 +249,7 @@ void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result) void PhysicalEntitySimulation::handleOutgoingChanges(const VectorOfMotionStates& motionStates, const QUuid& sessionID) { QMutexLocker lock(&_mutex); + // walk the motionStates looking for those that correspond to entities for (auto stateItr : motionStates) { ObjectMotionState* state = &(*stateItr); @@ -273,7 +274,7 @@ void PhysicalEntitySimulation::handleOutgoingChanges(const VectorOfMotionStates& return; } - // send outgoing packets + // look for entities that need outgoing packets QSet::iterator stateItr = _outgoingChanges.begin(); while (stateItr != _outgoingChanges.end()) { EntityMotionState* state = *stateItr; From 91a2f86482667b7c300b778e3a620d595d89ee67 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 Feb 2016 13:22:26 -0800 Subject: [PATCH 5/8] update some comments --- libraries/physics/src/PhysicalEntitySimulation.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index d5e8d972fe..71c78b8b86 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -274,13 +274,15 @@ void PhysicalEntitySimulation::handleOutgoingChanges(const VectorOfMotionStates& return; } - // look for entities that need outgoing packets + // look for entities to prune or update QSet::iterator stateItr = _outgoingChanges.begin(); while (stateItr != _outgoingChanges.end()) { EntityMotionState* state = *stateItr; if (!state->isCandidateForOwnership(sessionID)) { + // prune stateItr = _outgoingChanges.erase(stateItr); } else if (state->shouldSendUpdate(numSubsteps, sessionID)) { + // update state->sendUpdate(_entityPacketSender, sessionID, numSubsteps); ++stateItr; } else { From 959f924b1d3fd3e05cea16b934b853579f69e0bf Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 Feb 2016 13:22:48 -0800 Subject: [PATCH 6/8] flag entity as changed when changing simulatorID --- libraries/entities/src/EntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index b2102ed740..a4a646f532 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -652,6 +652,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef } if (_simulationOwner.set(newSimOwner)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; + somethingChanged = true; } } { // When we own the simulation we don't accept updates to the entity's transform/velocities From af57f5d12085acdec96472fb55f9880fcbc2546b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 Feb 2016 13:24:08 -0800 Subject: [PATCH 7/8] add simple and fast DirtyOctreeElementOperator --- .../octree/src/DirtyOctreeElementOperator.cpp | 30 +++++++++++++++++++ .../octree/src/DirtyOctreeElementOperator.h | 30 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 libraries/octree/src/DirtyOctreeElementOperator.cpp create mode 100644 libraries/octree/src/DirtyOctreeElementOperator.h diff --git a/libraries/octree/src/DirtyOctreeElementOperator.cpp b/libraries/octree/src/DirtyOctreeElementOperator.cpp new file mode 100644 index 0000000000..0f7e6d9f04 --- /dev/null +++ b/libraries/octree/src/DirtyOctreeElementOperator.cpp @@ -0,0 +1,30 @@ +// +// DirtyOctreeElementOperator.cpp +// libraries/entities/src +// +// Created by Andrew Meawdows 2016.02.04 +// Copyright 2016 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 "DirtyOctreeElementOperator.h" + +DirtyOctreeElementOperator::DirtyOctreeElementOperator(OctreeElementPointer element) + : _element(element) { + assert(_element.get()); + _point = _element->getAACube().calcCenter(); +} + +bool DirtyOctreeElementOperator::preRecursion(OctreeElementPointer element) { + if (element == _element) { + return false; + } + return element->getAACube().contains(_point); +} + +bool DirtyOctreeElementOperator::postRecursion(OctreeElementPointer element) { + element->markWithChangedTime(); + return true; +} diff --git a/libraries/octree/src/DirtyOctreeElementOperator.h b/libraries/octree/src/DirtyOctreeElementOperator.h new file mode 100644 index 0000000000..a5eec780a0 --- /dev/null +++ b/libraries/octree/src/DirtyOctreeElementOperator.h @@ -0,0 +1,30 @@ +// +// DirtyOctreeElementOperator.h +// libraries/entities/src +// +// Created by Andrew Meawdows 2016.02.04 +// Copyright 2016 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_DirtyOctreeElementOperator_h +#define hifi_DirtyOctreeElementOperator_h + +#include "Octree.h" + +class DirtyOctreeElementOperator : public RecurseOctreeOperator { +public: + DirtyOctreeElementOperator(OctreeElementPointer element); + + ~DirtyOctreeElementOperator() {} + + virtual bool preRecursion(OctreeElementPointer element); + virtual bool postRecursion(OctreeElementPointer element); +private: + glm::vec3 _point; + OctreeElementPointer _element; +}; + +#endif // hifi_DirtyOctreeElementOperator_h From 2da46ff26a16d8f812d12745735c121856c5e1f8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 Feb 2016 13:24:30 -0800 Subject: [PATCH 8/8] server-side release ownership --- .../entities/src/SimpleEntitySimulation.cpp | 16 +++- libraries/entities/src/SimulationOwner.cpp | 9 +-- libraries/entities/src/SimulationOwner.h | 4 +- libraries/physics/src/EntityMotionState.cpp | 80 +++++++++++-------- libraries/physics/src/EntityMotionState.h | 27 +++---- 5 files changed, 76 insertions(+), 60 deletions(-) diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index f0461abb97..bdf27f4440 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -11,8 +11,11 @@ //#include -#include "EntityItem.h" #include "SimpleEntitySimulation.h" + +#include + +#include "EntityItem.h" #include "EntitiesLogging.h" const quint64 MIN_SIMULATION_OWNERSHIP_UPDATE_PERIOD = 2 * USECS_PER_SECOND; @@ -40,17 +43,22 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) { if (entity->getSimulatorID().isNull()) { // no simulators are volunteering // zero the velocity on this entity so that it doesn't drift far away - entity->setVelocity(glm::vec3(0.0f)); + entity->setVelocity(Vectors::ZERO); + entity->setAngularVelocity(Vectors::ZERO); + entity->setAcceleration(Vectors::ZERO); // remove from list itemItr = _entitiesWithSimulator.erase(itemItr); continue; } else { // the simulator has stopped updating this object // clear ownership and restart timer, giving nearby simulators time to volunteer - qCDebug(entities) << "auto-removing simulation owner " << entity.getSimulatorID(); + qCDebug(entities) << "auto-removing simulation owner " << entity->getSimulatorID(); entity->clearSimulationOwnership(); - entity->markAsChangedOnServer(); } + entity->markAsChangedOnServer(); + // dirty all the tree elements that contain the entity + DirtyOctreeElementOperator op(entity->getElement()); + getEntityTree()->recurseTreeWithOperator(&op); } else if (expiry < _nextSimulationExpiry) { _nextSimulationExpiry = expiry; } diff --git a/libraries/entities/src/SimulationOwner.cpp b/libraries/entities/src/SimulationOwner.cpp index 24f6784954..669d811df9 100644 --- a/libraries/entities/src/SimulationOwner.cpp +++ b/libraries/entities/src/SimulationOwner.cpp @@ -16,11 +16,11 @@ #include -// static +// static const int SimulationOwner::NUM_BYTES_ENCODED = NUM_BYTES_RFC4122_UUID + 1; -SimulationOwner::SimulationOwner(const SimulationOwner& other) +SimulationOwner::SimulationOwner(const SimulationOwner& other) : _id(other._id), _priority(other._priority), _expiry(other._expiry) { } @@ -48,11 +48,6 @@ void SimulationOwner::clear() { void SimulationOwner::setPriority(quint8 priority) { _priority = priority; - if (_priority == 0) { - // when priority is zero we clear everything - _expiry = 0; - _id = QUuid(); - } } void SimulationOwner::promotePriority(quint8 priority) { diff --git a/libraries/entities/src/SimulationOwner.h b/libraries/entities/src/SimulationOwner.h index a848ad6e84..bd17128003 100644 --- a/libraries/entities/src/SimulationOwner.h +++ b/libraries/entities/src/SimulationOwner.h @@ -18,10 +18,10 @@ #include #include -const quint8 NO_PRORITY = 0x00; +const quint8 ZERO_SIMULATION_PRIORITY = 0x00; // Simulation observers will bid to simulate unowned active objects at the lowest possible priority -// which is VOLUNTEER. If the server accepts a VOLUNTEER bid it will automatically bump it +// which is VOLUNTEER. If the server accepts a VOLUNTEER bid it will automatically bump it // to RECRUIT priority so that other volunteers don't accidentally take over. const quint8 VOLUNTEER_SIMULATION_PRIORITY = 0x01; const quint8 RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 52151b7136..155186e891 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -26,10 +26,7 @@ #include "EntityTree.h" #endif -static const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f; -static const quint8 STEPS_TO_DECIDE_BALLISTIC = 4; - -const uint32_t LOOPS_FOR_SIMULATION_ORPHAN = 50; +const uint8_t LOOPS_FOR_SIMULATION_ORPHAN = 50; const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5; #ifdef WANT_DEBUG_ENTITY_TREE_LOCKS @@ -52,8 +49,6 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer ObjectMotionState(shape), _entityPtr(entity), _entity(entity.get()), - _sentInactive(true), - _lastStep(0), _serverPosition(0.0f), _serverRotation(), _serverVelocity(0.0f), @@ -61,13 +56,16 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer _serverGravity(0.0f), _serverAcceleration(0.0f), _serverActionData(QByteArray()), - _lastMeasureStep(0), _lastVelocity(glm::vec3(0.0f)), _measuredAcceleration(glm::vec3(0.0f)), - _measuredDeltaTime(0.0f), - _accelerationNearlyGravityCount(0), _nextOwnershipBid(0), - _loopsWithoutOwner(0) + _measuredDeltaTime(0.0f), + _lastMeasureStep(0), + _lastStep(0), + _loopsWithoutOwner(0), + _accelerationNearlyGravityCount(0), + _numInactiveUpdates(1), + _outgoingPriority(ZERO_SIMULATION_PRIORITY) { _type = MOTIONSTATE_TYPE_ENTITY; assert(_entity); @@ -102,20 +100,28 @@ bool EntityMotionState::handleEasyChanges(uint32_t& flags) { ObjectMotionState::handleEasyChanges(flags); if (flags & Simulation::DIRTY_SIMULATOR_ID) { - _loopsWithoutOwner = 0; if (_entity->getSimulatorID().isNull()) { // simulation ownership has been removed by an external simulator - // --> clear the ACTIVATION flag and outgoing priority because this object is coming to rest - flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION; - _body->setActivationState(WANTS_DEACTIVATION); - _outgoingPriority = NO_PRORITY; + if (glm::length2(_entity->getVelocity()) == 0.0f) { + // this object is coming to rest --> clear the ACTIVATION flag and outgoing priority + flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION; + _body->setActivationState(WANTS_DEACTIVATION); + _outgoingPriority = ZERO_SIMULATION_PRIORITY; + _loopsWithoutOwner = 0; + } else { + // unowned object is still moving --> we should volunteer to own it + // TODO? put a delay in here proportional to distance from object? + setOutgoingPriority(VOLUNTEER_SIMULATION_PRIORITY); + _loopsWithoutOwner = LOOPS_FOR_SIMULATION_ORPHAN; + _nextOwnershipBid = 0; + } } else { // this entity's simulation is owned by someone, so we push its ownership expiry into the future _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; if (Physics::getSessionUUID() == _entity->getSimulatorID() || _entity->getSimulationPriority() >= _outgoingPriority) { // either we already own the simulation or our old outgoing priority momentarily looses to current owner // so we clear it - _outgoingPriority = NO_PRORITY; + _outgoingPriority = ZERO_SIMULATION_PRIORITY; } } } @@ -237,10 +243,11 @@ bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const { assert(_body); assert(_entity); assert(entityTreeIsLocked()); - return _outgoingPriority != NO_PRORITY || sessionID == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit(); + return _outgoingPriority != ZERO_SIMULATION_PRIORITY || sessionID == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit(); } bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { + // NOTE: we only get here if we think we own the simulation assert(_body); // if we've never checked before, our _lastStep will be 0, and we need to initialize our state if (_lastStep == 0) { @@ -251,7 +258,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { _serverAngularVelocity = bulletToGLM(_body->getAngularVelocity()); _lastStep = simulationStep; _serverActionData = _entity->getActionData(); - _sentInactive = true; + _numInactiveUpdates = 1; return false; } @@ -264,16 +271,21 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { int numSteps = simulationStep - _lastStep; float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP; - const float INACTIVE_UPDATE_PERIOD = 0.5f; - if (_sentInactive) { + if (_numInactiveUpdates > 0) { + const uint8_t MAX_NUM_INACTIVE_UPDATES = 3; + if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES) { + // clear local ownership (stop sending updates) and let the server clear itself + _entity->clearSimulationOwnership(); + return false; + } // we resend the inactive update every INACTIVE_UPDATE_PERIOD // until it is removed from the outgoing updates // (which happens when we don't own the simulation and it isn't touching our simulation) + const float INACTIVE_UPDATE_PERIOD = 0.5f; return (dt > INACTIVE_UPDATE_PERIOD); } - bool isActive = _body->isActive(); - if (!isActive) { + if (!_body->isActive()) { // object has gone inactive but our last send was moving --> send non-moving update immediately return true; } @@ -373,11 +385,11 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s if (_entity->getSimulatorID() != sessionID) { // we don't own the simulation - if (_outgoingPriority != NO_PRORITY) { + if (_outgoingPriority != ZERO_SIMULATION_PRIORITY) { // but we would like to own it if (_outgoingPriority < _entity->getSimulationPriority()) { // but our priority loses to remote, so we don't bother trying - _outgoingPriority = NO_PRORITY; + _outgoingPriority = ZERO_SIMULATION_PRIORITY; return false; } return usecTimestampNow() > _nextOwnershipBid; @@ -399,10 +411,12 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q _entity->setVelocity(zero); _entity->setAngularVelocity(zero); _entity->setAcceleration(zero); - _sentInactive = true; + _numInactiveUpdates++; } else { + const uint8_t STEPS_TO_DECIDE_BALLISTIC = 4; float gravityLength = glm::length(_entity->getGravity()); float accVsGravity = glm::abs(glm::length(_measuredAcceleration) - gravityLength); + const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f; if (accVsGravity < ACCELERATION_EQUIVALENT_EPSILON_RATIO * gravityLength) { // acceleration measured during the most recent simulation step was close to gravity. if (getAccelerationNearlyGravityCount() < STEPS_TO_DECIDE_BALLISTIC) { @@ -439,7 +453,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q _entity->setVelocity(zero); _entity->setAngularVelocity(zero); } - _sentInactive = false; + _numInactiveUpdates = 0; } // remember properties for local server prediction @@ -487,12 +501,12 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q // we own the simulation but the entity has stopped, so we tell the server that we're clearing simulatorID // but we remember that we do still own it... and rely on the server to tell us that we don't properties.clearSimulationOwner(); - _outgoingPriority = NO_PRORITY; + _outgoingPriority = ZERO_SIMULATION_PRIORITY; } // else the ownership is not changing so we don't bother to pack it } else { // we don't own the simulation for this entity yet, but we're sending a bid for it - properties.setSimulationOwner(sessionID, glm::max(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY)); + properties.setSimulationOwner(sessionID, glm::max(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY)); _nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS; } @@ -557,7 +571,7 @@ void EntityMotionState::clearIncomingDirtyFlags() { } // virtual -quint8 EntityMotionState::getSimulationPriority() const { +uint8_t EntityMotionState::getSimulationPriority() const { return _entity->getSimulationPriority(); } @@ -567,7 +581,7 @@ QUuid EntityMotionState::getSimulatorID() const { return _entity->getSimulatorID(); } -void EntityMotionState::bump(quint8 priority) { +void EntityMotionState::bump(uint8_t priority) { setOutgoingPriority(glm::max(VOLUNTEER_SIMULATION_PRIORITY, --priority)); } @@ -600,7 +614,7 @@ void EntityMotionState::measureBodyAcceleration() { if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS) { _loopsWithoutOwner = 0; _lastStep = ObjectMotionState::getWorldSimulationStep(); - _sentInactive = false; + _numInactiveUpdates = 0; } } } @@ -630,6 +644,6 @@ void EntityMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& ma _entity->computeCollisionGroupAndFinalMask(group, mask); } -void EntityMotionState::setOutgoingPriority(quint8 priority) { - _outgoingPriority = glm::max(_outgoingPriority, priority); +void EntityMotionState::setOutgoingPriority(uint8_t priority) { + _outgoingPriority = glm::max(_outgoingPriority, priority); } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 7e350a4bae..cab8448dd9 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -53,7 +53,7 @@ public: void incrementAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount++; } void resetAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount = 0; } - quint8 getAccelerationNearlyGravityCount() { return _accelerationNearlyGravityCount; } + uint8_t getAccelerationNearlyGravityCount() { return _accelerationNearlyGravityCount; } virtual float getObjectRestitution() const override { return _entity->getRestitution(); } virtual float getObjectFriction() const override { return _entity->getFriction(); } @@ -69,9 +69,9 @@ public: virtual const QUuid getObjectID() const override { return _entity->getID(); } - virtual quint8 getSimulationPriority() const override; + virtual uint8_t getSimulationPriority() const override; virtual QUuid getSimulatorID() const override; - virtual void bump(quint8 priority) override; + virtual void bump(uint8_t priority) override; EntityItemPointer getEntity() const { return _entityPtr.lock(); } @@ -83,7 +83,7 @@ public: virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override; // eternal logic can suggest a simuator priority bid for the next outgoing update - void setOutgoingPriority(quint8 priority); + void setOutgoingPriority(uint8_t priority); friend class PhysicalEntitySimulation; @@ -106,10 +106,6 @@ protected: // Meanwhile we also keep a raw EntityItem* for internal stuff where the pointer is guaranteed valid. EntityItem* _entity; - bool _triedToReleaseOwnership; // true if we tried to release ownership - - // these are for the prediction of the remote server's simple extrapolation - uint32_t _lastStep; // last step of server extrapolation glm::vec3 _serverPosition; // in simulation-frame (not world-frame) glm::quat _serverRotation; glm::vec3 _serverVelocity; @@ -118,15 +114,18 @@ protected: glm::vec3 _serverAcceleration; QByteArray _serverActionData; - uint32_t _lastMeasureStep; glm::vec3 _lastVelocity; glm::vec3 _measuredAcceleration; - float _measuredDeltaTime; - - quint8 _accelerationNearlyGravityCount; quint64 _nextOwnershipBid { 0 }; - quint64 _orphanExpiry { 0 }; - quint8 _outgoingPriority = NO_PRORITY; + + float _measuredDeltaTime; + uint32_t _lastMeasureStep; + uint32_t _lastStep; // last step of server extrapolation + + uint8_t _loopsWithoutOwner; + uint8_t _accelerationNearlyGravityCount; + uint8_t _numInactiveUpdates { 1 }; + uint8_t _outgoingPriority { ZERO_SIMULATION_PRIORITY }; }; #endif // hifi_EntityMotionState_h