diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c0e024ec1e..bfc781fa2a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2540,11 +2540,15 @@ void Application::cleanupBeforeQuit() { Application::~Application() { // remove avatars from physics engine - DependencyManager::get()->clearOtherAvatars(); - VectorOfMotionStates motionStates; - DependencyManager::get()->getObjectsToRemoveFromPhysics(motionStates); - _physicsEngine->removeObjects(motionStates); - DependencyManager::get()->deleteAllAvatars(); + auto avatarManager = DependencyManager::get(); + avatarManager->clearOtherAvatars(); + + PhysicsEngine::Transaction transaction; + avatarManager->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + avatarManager->handleProcessedPhysicsTransaction(transaction); + + avatarManager->deleteAllAvatars(); _physicsEngine->setCharacterController(nullptr); @@ -5706,12 +5710,10 @@ void Application::update(float deltaTime) { t1 = std::chrono::high_resolution_clock::now(); - avatarManager->getObjectsToRemoveFromPhysics(motionStates); - _physicsEngine->removeObjects(motionStates); - avatarManager->getObjectsToAddToPhysics(motionStates); - _physicsEngine->addObjects(motionStates); - avatarManager->getObjectsToChange(motionStates); - _physicsEngine->changeObjects(motionStates); + PhysicsEngine::Transaction transaction; + avatarManager->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + avatarManager->handleProcessedPhysicsTransaction(transaction); myAvatar->prepareForPhysicsSimulation(); _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 11257e0808..62eadeddef 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -98,7 +98,7 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe } AvatarManager::~AvatarManager() { - assert(_motionStates.empty()); + assert(_avatarsToChangeInPhysics.empty()); } void AvatarManager::init() { @@ -213,7 +213,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { uint64_t updateExpiry = startTime + UPDATE_BUDGET; int numAvatarsUpdated = 0; int numAVatarsNotUpdated = 0; - bool physicsEnabled = qApp->isPhysicsEnabled(); render::Transaction renderTransaction; workload::Transaction workloadTransaction; @@ -240,18 +239,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { if (_shouldRender) { avatar->ensureInScene(avatar, qApp->getMain3DScene()); } - if (physicsEnabled && !avatar->isInPhysicsSimulation()) { - ShapeInfo shapeInfo; - avatar->computeShapeInfo(shapeInfo); - btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); - if (shape) { - AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); - motionState->setMass(avatar->computeMass()); - avatar->setMotionState(motionState); - _motionStates.insert(avatar.get(), motionState); - _motionStatesToAddToPhysics.insert(motionState); - } - } avatar->animateScaleChanges(deltaTime); const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; @@ -393,8 +380,52 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() { return AvatarSharedPointer(new OtherAvatar(qApp->thread()), [](OtherAvatar* ptr) { ptr->deleteLater(); }); } -void AvatarManager::handleSpaceChange(OtherAvatarPointer avatar) { - // WORKLOAD_AVATARS_BOOKMARK: implement this +void AvatarManager::queuePhysicsChange(const OtherAvatarPointer& avatar) { + _avatarsToChangeInPhysics.insert(avatar); +} + +void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) { + SetOfOtherAvatars failedShapeBuilds; + for (auto avatar : _avatarsToChangeInPhysics) { + bool isInPhysics = avatar->isInPhysicsSimulation(); + if (isInPhysics != avatar->shouldBeInPhysicsSimulation()) { + if (isInPhysics) { + transaction.objectsToRemove.push_back(avatar->_motionState); + avatar->_motionState = nullptr; + } else { + ShapeInfo shapeInfo; + avatar->computeShapeInfo(shapeInfo); + btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); + if (shape) { + AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); + motionState->setMass(avatar->computeMass()); + avatar->_motionState = motionState; + transaction.objectsToAdd.push_back(motionState); + } else { + failedShapeBuilds.insert(avatar); + } + } + } else if (isInPhysics) { + transaction.objectsToChange.push_back(avatar->_motionState); + } + } + _avatarsToChangeInPhysics.swap(failedShapeBuilds); +} + +void AvatarManager::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) { + // things on objectsToChange correspond to failed changes + // so we push them back onto _avatarsToChangeInPhysics + for (auto object : transaction.objectsToChange) { + AvatarMotionState* motionState = static_cast(object); + assert(motionState); + assert(motionState->_avatar); + _avatarsToChangeInPhysics.insert(motionState->_avatar); + } + // things on objectsToRemove are ready for delete + for (auto object : transaction.objectsToRemove) { + delete object; + } + transaction.clear(); } void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { @@ -405,15 +436,8 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar } AvatarHashMap::handleRemovedAvatar(avatar, removalReason); - // remove from physics - avatar->setMotionState(nullptr); - AvatarMotionStateMap::iterator itr = _motionStates.find(avatar.get()); - if (itr != _motionStates.end()) { - AvatarMotionState* motionState = *itr; - _motionStatesToAddToPhysics.remove(motionState); - _motionStatesToRemoveFromPhysics.push_back(motionState); - _motionStates.erase(itr); - } + avatar->die(); + queuePhysicsChange(avatar); if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) { emit DependencyManager::get()->enteredIgnoreRadius(); @@ -449,8 +473,7 @@ void AvatarManager::clearOtherAvatars() { } void AvatarManager::deleteAllAvatars() { - assert(_motionStates.empty()); // should have called clearOtherAvatars() before getting here - deleteMotionStates(); + assert(_avatarsToChangeInPhysics.empty()); QReadLocker locker(&_hashLock); AvatarHash::iterator avatarIterator = _avatarHash.begin(); @@ -458,39 +481,7 @@ void AvatarManager::deleteAllAvatars() { auto avatar = std::static_pointer_cast(avatarIterator.value()); avatarIterator = _avatarHash.erase(avatarIterator); avatar->die(); - } -} - -void AvatarManager::deleteMotionStates() { - // delete motionstates that were removed from physics last frame - for (auto state : _motionStatesToDelete) { - delete state; - } - _motionStatesToDelete.clear(); -} - -void AvatarManager::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) { - deleteMotionStates(); - result = _motionStatesToRemoveFromPhysics; - _motionStatesToDelete.swap(_motionStatesToRemoveFromPhysics); -} - -void AvatarManager::getObjectsToAddToPhysics(VectorOfMotionStates& result) { - result.clear(); - for (auto motionState : _motionStatesToAddToPhysics) { - result.push_back(motionState); - } - _motionStatesToAddToPhysics.clear(); -} - -void AvatarManager::getObjectsToChange(VectorOfMotionStates& result) { - result.clear(); - AvatarMotionStateMap::iterator motionStateItr = _motionStates.begin(); - while (motionStateItr != _motionStates.end()) { - if ((*motionStateItr)->getIncomingDirtyFlags() != 0) { - result.push_back(*motionStateItr); - } - ++motionStateItr; + assert(!avatar->_motionState); } } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index bbf13d25c4..36df2f0aaf 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -12,6 +12,8 @@ #ifndef hifi_AvatarManager_h #define hifi_AvatarManager_h +#include + #include #include #include @@ -177,16 +179,17 @@ public: float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); } int getIdentityRequestsSent() const { return _identityRequestsSent; } -public slots: + void queuePhysicsChange(const OtherAvatarPointer& avatar); + void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction); + void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction); +public slots: /**jsdoc * @function AvatarManager.updateAvatarRenderStatus * @param {boolean} shouldRenderAvatars */ void updateAvatarRenderStatus(bool shouldRenderAvatars); - void handleSpaceChange(OtherAvatarPointer avatar); - protected: AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) override; @@ -197,16 +200,12 @@ private: void simulateAvatarFades(float deltaTime); AvatarSharedPointer newSharedAvatar() override; - void deleteMotionStates(); void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; QVector _avatarsToFade; - using AvatarMotionStateMap = QMap; - AvatarMotionStateMap _motionStates; - VectorOfMotionStates _motionStatesToRemoveFromPhysics; - VectorOfMotionStates _motionStatesToDelete; - SetOfMotionStates _motionStatesToAddToPhysics; + using SetOfOtherAvatars = std::set; + SetOfOtherAvatars _avatarsToChangeInPhysics; std::shared_ptr _myAvatar; quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index 653b6c4533..29ad5aed91 100644 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -82,12 +82,16 @@ int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) { return bytesRead; } +void OtherAvatar::setWorkloadRegion(uint8_t region) { + _workloadRegion = region; +} + +bool OtherAvatar::shouldBeInPhysicsSimulation() const { + return (_workloadRegion < workload::Region::R3 && !isDead()); +} + void OtherAvatar::rebuildCollisionShape() { if (_motionState) { _motionState->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); } } - -void OtherAvatar::setMotionState(AvatarMotionState* motionState) { - _motionState = motionState; -} diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index e34d4d767b..ccfe42dbed 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -18,6 +18,7 @@ #include "ui/overlays/Overlays.h" #include "ui/overlays/Sphere3DOverlay.h" +class AvatarManager; class AvatarMotionState; class OtherAvatar : public Avatar { @@ -36,15 +37,20 @@ public: int parseDataFromBuffer(const QByteArray& buffer) override; - void setMotionState(AvatarMotionState* motionState); bool isInPhysicsSimulation() const { return _motionState != nullptr; } void rebuildCollisionShape() override; + void setWorkloadRegion(uint8_t region); + bool shouldBeInPhysicsSimulation() const; + + friend AvatarManager; + protected: std::shared_ptr _otherAvatarOrbMeshPlaceholder { nullptr }; OverlayID _otherAvatarOrbMeshPlaceholderID { UNKNOWN_OVERLAY_ID }; AvatarMotionState* _motionState { nullptr }; int32_t _spaceIndex { -1 }; + uint8_t _workloadRegion { workload::Region::INVALID }; }; using OtherAvatarPointer = std::shared_ptr; diff --git a/interface/src/workload/PhysicsBoundary.cpp b/interface/src/workload/PhysicsBoundary.cpp index d1de9ad6b2..cc78789145 100644 --- a/interface/src/workload/PhysicsBoundary.cpp +++ b/interface/src/workload/PhysicsBoundary.cpp @@ -25,8 +25,6 @@ void PhysicsBoundary::run(const workload::WorkloadContextPointer& context, const return; } GameWorkloadContext* gameContext = static_cast(context.get()); - PhysicalEntitySimulationPointer simulation = gameContext->_simulation; - auto avatarManager = DependencyManager::get(); const auto& regionChanges = inputs.get0(); for (uint32_t i = 0; i < (uint32_t)regionChanges.size(); ++i) { const workload::Space::Change& change = regionChanges[i]; @@ -34,13 +32,15 @@ void PhysicsBoundary::run(const workload::WorkloadContextPointer& context, const if (nestable) { switch (nestable->getNestableType()) { case NestableType::Entity: { - EntityItemPointer entity = std::static_pointer_cast(nestable); - simulation->changeEntity(entity); + gameContext->_simulation->changeEntity(std::static_pointer_cast(nestable)); } break; case NestableType::Avatar: { auto avatar = std::static_pointer_cast(nestable); - avatarManager->handleSpaceChange(avatar); + avatar->setWorkloadRegion(change.region); + if (avatar->isInPhysicsSimulation() != avatar->shouldBeInPhysicsSimulation()) { + DependencyManager::get()->queuePhysicsChange(avatar); + } } break; default: diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 163005bc81..92ae3d546d 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -279,6 +279,57 @@ void PhysicsEngine::reinsertObject(ObjectMotionState* object) { } } +void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) { + // removes + for (auto object : transaction.objectsToRemove) { + btRigidBody* body = object->getRigidBody(); + if (body) { + removeDynamicsForBody(body); + _dynamicsWorld->removeRigidBody(body); + + // NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it. + object->setRigidBody(nullptr); + body->setMotionState(nullptr); + delete body; + } + object->clearIncomingDirtyFlags(); + } + + // adds + for (auto object : transaction.objectsToAdd) { + addObjectToDynamicsWorld(object); + } + + // changes + std::vector failedChanges; + for (auto object : transaction.objectsToChange) { + uint32_t flags = object->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS; + if (flags & HARD_DIRTY_PHYSICS_FLAGS) { + if (object->handleHardAndEasyChanges(flags, this)) { + object->clearIncomingDirtyFlags(); + } else { + failedChanges.push_back(object); + } + } else if (flags & EASY_DIRTY_PHYSICS_FLAGS) { + object->handleEasyChanges(flags); + object->clearIncomingDirtyFlags(); + } + if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) { + _activeStaticBodies.insert(object->getRigidBody()); + } + } + // activeStaticBodies have changed (in an Easy way) and need their Aabbs updated + // but we've configured Bullet to NOT update them automatically (for improved performance) + // so we must do it ourselves + std::set::const_iterator itr = _activeStaticBodies.begin(); + while (itr != _activeStaticBodies.end()) { + _dynamicsWorld->updateSingleAabb(*itr); + ++itr; + } + // we replace objectsToChange with any that failed + transaction.objectsToChange.swap(failedChanges); +} + void PhysicsEngine::removeContacts(ObjectMotionState* motionState) { // trigger events for new/existing/old contacts ContactMap::iterator contactItr = _contactMap.begin(); diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index c6e165632b..0f40a06825 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -70,6 +70,18 @@ using CollisionEvents = std::vector; class PhysicsEngine { public: + class Transaction { + public: + void clear() { + objectsToRemove.clear(); + objectsToAdd.clear(); + objectsToChange.clear(); + } + std::vector objectsToRemove; + std::vector objectsToAdd; + std::vector objectsToChange; + }; + PhysicsEngine(const glm::vec3& offset); ~PhysicsEngine(); void init(); @@ -83,6 +95,8 @@ public: VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects); void reinsertObject(ObjectMotionState* object); + void processTransaction(Transaction& transaction); + void stepSimulation(); void harvestPerformanceStats(); void printPerformanceStatsToFile(const QString& filename);