From d4abdcb6c8e1df93e9286a7a62458feb2bf31ebc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 6 Mar 2017 14:09:10 -0800 Subject: [PATCH 1/6] comments, namechange, and temporary debug code --- interface/src/Application.cpp | 4 +- interface/src/avatar/AvatarManager.cpp | 2 +- interface/src/avatar/AvatarManager.h | 2 +- libraries/entities/src/EntityItem.cpp | 21 ++++-- libraries/physics/src/EntityMotionState.cpp | 67 +++++++++++++++++-- .../physics/src/PhysicalEntitySimulation.cpp | 5 +- .../physics/src/PhysicalEntitySimulation.h | 4 +- .../physics/src/ThreadSafeDynamicsWorld.cpp | 3 + 8 files changed, 92 insertions(+), 16 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f870bd9f83..4894ae55ec 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4440,8 +4440,8 @@ void Application::update(float deltaTime) { getEntities()->getTree()->withWriteLock([&] { PerformanceTimer perfTimer("handleOutgoingChanges"); const VectorOfMotionStates& outgoingChanges = _physicsEngine->getOutgoingChanges(); - _entitySimulation->handleOutgoingChanges(outgoingChanges); - avatarManager->handleOutgoingChanges(outgoingChanges); + _entitySimulation->handleChangedMotionStates(outgoingChanges); + avatarManager->handleChangedMotionStates(outgoingChanges); }); if (!_aboutToQuit) { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 94ce444416..6152148887 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -424,7 +424,7 @@ void AvatarManager::getObjectsToChange(VectorOfMotionStates& result) { } } -void AvatarManager::handleOutgoingChanges(const VectorOfMotionStates& motionStates) { +void AvatarManager::handleChangedMotionStates(const VectorOfMotionStates& motionStates) { // TODO: extract the MyAvatar results once we use a MotionState for it. } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index e1f5a3b411..b94f9e6a96 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -70,7 +70,7 @@ public: void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates); void getObjectsToAddToPhysics(VectorOfMotionStates& motionStates); void getObjectsToChange(VectorOfMotionStates& motionStates); - void handleOutgoingChanges(const VectorOfMotionStates& motionStates); + void handleChangedMotionStates(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); Q_INVOKABLE float getAvatarDataRate(const QUuid& sessionID, const QString& rateName = QString("")) const; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 3ef1648fae..db253b639f 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -655,13 +655,11 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // pack SimulationOwner and terse update properties near each other - // NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data // even when we would otherwise ignore the rest of the packet. bool filterRejection = false; if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) { - QByteArray simOwnerData; int bytes = OctreePacketData::unpackDataFromBytes(dataAt, simOwnerData); SimulationOwner newSimOwner; @@ -676,6 +674,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // or rejects a set of properties, it clears this. In such cases, we don't want those custom // setters to ignore what the server says. filterRejection = newSimOwner.getID().isNull(); + bool verbose = getName() == "fubar"; // adebug + if (verbose && _simulationOwner != newSimOwner) { + std::cout << (void*)(this) << " adebug ownership changed " + << _simulationOwner.getID().toString().toStdString() << "." << (int)_simulationOwner.getPriority() << "-->" + << newSimOwner.getID().toString().toStdString() << "." << (int)newSimOwner.getPriority() + << std::endl; // adebug + } if (weOwnSimulation) { if (newSimOwner.getID().isNull() && !_simulationOwner.pendingRelease(lastEditedFromBufferAdjusted)) { // entity-server is trying to clear our ownership (probably at our own request) @@ -1879,12 +1884,16 @@ void EntityItem::setSimulationOwner(const SimulationOwner& owner) { } void EntityItem::updateSimulationOwner(const SimulationOwner& owner) { + // NOTE: this method only used by EntityServer. The Interface uses special code in readEntityDataFromBuffer(). if (wantTerseEditLogging() && _simulationOwner != owner) { qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << owner; } if (_simulationOwner.set(owner)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; + if (getName() == "fubar") { + std::cout << "debug updateSimulationOwner() " << _simulationOwner.getID().toString().toStdString() << "." << (int)(_simulationOwner.getPriority()) << std::endl; // adebug + } } } @@ -1892,10 +1901,14 @@ void EntityItem::clearSimulationOwnership() { if (wantTerseEditLogging() && !_simulationOwner.isNull()) { qCDebug(entities) << "sim ownership for" << getDebugName() << "is now null"; } + if (getName() == "fubar") { + std::cout << "debug clearSimulationOwnership()" << std::endl; // adebug + } _simulationOwner.clear(); - // don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulationOwnership() - // is only ever called on the entity-server and the flags are only used client-side + // don't bother setting the DIRTY_SIMULATOR_ID flag because: + // (a) when entity-server calls clearSimulationOwnership() the dirty-flags are meaningless (only used by interface) + // (b) the interface only calls clearSimulationOwnership() in a context that already knows best about dirty flags //_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index c175a836cc..77c5a4f697 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -27,6 +27,18 @@ #include "EntityTree.h" #endif +// adebug TODO BOOKMARK: +// The problem is that userB may deactivate and disown and object before userA deactivates +// userA will sometimes insert non-zero velocities (and position errors) into the Entity before it is deactivated locally +// +// It would be nice to prevent data export from Bullet to Entity for unowned objects except in cases where it is really needed (?) +// Maybe can recycle _serverPosition and friends to store the "before simulationStep" data to more efficiently figure out +// if data should be used for non-owned objects. +// +// If we do that, we should convert _serverPosition and friends to use Bullet data types for efficiency. +// +// adebug + const uint8_t LOOPS_FOR_SIMULATION_ORPHAN = 50; const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5; @@ -111,6 +123,10 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION; _body->setActivationState(WANTS_DEACTIVATION); _outgoingPriority = 0; + bool verbose = _entity->getName() == "fubar"; // adebug + if (verbose) { + std::cout << (void*)(this) << " adebug flag for deactivation" << std::endl; // adebug + } } else { // disowned object is still moving --> start timer for ownership bid // TODO? put a delay in here proportional to distance from object? @@ -118,6 +134,13 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; } _loopsWithoutOwner = 0; + + // adebug BOOKMARK: the problem is that userB may deactivate and disown the Object + // but it may still be active for userA... who will store slightly non-zero velocities into EntityItem in the meantime + // + // It would be nice if we could ignore slight outgoing changes for unowned objects that WANT_DEACTIVATION until... + // (a) the changes exceed some threshold (--> bid for ownership) or... + // (b) they actually get deactivated (--> slam RigidBody positions to agree with EntityItem) } else if (_entity->getSimulatorID() == Physics::getSessionUUID()) { // we just inherited ownership, make sure our desired priority matches what we have upgradeOutgoingPriority(_entity->getSimulationPriority()); @@ -223,11 +246,19 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { // This callback is invoked by the physics simulation at the end of each simulation step... // iff the corresponding RigidBody is DYNAMIC and has moved. void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { - if (!_entity) { - return; - } - + assert(_entity); assert(entityTreeIsLocked()); + bool verbose = _entity->getName() == "fubar"; // adebug + // adebug BOOKMARK: the problem is that userB may deactivate and disown the Object + // but it may still be active for userA... who will store slightly non-zero velocities into EntityItem in the meantime + // so what we need to do is ignore setWorldTransform() events for unowned objects when the bullet data is not helpful + // until either the data passes some threshold (bid for it) or + // it goes inactive (at which point we should slam bullet to agree with entity) + if (_body->getActivationState() == WANTS_DEACTIVATION && !_entity->isMoving()) { + if (verbose) { + std::cout << (void*)(this) << " adebug v = " << _body->getLinearVelocity().length() << " w = " << _body->getAngularVelocity().length() << std::endl; // adebug + } + } measureBodyAcceleration(); bool positionSuccess; _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false); @@ -245,6 +276,12 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { "setOrientation failed.*"); qCDebug(physics) << "EntityMotionState::setWorldTransform setOrientation failed" << _entity->getID(); } + if (verbose + && (glm::length(getBodyLinearVelocity()) > 0.0f || glm::length(getBodyAngularVelocity()) > 0.0f) + && _entity->getSimulationOwner().getID().isNull()) { + std::cout << (void*)(this) << " adebug set non-zero v on unowned object AS = " << _body->getActivationState() << std::endl; // adebug + + } _entity->setVelocity(getBodyLinearVelocity()); _entity->setAngularVelocity(getBodyAngularVelocity()); _entity->setLastSimulated(usecTimestampNow()); @@ -293,6 +330,18 @@ bool EntityMotionState::isCandidateForOwnership() const { assert(_body); assert(_entity); assert(entityTreeIsLocked()); + + /* adebug + bool verbose = _entity->getName() == "fubar"; // adebug + if (verbose) { + bool isCandidate = _outgoingPriority != 0 + || Physics::getSessionUUID() == _entity->getSimulatorID() + || _entity->actionDataNeedsTransmit(); + if (!isCandidate) { + std::cout << (void*)(this) << " adebug not candidate --> erase" << std::endl; // adebug + } + } + */ return _outgoingPriority != 0 || Physics::getSessionUUID() == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit(); @@ -491,6 +540,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) { assert(_entity); assert(entityTreeIsLocked()); + bool verbose = _entity->getName() == "fubar"; // adebug if (!_body->isActive()) { // make sure all derivatives are zero @@ -576,6 +626,9 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.clearSimulationOwner(); _outgoingPriority = 0; _entity->setPendingOwnershipPriority(_outgoingPriority, now); + if (verbose) { + std::cout << (void*)(this) << " adebug sendUpdate() clearOwnership numInactiveUpdates = " << (int)_numInactiveUpdates << std::endl; // adebug + } } else if (Physics::getSessionUUID() != _entity->getSimulatorID()) { // we don't own the simulation for this entity yet, but we're sending a bid for it quint8 bidPriority = glm::max(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY); @@ -586,6 +639,9 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ // don't forget to remember that we have made a bid _entity->rememberHasSimulationOwnershipBid(); // ...then reset _outgoingPriority in preparation for the next frame + if (verbose) { + std::cout << (void*)(this) << " adebug sendUpdate() bidOwnership at " << (int)_outgoingPriority << std::endl; // adebug + } _outgoingPriority = 0; } else if (_outgoingPriority != _entity->getSimulationPriority()) { // we own the simulation but our desired priority has changed @@ -597,6 +653,9 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.setSimulationOwner(Physics::getSessionUUID(), _outgoingPriority); } _entity->setPendingOwnershipPriority(_outgoingPriority, now); + if (verbose) { + std::cout << (void*)(this) << " adebug sendUpdate() changePriority to " << (int)_outgoingPriority << std::endl; // adebug + } } EntityItemID id(_entity->getID()); diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 903b160a5e..0b0bbcb6fc 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -259,13 +259,14 @@ void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result) _pendingChanges.clear(); } -void PhysicalEntitySimulation::handleOutgoingChanges(const VectorOfMotionStates& motionStates) { +void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionStates& motionStates) { QMutexLocker lock(&_mutex); // walk the motionStates looking for those that correspond to entities for (auto stateItr : motionStates) { ObjectMotionState* state = &(*stateItr); - if (state && state->getType() == MOTIONSTATE_TYPE_ENTITY) { + assert(state); + if (state->getType() == MOTIONSTATE_TYPE_ENTITY) { EntityMotionState* entityState = static_cast(state); EntityItemPointer entity = entityState->getEntity(); assert(entity.get()); diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index af5def9775..9035308741 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -56,7 +56,7 @@ public: void setObjectsToChange(const VectorOfMotionStates& objectsToChange); void getObjectsToChange(VectorOfMotionStates& result); - void handleOutgoingChanges(const VectorOfMotionStates& motionStates); + void handleChangedMotionStates(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); EntityEditPacketSender* getPacketSender() { return _entityPacketSender; } @@ -67,7 +67,7 @@ private: SetOfEntities _entitiesToAddToPhysics; SetOfEntityMotionStates _pendingChanges; // EntityMotionStates already in PhysicsEngine that need their physics changed - SetOfEntityMotionStates _outgoingChanges; // EntityMotionStates for which we need to send updates to entity-server + SetOfEntityMotionStates _outgoingChanges; // EntityMotionStates for which we may need to send updates to entity-server SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index 5fe99f137c..c9cbc6a2be 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -120,6 +120,9 @@ void ThreadSafeDynamicsWorld::synchronizeMotionState(btRigidBody* body) { void ThreadSafeDynamicsWorld::synchronizeMotionStates() { BT_PROFILE("synchronizeMotionStates"); _changedMotionStates.clear(); + + // NOTE: m_synchronizeAllMotionStates is 'false' by default for optimization. + // See PhysicsEngine::init() where we call _dynamicsWorld->setForceUpdateAllAabbs(false) if (m_synchronizeAllMotionStates) { //iterate over all collision objects for (int i=0;i Date: Thu, 9 Mar 2017 17:25:22 -0800 Subject: [PATCH 2/6] restore transform of deactivated entities --- interface/src/Application.cpp | 5 ++- libraries/entities/src/EntityItem.cpp | 2 +- libraries/physics/src/EntityMotionState.cpp | 39 +++++++++++++++---- libraries/physics/src/EntityMotionState.h | 1 + .../physics/src/PhysicalEntitySimulation.cpp | 13 +++++++ .../physics/src/PhysicalEntitySimulation.h | 1 + libraries/physics/src/PhysicsEngine.cpp | 2 +- libraries/physics/src/PhysicsEngine.h | 3 +- .../physics/src/ThreadSafeDynamicsWorld.cpp | 27 +++++++++---- .../physics/src/ThreadSafeDynamicsWorld.h | 4 ++ 10 files changed, 77 insertions(+), 20 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4894ae55ec..368541490a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4439,7 +4439,10 @@ void Application::update(float deltaTime) { getEntities()->getTree()->withWriteLock([&] { PerformanceTimer perfTimer("handleOutgoingChanges"); - const VectorOfMotionStates& outgoingChanges = _physicsEngine->getOutgoingChanges(); + const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates(); + _entitySimulation->handleDeactivatedMotionStates(deactivations); + + const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates(); _entitySimulation->handleChangedMotionStates(outgoingChanges); avatarManager->handleChangedMotionStates(outgoingChanges); }); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index db253b639f..888de44505 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -676,7 +676,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef filterRejection = newSimOwner.getID().isNull(); bool verbose = getName() == "fubar"; // adebug if (verbose && _simulationOwner != newSimOwner) { - std::cout << (void*)(this) << " adebug ownership changed " + std::cout << (void*)(this) << " " << secTimestampNow() << " adebug ownership changed " << _simulationOwner.getID().toString().toStdString() << "." << (int)_simulationOwner.getPriority() << "-->" << newSimOwner.getID().toString().toStdString() << "." << (int)newSimOwner.getPriority() << std::endl; // adebug diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 77c5a4f697..5da9d52169 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -10,6 +10,7 @@ // #include +#include // adebug #include #include @@ -28,7 +29,8 @@ #endif // adebug TODO BOOKMARK: -// The problem is that userB may deactivate and disown and object before userA deactivates +// Consider an object near deactivation owned by userB and in view of userA... +// The problem is that userB may deactivate and disown the object before userA deactivates // userA will sometimes insert non-zero velocities (and position errors) into the Entity before it is deactivated locally // // It would be nice to prevent data export from Bullet to Entity for unowned objects except in cases where it is really needed (?) @@ -109,6 +111,24 @@ void EntityMotionState::updateServerPhysicsVariables() { _serverActionData = _entity->getActionData(); } +void EntityMotionState::handleDeactivation() { + // adebug + glm::vec3 pos = _entity->getPosition(); + float dx = glm::distance(pos, _serverPosition); + glm::vec3 v = _entity->getVelocity(); + float dv = glm::distance(v, _serverVelocity); + std::cout << "adebug deactivate '" << _entity->getName().toStdString() + << "' dx = " << dx << " dv = " << dv << std::endl; // adebug + // adebug + + // copy _server data to entity + bool success; + _entity->setPosition(_serverPosition, success, false); + _entity->setOrientation(_serverRotation, success, false); + _entity->setVelocity(ENTITY_ITEM_ZERO_VEC3); + _entity->setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); +} + // virtual void EntityMotionState::handleEasyChanges(uint32_t& flags) { assert(entityTreeIsLocked()); @@ -125,7 +145,7 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { _outgoingPriority = 0; bool verbose = _entity->getName() == "fubar"; // adebug if (verbose) { - std::cout << (void*)(this) << " adebug flag for deactivation" << std::endl; // adebug + std::cout << (void*)(this) << " " << secTimestampNow() << " adebug flag for deactivation" << std::endl; // adebug } } else { // disowned object is still moving --> start timer for ownership bid @@ -244,7 +264,7 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { } // This callback is invoked by the physics simulation at the end of each simulation step... -// iff the corresponding RigidBody is DYNAMIC and has moved. +// iff the corresponding RigidBody is DYNAMIC and ACTIVE. void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { assert(_entity); assert(entityTreeIsLocked()); @@ -256,7 +276,10 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { // it goes inactive (at which point we should slam bullet to agree with entity) if (_body->getActivationState() == WANTS_DEACTIVATION && !_entity->isMoving()) { if (verbose) { - std::cout << (void*)(this) << " adebug v = " << _body->getLinearVelocity().length() << " w = " << _body->getAngularVelocity().length() << std::endl; // adebug + std::cout << (void*)(this) << " " << secTimestampNow() << " adebug entity at rest but physics is not?" + << " v = " << _body->getLinearVelocity().length() + << " w = " << _body->getAngularVelocity().length() + << std::endl; // adebug } } measureBodyAcceleration(); @@ -279,7 +302,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { if (verbose && (glm::length(getBodyLinearVelocity()) > 0.0f || glm::length(getBodyAngularVelocity()) > 0.0f) && _entity->getSimulationOwner().getID().isNull()) { - std::cout << (void*)(this) << " adebug set non-zero v on unowned object AS = " << _body->getActivationState() << std::endl; // adebug + std::cout << (void*)(this) << " " << secTimestampNow() << " adebug set non-zero v on unowned object AS = " << _body->getActivationState() << std::endl; // adebug } _entity->setVelocity(getBodyLinearVelocity()); @@ -627,7 +650,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ _outgoingPriority = 0; _entity->setPendingOwnershipPriority(_outgoingPriority, now); if (verbose) { - std::cout << (void*)(this) << " adebug sendUpdate() clearOwnership numInactiveUpdates = " << (int)_numInactiveUpdates << std::endl; // adebug + std::cout << (void*)(this) << " " << secTimestampNow() << " adebug sendUpdate() clearOwnership numInactiveUpdates = " << (int)_numInactiveUpdates << std::endl; // adebug } } else if (Physics::getSessionUUID() != _entity->getSimulatorID()) { // we don't own the simulation for this entity yet, but we're sending a bid for it @@ -640,7 +663,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ _entity->rememberHasSimulationOwnershipBid(); // ...then reset _outgoingPriority in preparation for the next frame if (verbose) { - std::cout << (void*)(this) << " adebug sendUpdate() bidOwnership at " << (int)_outgoingPriority << std::endl; // adebug + std::cout << (void*)(this) << " " << secTimestampNow() << " adebug sendUpdate() bidOwnership at " << (int)_outgoingPriority << std::endl; // adebug } _outgoingPriority = 0; } else if (_outgoingPriority != _entity->getSimulationPriority()) { @@ -654,7 +677,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ } _entity->setPendingOwnershipPriority(_outgoingPriority, now); if (verbose) { - std::cout << (void*)(this) << " adebug sendUpdate() changePriority to " << (int)_outgoingPriority << std::endl; // adebug + std::cout << (void*)(this) << " " << secTimestampNow() << " adebug sendUpdate() changePriority to " << (int)_outgoingPriority << std::endl; // adebug } } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index feac47d8ec..380edf3927 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -29,6 +29,7 @@ public: virtual ~EntityMotionState(); void updateServerPhysicsVariables(); + void handleDeactivation(); virtual void handleEasyChanges(uint32_t& flags) override; virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 0b0bbcb6fc..bd76b2d70f 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -259,6 +259,19 @@ void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result) _pendingChanges.clear(); } +void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates) { + for (auto stateItr : motionStates) { + ObjectMotionState* state = &(*stateItr); + assert(state); + if (state->getType() == MOTIONSTATE_TYPE_ENTITY) { + EntityMotionState* entityState = static_cast(state); + entityState->handleDeactivation(); + EntityItemPointer entity = entityState->getEntity(); + _entitiesToSort.insert(entity); + } + } +} + void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionStates& motionStates) { QMutexLocker lock(&_mutex); diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 9035308741..5f6185add3 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -56,6 +56,7 @@ public: void setObjectsToChange(const VectorOfMotionStates& objectsToChange); void getObjectsToChange(VectorOfMotionStates& result); + void handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates); void handleChangedMotionStates(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index f57be4eab3..7d97d25135 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -449,7 +449,7 @@ const CollisionEvents& PhysicsEngine::getCollisionEvents() { return _collisionEvents; } -const VectorOfMotionStates& PhysicsEngine::getOutgoingChanges() { +const VectorOfMotionStates& PhysicsEngine::getChangedMotionStates() { BT_PROFILE("copyOutgoingChanges"); // Bullet will not deactivate static objects (it doesn't expect them to be active) // so we must deactivate them ourselves diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index bbafbb06b6..b2ebe58f08 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -65,7 +65,8 @@ public: bool hasOutgoingChanges() const { return _hasOutgoingChanges; } /// \return reference to list of changed MotionStates. The list is only valid until beginning of next simulation loop. - const VectorOfMotionStates& getOutgoingChanges(); + const VectorOfMotionStates& getChangedMotionStates(); + const VectorOfMotionStates& getDeactivatedMotionStates() const { return _dynamicsWorld->getDeactivatedMotionStates(); } /// \return reference to list of Collision events. The list is only valid until beginning of next simulation loop. const CollisionEvents& getCollisionEvents(); diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index c9cbc6a2be..3c05257fc5 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -128,25 +128,36 @@ void ThreadSafeDynamicsWorld::synchronizeMotionStates() { for (int i=0;igetMotionState()) { - synchronizeMotionState(body); - _changedMotionStates.push_back(static_cast(body->getMotionState())); - } + if (body && body->getMotionState()) { + synchronizeMotionState(body); + _changedMotionStates.push_back(static_cast(body->getMotionState())); } } } else { //iterate over all active rigid bodies + // TODO? if this becomes a performance bottleneck we could derive our own SimulationIslandManager + // that remembers a list of objects it recently deactivated + _activeStates.clear(); + _deactivatedStates.clear(); for (int i=0;iisActive()) { - if (body->getMotionState()) { + ObjectMotionState* motionState = static_cast(body->getMotionState()); + if (motionState) { + if (body->isActive()) { synchronizeMotionState(body); - _changedMotionStates.push_back(static_cast(body->getMotionState())); + _changedMotionStates.push_back(motionState); + _activeStates.insert(motionState); + } else if (_lastActiveStates.find(motionState) != _lastActiveStates.end()) { + // this object was active last frame but is no longer + _deactivatedStates.push_back(motionState); } } } } + if (_deactivatedStates.size() > 0) { + std::cout << secTimestampNow() << " adebug num deactivated = " << _deactivatedStates.size() << std::endl; // adebug + } + _activeStates.swap(_lastActiveStates); } void ThreadSafeDynamicsWorld::saveKinematicState(btScalar timeStep) { diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.h b/libraries/physics/src/ThreadSafeDynamicsWorld.h index 68062d8d29..b4fcca8cdb 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.h +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.h @@ -49,12 +49,16 @@ public: float getLocalTimeAccumulation() const { return m_localTime; } const VectorOfMotionStates& getChangedMotionStates() const { return _changedMotionStates; } + const VectorOfMotionStates& getDeactivatedMotionStates() const { return _deactivatedStates; } private: // call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState() void synchronizeMotionState(btRigidBody* body); VectorOfMotionStates _changedMotionStates; + VectorOfMotionStates _deactivatedStates; + SetOfMotionStates _activeStates; + SetOfMotionStates _lastActiveStates; }; #endif // hifi_ThreadSafeDynamicsWorld_h From be3012181fee72dcdefaa39c189f5447b82b3058 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 9 Mar 2017 17:53:17 -0800 Subject: [PATCH 3/6] force local deactivation on remote deactivation --- libraries/physics/src/EntityMotionState.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 5da9d52169..03bcc43362 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -143,6 +143,8 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION; _body->setActivationState(WANTS_DEACTIVATION); _outgoingPriority = 0; + const float ACTIVATION_EXPIRY = 3.0f; // something larger than the 2.0 hard coded in Bullet + _body->setDeactivationTime(ACTIVATION_EXPIRY); bool verbose = _entity->getName() == "fubar"; // adebug if (verbose) { std::cout << (void*)(this) << " " << secTimestampNow() << " adebug flag for deactivation" << std::endl; // adebug From a16760278e5f84844604a8eda1e76dc379b19cfc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 9 Mar 2017 17:58:53 -0800 Subject: [PATCH 4/6] remove debug code --- libraries/entities/src/EntityItem.cpp | 13 ---- libraries/physics/src/EntityMotionState.cpp | 76 ------------------- .../physics/src/ThreadSafeDynamicsWorld.cpp | 5 +- 3 files changed, 1 insertion(+), 93 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 888de44505..0bb085459e 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -674,13 +674,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // or rejects a set of properties, it clears this. In such cases, we don't want those custom // setters to ignore what the server says. filterRejection = newSimOwner.getID().isNull(); - bool verbose = getName() == "fubar"; // adebug - if (verbose && _simulationOwner != newSimOwner) { - std::cout << (void*)(this) << " " << secTimestampNow() << " adebug ownership changed " - << _simulationOwner.getID().toString().toStdString() << "." << (int)_simulationOwner.getPriority() << "-->" - << newSimOwner.getID().toString().toStdString() << "." << (int)newSimOwner.getPriority() - << std::endl; // adebug - } if (weOwnSimulation) { if (newSimOwner.getID().isNull() && !_simulationOwner.pendingRelease(lastEditedFromBufferAdjusted)) { // entity-server is trying to clear our ownership (probably at our own request) @@ -1891,9 +1884,6 @@ void EntityItem::updateSimulationOwner(const SimulationOwner& owner) { if (_simulationOwner.set(owner)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; - if (getName() == "fubar") { - std::cout << "debug updateSimulationOwner() " << _simulationOwner.getID().toString().toStdString() << "." << (int)(_simulationOwner.getPriority()) << std::endl; // adebug - } } } @@ -1901,9 +1891,6 @@ void EntityItem::clearSimulationOwnership() { if (wantTerseEditLogging() && !_simulationOwner.isNull()) { qCDebug(entities) << "sim ownership for" << getDebugName() << "is now null"; } - if (getName() == "fubar") { - std::cout << "debug clearSimulationOwnership()" << std::endl; // adebug - } _simulationOwner.clear(); // don't bother setting the DIRTY_SIMULATOR_ID flag because: diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 03bcc43362..d80615e2c6 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -10,7 +10,6 @@ // #include -#include // adebug #include #include @@ -28,19 +27,6 @@ #include "EntityTree.h" #endif -// adebug TODO BOOKMARK: -// Consider an object near deactivation owned by userB and in view of userA... -// The problem is that userB may deactivate and disown the object before userA deactivates -// userA will sometimes insert non-zero velocities (and position errors) into the Entity before it is deactivated locally -// -// It would be nice to prevent data export from Bullet to Entity for unowned objects except in cases where it is really needed (?) -// Maybe can recycle _serverPosition and friends to store the "before simulationStep" data to more efficiently figure out -// if data should be used for non-owned objects. -// -// If we do that, we should convert _serverPosition and friends to use Bullet data types for efficiency. -// -// adebug - const uint8_t LOOPS_FOR_SIMULATION_ORPHAN = 50; const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5; @@ -112,15 +98,6 @@ void EntityMotionState::updateServerPhysicsVariables() { } void EntityMotionState::handleDeactivation() { - // adebug - glm::vec3 pos = _entity->getPosition(); - float dx = glm::distance(pos, _serverPosition); - glm::vec3 v = _entity->getVelocity(); - float dv = glm::distance(v, _serverVelocity); - std::cout << "adebug deactivate '" << _entity->getName().toStdString() - << "' dx = " << dx << " dv = " << dv << std::endl; // adebug - // adebug - // copy _server data to entity bool success; _entity->setPosition(_serverPosition, success, false); @@ -145,10 +122,6 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { _outgoingPriority = 0; const float ACTIVATION_EXPIRY = 3.0f; // something larger than the 2.0 hard coded in Bullet _body->setDeactivationTime(ACTIVATION_EXPIRY); - bool verbose = _entity->getName() == "fubar"; // adebug - if (verbose) { - std::cout << (void*)(this) << " " << secTimestampNow() << " adebug flag for deactivation" << std::endl; // adebug - } } else { // disowned object is still moving --> start timer for ownership bid // TODO? put a delay in here proportional to distance from object? @@ -156,13 +129,6 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; } _loopsWithoutOwner = 0; - - // adebug BOOKMARK: the problem is that userB may deactivate and disown the Object - // but it may still be active for userA... who will store slightly non-zero velocities into EntityItem in the meantime - // - // It would be nice if we could ignore slight outgoing changes for unowned objects that WANT_DEACTIVATION until... - // (a) the changes exceed some threshold (--> bid for ownership) or... - // (b) they actually get deactivated (--> slam RigidBody positions to agree with EntityItem) } else if (_entity->getSimulatorID() == Physics::getSessionUUID()) { // we just inherited ownership, make sure our desired priority matches what we have upgradeOutgoingPriority(_entity->getSimulationPriority()); @@ -270,20 +236,6 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { assert(_entity); assert(entityTreeIsLocked()); - bool verbose = _entity->getName() == "fubar"; // adebug - // adebug BOOKMARK: the problem is that userB may deactivate and disown the Object - // but it may still be active for userA... who will store slightly non-zero velocities into EntityItem in the meantime - // so what we need to do is ignore setWorldTransform() events for unowned objects when the bullet data is not helpful - // until either the data passes some threshold (bid for it) or - // it goes inactive (at which point we should slam bullet to agree with entity) - if (_body->getActivationState() == WANTS_DEACTIVATION && !_entity->isMoving()) { - if (verbose) { - std::cout << (void*)(this) << " " << secTimestampNow() << " adebug entity at rest but physics is not?" - << " v = " << _body->getLinearVelocity().length() - << " w = " << _body->getAngularVelocity().length() - << std::endl; // adebug - } - } measureBodyAcceleration(); bool positionSuccess; _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false); @@ -301,12 +253,6 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { "setOrientation failed.*"); qCDebug(physics) << "EntityMotionState::setWorldTransform setOrientation failed" << _entity->getID(); } - if (verbose - && (glm::length(getBodyLinearVelocity()) > 0.0f || glm::length(getBodyAngularVelocity()) > 0.0f) - && _entity->getSimulationOwner().getID().isNull()) { - std::cout << (void*)(this) << " " << secTimestampNow() << " adebug set non-zero v on unowned object AS = " << _body->getActivationState() << std::endl; // adebug - - } _entity->setVelocity(getBodyLinearVelocity()); _entity->setAngularVelocity(getBodyAngularVelocity()); _entity->setLastSimulated(usecTimestampNow()); @@ -355,18 +301,6 @@ bool EntityMotionState::isCandidateForOwnership() const { assert(_body); assert(_entity); assert(entityTreeIsLocked()); - - /* adebug - bool verbose = _entity->getName() == "fubar"; // adebug - if (verbose) { - bool isCandidate = _outgoingPriority != 0 - || Physics::getSessionUUID() == _entity->getSimulatorID() - || _entity->actionDataNeedsTransmit(); - if (!isCandidate) { - std::cout << (void*)(this) << " adebug not candidate --> erase" << std::endl; // adebug - } - } - */ return _outgoingPriority != 0 || Physics::getSessionUUID() == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit(); @@ -565,7 +499,6 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) { assert(_entity); assert(entityTreeIsLocked()); - bool verbose = _entity->getName() == "fubar"; // adebug if (!_body->isActive()) { // make sure all derivatives are zero @@ -651,9 +584,6 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.clearSimulationOwner(); _outgoingPriority = 0; _entity->setPendingOwnershipPriority(_outgoingPriority, now); - if (verbose) { - std::cout << (void*)(this) << " " << secTimestampNow() << " adebug sendUpdate() clearOwnership numInactiveUpdates = " << (int)_numInactiveUpdates << std::endl; // adebug - } } else if (Physics::getSessionUUID() != _entity->getSimulatorID()) { // we don't own the simulation for this entity yet, but we're sending a bid for it quint8 bidPriority = glm::max(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY); @@ -664,9 +594,6 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ // don't forget to remember that we have made a bid _entity->rememberHasSimulationOwnershipBid(); // ...then reset _outgoingPriority in preparation for the next frame - if (verbose) { - std::cout << (void*)(this) << " " << secTimestampNow() << " adebug sendUpdate() bidOwnership at " << (int)_outgoingPriority << std::endl; // adebug - } _outgoingPriority = 0; } else if (_outgoingPriority != _entity->getSimulationPriority()) { // we own the simulation but our desired priority has changed @@ -678,9 +605,6 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.setSimulationOwner(Physics::getSessionUUID(), _outgoingPriority); } _entity->setPendingOwnershipPriority(_outgoingPriority, now); - if (verbose) { - std::cout << (void*)(this) << " " << secTimestampNow() << " adebug sendUpdate() changePriority to " << (int)_outgoingPriority << std::endl; // adebug - } } EntityItemID id(_entity->getID()); diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index 3c05257fc5..24cfbc2609 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -136,7 +136,7 @@ void ThreadSafeDynamicsWorld::synchronizeMotionStates() { } else { //iterate over all active rigid bodies // TODO? if this becomes a performance bottleneck we could derive our own SimulationIslandManager - // that remembers a list of objects it recently deactivated + // that remembers a list of objects deactivated last step _activeStates.clear(); _deactivatedStates.clear(); for (int i=0;i 0) { - std::cout << secTimestampNow() << " adebug num deactivated = " << _deactivatedStates.size() << std::endl; // adebug - } _activeStates.swap(_lastActiveStates); } From 9c9eb879255d3136b321622d0b221196a27e96d9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 10 Mar 2017 12:55:16 -0800 Subject: [PATCH 5/6] sync RigidBody on deactivation --- libraries/physics/src/EntityMotionState.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index d80615e2c6..bfa15537fc 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -104,6 +104,11 @@ void EntityMotionState::handleDeactivation() { _entity->setOrientation(_serverRotation, success, false); _entity->setVelocity(ENTITY_ITEM_ZERO_VEC3); _entity->setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); + // and also to RigidBody + btTransform worldTrans; + worldTrans.setOrigin(glmToBullet(_serverPosition)); + worldTrans.setRotation(glmToBullet(_serverRotation)); + // no need to update velocities... should already be zero } // virtual From bde2222dfd758292f8ac0ca6c3e26ec3dd6c492b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 10 Mar 2017 18:30:56 -0800 Subject: [PATCH 6/6] actually use worldTrans --- libraries/physics/src/EntityMotionState.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index bfa15537fc..d383f4c199 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -108,6 +108,7 @@ void EntityMotionState::handleDeactivation() { btTransform worldTrans; worldTrans.setOrigin(glmToBullet(_serverPosition)); worldTrans.setRotation(glmToBullet(_serverRotation)); + _body->setWorldTransform(worldTrans); // no need to update velocities... should already be zero }