diff --git a/assignment-client/src/entities/EntityTreeHeadlessViewer.cpp b/assignment-client/src/entities/EntityTreeHeadlessViewer.cpp index 3649cf1129..5f43627763 100644 --- a/assignment-client/src/entities/EntityTreeHeadlessViewer.cpp +++ b/assignment-client/src/entities/EntityTreeHeadlessViewer.cpp @@ -37,6 +37,7 @@ void EntityTreeHeadlessViewer::update() { if (_tree) { EntityTreePointer tree = std::static_pointer_cast(_tree); tree->withTryWriteLock([&] { + tree->preUpdate(); tree->update(); }); } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bf9df95ac0..ef1ab23e1b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2790,21 +2790,15 @@ Application::~Application() { // remove avatars from physics engine auto avatarManager = DependencyManager::get(); avatarManager->clearOtherAvatars(); + auto myCharacterController = getMyAvatar()->getCharacterController(); + myCharacterController->clearDetailedMotionStates(); PhysicsEngine::Transaction transaction; avatarManager->buildPhysicsTransaction(transaction); _physicsEngine->processTransaction(transaction); avatarManager->handleProcessedPhysicsTransaction(transaction); - avatarManager->deleteAllAvatars(); - auto myCharacterController = getMyAvatar()->getCharacterController(); - myCharacterController->clearDetailedMotionStates(); - - myCharacterController->buildPhysicsTransaction(transaction); - _physicsEngine->processTransaction(transaction); - myCharacterController->handleProcessedPhysicsTransaction(transaction); - _physicsEngine->setCharacterController(nullptr); // the _shapeManager should have zero references @@ -6414,64 +6408,42 @@ void Application::update(float deltaTime) { PROFILE_RANGE(simulation_physics, "Simulation"); PerformanceTimer perfTimer("simulation"); - if (_physicsEnabled) { - auto t0 = std::chrono::high_resolution_clock::now(); - auto t1 = t0; + getEntities()->preUpdate(); + + auto t0 = std::chrono::high_resolution_clock::now(); + auto t1 = t0; + { + PROFILE_RANGE(simulation_physics, "PrePhysics"); + PerformanceTimer perfTimer("prePhysics)"); { - PROFILE_RANGE(simulation_physics, "PrePhysics"); - PerformanceTimer perfTimer("prePhysics)"); - { - PROFILE_RANGE(simulation_physics, "RemoveEntities"); - const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics(); - { - PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size()); - _physicsEngine->removeObjects(motionStates); - } - _entitySimulation->deleteObjectsRemovedFromPhysics(); - } + PROFILE_RANGE(simulation_physics, "Entities"); + PhysicsEngine::Transaction transaction; + _entitySimulation->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + _entitySimulation->handleProcessedPhysicsTransaction(transaction); + } - { - PROFILE_RANGE(simulation_physics, "AddEntities"); - VectorOfMotionStates motionStates; - getEntities()->getTree()->withReadLock([&] { - _entitySimulation->getObjectsToAddToPhysics(motionStates); - PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size()); - _physicsEngine->addObjects(motionStates); - }); - } - { - VectorOfMotionStates motionStates; - PROFILE_RANGE(simulation_physics, "ChangeEntities"); - getEntities()->getTree()->withReadLock([&] { - _entitySimulation->getObjectsToChange(motionStates); - VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates); - _entitySimulation->setObjectsToChange(stillNeedChange); - }); - } + t1 = std::chrono::high_resolution_clock::now(); + { + PROFILE_RANGE(simulation_physics, "Avatars"); + PhysicsEngine::Transaction transaction; + avatarManager->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + avatarManager->handleProcessedPhysicsTransaction(transaction); + + myAvatar->prepareForPhysicsSimulation(); + _physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying()); + } + } + + if (_physicsEnabled) { + { + PROFILE_RANGE(simulation_physics, "PrepareActions"); _entitySimulation->applyDynamicChanges(); - - t1 = std::chrono::high_resolution_clock::now(); - - { - PROFILE_RANGE(simulation_physics, "Avatars"); - PhysicsEngine::Transaction transaction; - avatarManager->buildPhysicsTransaction(transaction); - _physicsEngine->processTransaction(transaction); - avatarManager->handleProcessedPhysicsTransaction(transaction); - myAvatar->getCharacterController()->buildPhysicsTransaction(transaction); - _physicsEngine->processTransaction(transaction); - myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction); - myAvatar->prepareForPhysicsSimulation(); - _physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying()); - } - - { - PROFILE_RANGE(simulation_physics, "PrepareActions"); - _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) { - dynamic->prepareForPhysicsSimulation(); - }); - } + _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) { + dynamic->prepareForPhysicsSimulation(); + }); } auto t2 = std::chrono::high_resolution_clock::now(); { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 00e743312f..e01f9339f4 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -101,7 +101,7 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe } AvatarManager::~AvatarManager() { - assert(_avatarsToChangeInPhysics.empty()); + assert(_otherAvatarsToChangeInPhysics.empty()); } void AvatarManager::init() { @@ -295,7 +295,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { render::Transaction renderTransaction; workload::Transaction workloadTransaction; - + for (int p = kHero; p < NumVariants; p++) { auto& priorityQueue = avatarPriorityQueues[p]; // Sorting the current queue HERE as part of the measured timing. @@ -314,7 +314,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // remove the orb if it is there avatar->removeOrb(); if (avatar->needsPhysicsUpdate()) { - _avatarsToChangeInPhysics.insert(avatar); + _otherAvatarsToChangeInPhysics.insert(avatar); } } else { avatar->updateOrbPosition(); @@ -419,69 +419,111 @@ AvatarSharedPointer AvatarManager::newSharedAvatar(const QUuid& sessionUUID) { } void AvatarManager::queuePhysicsChange(const OtherAvatarPointer& avatar) { - _avatarsToChangeInPhysics.insert(avatar); + _otherAvatarsToChangeInPhysics.insert(avatar); +} + +DetailedMotionState* AvatarManager::createDetailedMotionState(OtherAvatarPointer avatar, int32_t jointIndex) { + bool isBound = false; + std::vector boundJoints; + const btCollisionShape* shape = avatar->createCollisionShape(jointIndex, isBound, boundJoints); + if (shape) { + DetailedMotionState* motionState = new DetailedMotionState(avatar, shape, jointIndex); + motionState->setMass(0.0f); // DetailedMotionState has KINEMATIC MotionType, so zero mass is ok + motionState->setIsBound(isBound, boundJoints); + return motionState; + } + return nullptr; +} + +void AvatarManager::rebuildAvatarPhysics(PhysicsEngine::Transaction& transaction, OtherAvatarPointer avatar) { + if (!avatar->_motionState) { + avatar->_motionState = new AvatarMotionState(avatar, nullptr); + } + AvatarMotionState* motionState = avatar->_motionState; + ShapeInfo shapeInfo; + avatar->computeShapeInfo(shapeInfo); + const btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); + assert(shape); + motionState->setShape(shape); + motionState->setMass(avatar->computeMass()); + if (motionState->getRigidBody()) { + transaction.objectsToReinsert.push_back(motionState); + } else { + transaction.objectsToAdd.push_back(motionState); + } + motionState->clearIncomingDirtyFlags(); + + // Rather than reconcile numbers of joints after change to model or LOD + // we blow away old detailedMotionStates and create anew all around. + + // delete old detailedMotionStates + auto& detailedMotionStates = avatar->getDetailedMotionStates(); + if (detailedMotionStates.size() != 0) { + for (auto& detailedMotionState : detailedMotionStates) { + transaction.objectsToRemove.push_back(detailedMotionState); + } + avatar->resetDetailedMotionStates(); + } + + // build new detailedMotionStates + OtherAvatar::BodyLOD lod = avatar->getBodyLOD(); + if (lod == OtherAvatar::BodyLOD::Sphere) { + auto dMotionState = createDetailedMotionState(avatar, -1); + if (dMotionState) { + detailedMotionStates.push_back(dMotionState); + transaction.objectsToAdd.push_back(dMotionState); + } + } else { + int32_t numJoints = avatar->getJointCount(); + for (int32_t i = 0; i < numJoints; i++) { + auto dMotionState = createDetailedMotionState(avatar, i); + if (dMotionState) { + detailedMotionStates.push_back(dMotionState); + transaction.objectsToAdd.push_back(dMotionState); + } + } + } + avatar->_needsReinsertion = false; } void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) { - SetOfOtherAvatars failedShapeBuilds; - for (auto avatar : _avatarsToChangeInPhysics) { + _myAvatar->getCharacterController()->buildPhysicsTransaction(transaction); + for (auto avatar : _otherAvatarsToChangeInPhysics) { bool isInPhysics = avatar->isInPhysicsSimulation(); if (isInPhysics != avatar->shouldBeInPhysicsSimulation()) { if (isInPhysics) { transaction.objectsToRemove.push_back(avatar->_motionState); avatar->_motionState = nullptr; auto& detailedMotionStates = avatar->getDetailedMotionStates(); - for (auto& mState : detailedMotionStates) { - transaction.objectsToRemove.push_back(mState); + for (auto& motionState : detailedMotionStates) { + transaction.objectsToRemove.push_back(motionState); } avatar->resetDetailedMotionStates(); } else { - if (avatar->getDetailedMotionStates().size() == 0) { - avatar->createDetailedMotionStates(avatar); - for (auto dMotionState : avatar->getDetailedMotionStates()) { - transaction.objectsToAdd.push_back(dMotionState); - } - } - if (avatar->getDetailedMotionStates().size() > 0) { - 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 { - failedShapeBuilds.insert(avatar); - } + rebuildAvatarPhysics(transaction, avatar); } } else if (isInPhysics) { - transaction.objectsToChange.push_back(avatar->_motionState); - - auto& detailedMotionStates = avatar->getDetailedMotionStates(); - for (auto& mState : detailedMotionStates) { - if (mState) { - transaction.objectsToChange.push_back(mState); - } + AvatarMotionState* motionState = avatar->_motionState; + uint32_t flags = motionState->getIncomingDirtyFlags(); + if (flags & EASY_DIRTY_PHYSICS_FLAGS) { + motionState->handleEasyChanges(flags); + } + // NOTE: we don't call detailedMotionState->handleEasyChanges() here because they are KINEMATIC + // and Bullet will automagically call DetailedMotionState::getWorldTransform() on all that are active. + + if (motionState->needsNewShape()) { + rebuildAvatarPhysics(transaction, avatar); + } else { + if (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { + transaction.objectsToReinsert.push_back(motionState); + } + motionState->clearIncomingDirtyFlags(); } - } } - _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; @@ -570,7 +612,7 @@ void AvatarManager::clearOtherAvatars() { ++avatarIterator; } } - } + } for (auto& av : removedAvatars) { handleRemovedAvatar(av); @@ -578,7 +620,7 @@ void AvatarManager::clearOtherAvatars() { } void AvatarManager::deleteAllAvatars() { - assert(_avatarsToChangeInPhysics.empty()); + assert(_otherAvatarsToChangeInPhysics.empty()); QReadLocker locker(&_hashLock); AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { @@ -588,7 +630,7 @@ void AvatarManager::deleteAllAvatars() { if (avatar != _myAvatar) { auto otherAvatar = std::static_pointer_cast(avatar); assert(!otherAvatar->_motionState); - assert(otherAvatar->getDetailedMotionStates().size() == 0); + assert(otherAvatar->getDetailedMotionStates().size() == 0); } } } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 98deebe919..db1bc125a4 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -273,6 +273,8 @@ public slots: protected: AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) override; + DetailedMotionState* createDetailedMotionState(OtherAvatarPointer avatar, int32_t jointIndex); + void rebuildAvatarPhysics(PhysicsEngine::Transaction& transaction, OtherAvatarPointer avatar); private: explicit AvatarManager(QObject* parent = 0); @@ -288,7 +290,7 @@ private: void handleTransitAnimations(AvatarTransit::Status status); using SetOfOtherAvatars = std::set; - SetOfOtherAvatars _avatarsToChangeInPhysics; + SetOfOtherAvatars _otherAvatarsToChangeInPhysics; std::shared_ptr _myAvatar; quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index 77fc81fa04..97085c8443 100755 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -29,23 +29,19 @@ void AvatarMotionState::handleEasyChanges(uint32_t& flags) { } } -bool AvatarMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { - return ObjectMotionState::handleHardAndEasyChanges(flags, engine); -} - AvatarMotionState::~AvatarMotionState() { assert(_avatar); _avatar = nullptr; } // virtual -uint32_t AvatarMotionState::getIncomingDirtyFlags() { +uint32_t AvatarMotionState::getIncomingDirtyFlags() const { return _body ? _dirtyFlags : 0; } -void AvatarMotionState::clearIncomingDirtyFlags() { +void AvatarMotionState::clearIncomingDirtyFlags(uint32_t mask) { if (_body) { - _dirtyFlags = 0; + _dirtyFlags &= ~mask; } } @@ -54,13 +50,6 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const { return MOTION_TYPE_DYNAMIC; } -// virtual and protected -const btCollisionShape* AvatarMotionState::computeNewShape() { - ShapeInfo shapeInfo; - _avatar->computeShapeInfo(shapeInfo); - return getShapeManager()->getShape(shapeInfo); -} - // virtual bool AvatarMotionState::isMoving() const { return false; diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 3103341622..a752d2c8fb 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -23,44 +23,44 @@ class AvatarMotionState : public ObjectMotionState { public: AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape); - virtual void handleEasyChanges(uint32_t& flags) override; - virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; + void handleEasyChanges(uint32_t& flags) override; - virtual PhysicsMotionType getMotionType() const override { return _motionType; } + PhysicsMotionType getMotionType() const override { return _motionType; } - virtual uint32_t getIncomingDirtyFlags() override; - virtual void clearIncomingDirtyFlags() override; + uint32_t getIncomingDirtyFlags() const override; + void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) override; - virtual PhysicsMotionType computePhysicsMotionType() const override; + PhysicsMotionType computePhysicsMotionType() const override; - virtual bool isMoving() const override; + bool isMoving() const override; // this relays incoming position/rotation to the RigidBody - virtual void getWorldTransform(btTransform& worldTrans) const override; + void getWorldTransform(btTransform& worldTrans) const override; // this relays outgoing position/rotation to the EntityItem - virtual void setWorldTransform(const btTransform& worldTrans) override; + void setWorldTransform(const btTransform& worldTrans) override; // These pure virtual methods must be implemented for each MotionState type // and make it possible to implement more complicated methods in this base class. // pure virtual overrides from ObjectMotionState - virtual float getObjectRestitution() const override; - virtual float getObjectFriction() const override; - virtual float getObjectLinearDamping() const override; - virtual float getObjectAngularDamping() const override; + float getObjectRestitution() const override; + float getObjectFriction() const override; + float getObjectLinearDamping() const override; + float getObjectAngularDamping() const override; - virtual glm::vec3 getObjectPosition() const override; - virtual glm::quat getObjectRotation() const override; - virtual glm::vec3 getObjectLinearVelocity() const override; - virtual glm::vec3 getObjectAngularVelocity() const override; - virtual glm::vec3 getObjectGravity() const override; + glm::vec3 getObjectPosition() const override; + glm::quat getObjectRotation() const override; + glm::vec3 getObjectLinearVelocity() const override; + glm::vec3 getObjectAngularVelocity() const override; + glm::vec3 getObjectGravity() const override; - virtual const QUuid getObjectID() const override; + const QUuid getObjectID() const override; - virtual QString getName() const override; - virtual QUuid getSimulatorID() const override; + QString getName() const override; + ShapeType getShapeType() const override { return SHAPE_TYPE_CAPSULE_Y; } + QUuid getSimulatorID() const override; void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal); @@ -69,9 +69,9 @@ public: void setCollisionGroup(int32_t group) { _collisionGroup = group; } int32_t getCollisionGroup() { return _collisionGroup; } - virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override; + void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override; - virtual float getMass() const override; + float getMass() const override; friend class AvatarManager; friend class Avatar; @@ -85,9 +85,6 @@ protected: // ever called by the Avatar class dtor. ~AvatarMotionState(); - virtual bool isReadyToComputeShape() const override { return true; } - virtual const btCollisionShape* computeNewShape() override; - OtherAvatarPointer _avatar; float _diameter { 0.0f }; int32_t _collisionGroup; diff --git a/interface/src/avatar/DetailedMotionState.cpp b/interface/src/avatar/DetailedMotionState.cpp index cec27108ca..02a2b9d425 100644 --- a/interface/src/avatar/DetailedMotionState.cpp +++ b/interface/src/avatar/DetailedMotionState.cpp @@ -17,7 +17,7 @@ #include "MyAvatar.h" -DetailedMotionState::DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int jointIndex) : +DetailedMotionState::DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int32_t jointIndex) : ObjectMotionState(shape), _avatar(avatar), _jointIndex(jointIndex) { assert(_avatar); if (!_avatar->isMyAvatar()) { @@ -33,47 +33,26 @@ void DetailedMotionState::handleEasyChanges(uint32_t& flags) { } } -bool DetailedMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { - return ObjectMotionState::handleHardAndEasyChanges(flags, engine); -} - DetailedMotionState::~DetailedMotionState() { assert(_avatar); _avatar = nullptr; } // virtual -uint32_t DetailedMotionState::getIncomingDirtyFlags() { +uint32_t DetailedMotionState::getIncomingDirtyFlags() const { return _body ? _dirtyFlags : 0; } -void DetailedMotionState::clearIncomingDirtyFlags() { +void DetailedMotionState::clearIncomingDirtyFlags(uint32_t mask) { if (_body) { - _dirtyFlags = 0; + _dirtyFlags &= ~mask; } } PhysicsMotionType DetailedMotionState::computePhysicsMotionType() const { - // TODO?: support non-DYNAMIC motion for avatars? (e.g. when sitting) return MOTION_TYPE_KINEMATIC; } -// virtual and protected -const btCollisionShape* DetailedMotionState::computeNewShape() { - btCollisionShape* shape = nullptr; - if (!_avatar->isMyAvatar()) { - if (_otherAvatar != nullptr) { - shape = _otherAvatar->createCollisionShape(_jointIndex, _isBound, _boundJoints); - } - } else { - std::shared_ptr myAvatar = std::static_pointer_cast(_avatar); - if (myAvatar) { - shape = myAvatar->getCharacterController()->createDetailedCollisionShapeForJoint(_jointIndex); - } - } - return shape; -} - // virtual bool DetailedMotionState::isMoving() const { return false; @@ -178,11 +157,23 @@ void DetailedMotionState::setRigidBody(btRigidBody* body) { } void DetailedMotionState::setShape(const btCollisionShape* shape) { - ObjectMotionState::setShape(shape); + if (_shape != shape) { + if (_shape) { + getShapeManager()->releaseShape(_shape); + } + _shape = shape; + if (_body) { + assert(_shape); + _body->setCollisionShape(const_cast(_shape)); + } + } else if (shape) { + // we need to release unused reference to shape + getShapeManager()->releaseShape(shape); + } } void DetailedMotionState::forceActive() { if (_body && !_body->isActive()) { _body->setActivationState(ACTIVE_TAG); } -} \ No newline at end of file +} diff --git a/interface/src/avatar/DetailedMotionState.h b/interface/src/avatar/DetailedMotionState.h index a9b4b4bb64..de59f41310 100644 --- a/interface/src/avatar/DetailedMotionState.h +++ b/interface/src/avatar/DetailedMotionState.h @@ -23,55 +23,55 @@ class DetailedMotionState : public ObjectMotionState { public: DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int jointIndex); - virtual void handleEasyChanges(uint32_t& flags) override; - virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; + void handleEasyChanges(uint32_t& flags) override; - virtual PhysicsMotionType getMotionType() const override { return _motionType; } + PhysicsMotionType getMotionType() const override { return _motionType; } - virtual uint32_t getIncomingDirtyFlags() override; - virtual void clearIncomingDirtyFlags() override; + uint32_t getIncomingDirtyFlags() const override; + void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) override; - virtual PhysicsMotionType computePhysicsMotionType() const override; + PhysicsMotionType computePhysicsMotionType() const override; - virtual bool isMoving() const override; + bool isMoving() const override; // this relays incoming position/rotation to the RigidBody - virtual void getWorldTransform(btTransform& worldTrans) const override; + void getWorldTransform(btTransform& worldTrans) const override; // this relays outgoing position/rotation to the EntityItem - virtual void setWorldTransform(const btTransform& worldTrans) override; + void setWorldTransform(const btTransform& worldTrans) override; // These pure virtual methods must be implemented for each MotionState type // and make it possible to implement more complicated methods in this base class. // pure virtual overrides from ObjectMotionState - virtual float getObjectRestitution() const override; - virtual float getObjectFriction() const override; - virtual float getObjectLinearDamping() const override; - virtual float getObjectAngularDamping() const override; + float getObjectRestitution() const override; + float getObjectFriction() const override; + float getObjectLinearDamping() const override; + float getObjectAngularDamping() const override; - virtual glm::vec3 getObjectPosition() const override; - virtual glm::quat getObjectRotation() const override; - virtual glm::vec3 getObjectLinearVelocity() const override; - virtual glm::vec3 getObjectAngularVelocity() const override; - virtual glm::vec3 getObjectGravity() const override; + glm::vec3 getObjectPosition() const override; + glm::quat getObjectRotation() const override; + glm::vec3 getObjectLinearVelocity() const override; + glm::vec3 getObjectAngularVelocity() const override; + glm::vec3 getObjectGravity() const override; - virtual const QUuid getObjectID() const override; + const QUuid getObjectID() const override; - virtual QString getName() const override; - virtual QUuid getSimulatorID() const override; + QString getName() const override; + ShapeType getShapeType() const override { return SHAPE_TYPE_HULL; } + QUuid getSimulatorID() const override; void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; } - virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override; + void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override; - virtual float getMass() const override; + float getMass() const override; void forceActive(); QUuid getAvatarID() const { return _avatar->getID(); } - int getJointIndex() const { return _jointIndex; } - void setIsBound(bool isBound, std::vector boundJoints) { _isBound = isBound; _boundJoints = boundJoints; } - bool getIsBound(std::vector& boundJoints) const { boundJoints = _boundJoints; return _isBound; } + int32_t getJointIndex() const { return _jointIndex; } + void setIsBound(bool isBound, const std::vector& boundJoints) { _isBound = isBound; _boundJoints = boundJoints; } + bool getIsBound(std::vector& boundJoints) const { boundJoints = _boundJoints; return _isBound; } friend class AvatarManager; friend class Avatar; @@ -84,17 +84,14 @@ protected: // ever called by the Avatar class dtor. ~DetailedMotionState(); - virtual bool isReadyToComputeShape() const override { return true; } - virtual const btCollisionShape* computeNewShape() override; - AvatarPointer _avatar; float _diameter { 0.0f }; uint32_t _dirtyFlags; - int _jointIndex { -1 }; + int32_t _jointIndex { -1 }; OtherAvatarPointer _otherAvatar { nullptr }; bool _isBound { false }; - std::vector _boundJoints; + std::vector _boundJoints; }; #endif // hifi_DetailedMotionState_h diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index b0123abe8d..aef1bcd668 100755 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -377,21 +377,18 @@ void MyCharacterController::updateMassProperties() { _rigidBody->setMassProps(mass, inertia); } -btCollisionShape* MyCharacterController::createDetailedCollisionShapeForJoint(int jointIndex) { +const btCollisionShape* MyCharacterController::createDetailedCollisionShapeForJoint(int32_t jointIndex) { ShapeInfo shapeInfo; _avatar->computeDetailedShapeInfo(shapeInfo, jointIndex); if (shapeInfo.getType() != SHAPE_TYPE_NONE) { - btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); - if (shape) { - shape->setMargin(0.001f); - } + const btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); return shape; } return nullptr; } -DetailedMotionState* MyCharacterController::createDetailedMotionStateForJoint(int jointIndex) { - auto shape = createDetailedCollisionShapeForJoint(jointIndex); +DetailedMotionState* MyCharacterController::createDetailedMotionStateForJoint(int32_t jointIndex) { + const btCollisionShape* shape = createDetailedCollisionShapeForJoint(jointIndex); if (shape) { DetailedMotionState* motionState = new DetailedMotionState(_avatar, shape, jointIndex); motionState->setMass(_avatar->computeMass()); @@ -423,25 +420,16 @@ void MyCharacterController::buildPhysicsTransaction(PhysicsEngine::Transaction& } if (_pendingFlags & PENDING_FLAG_ADD_DETAILED_TO_SIMULATION) { _pendingFlags &= ~PENDING_FLAG_ADD_DETAILED_TO_SIMULATION; - for (int i = 0; i < _avatar->getJointCount(); i++) { + for (int32_t i = 0; i < _avatar->getJointCount(); i++) { auto dMotionState = createDetailedMotionStateForJoint(i); if (dMotionState) { _detailedMotionStates.push_back(dMotionState); transaction.objectsToAdd.push_back(dMotionState); } } - } -} - -void MyCharacterController::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) { - // things on objectsToRemove are ready for delete - for (auto object : transaction.objectsToRemove) { - delete object; } - transaction.clear(); } - class DetailedRayResultCallback : public btCollisionWorld::AllHitsRayResultCallback { public: DetailedRayResultCallback() @@ -467,7 +455,7 @@ std::vector MyCharacterController::rayTe _dynamicsWorld->rayTest(origin, end, rayCallback); if (rayCallback.m_hitFractions.size() > 0) { foundAvatars.reserve(rayCallback.m_hitFractions.size()); - for (int i = 0; i < rayCallback.m_hitFractions.size(); i++) { + for (int32_t i = 0; i < rayCallback.m_hitFractions.size(); i++) { auto object = rayCallback.m_collisionObjects[i]; ObjectMotionState* motionState = static_cast(object->getUserPointer()); if (motionState && motionState->getType() == MOTIONSTATE_TYPE_DETAILED) { @@ -493,4 +481,4 @@ std::vector MyCharacterController::rayTe } } return foundAvatars; -} \ No newline at end of file +} diff --git a/interface/src/avatar/MyCharacterController.h b/interface/src/avatar/MyCharacterController.h index f861f82422..0b64f66850 100644 --- a/interface/src/avatar/MyCharacterController.h +++ b/interface/src/avatar/MyCharacterController.h @@ -44,27 +44,25 @@ public: void setDensity(btScalar density) { _density = density; } - btCollisionShape* createDetailedCollisionShapeForJoint(int jointIndex); - DetailedMotionState* createDetailedMotionStateForJoint(int jointIndex); + const btCollisionShape* createDetailedCollisionShapeForJoint(int32_t jointIndex); + DetailedMotionState* createDetailedMotionStateForJoint(int32_t jointIndex); std::vector& getDetailedMotionStates() { return _detailedMotionStates; } void clearDetailedMotionStates(); void resetDetailedMotionStates(); void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction); - void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction); - struct RayAvatarResult { bool _intersect { false }; bool _isBound { false }; QUuid _intersectWithAvatar; - int _intersectWithJoint { -1 }; + int32_t _intersectWithJoint { -1 }; float _distance { 0.0f }; float _maxDistance { 0.0f }; QVariantMap _extraInfo; glm::vec3 _intersectionPoint; glm::vec3 _intersectionNormal; - std::vector _boundJoints; + std::vector _boundJoints; }; std::vector rayTest(const btVector3& origin, const btVector3& direction, const btScalar& length, const QVector& jointsToExclude) const; diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index d8cfe8f107..107932d5ec 100755 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -116,6 +116,8 @@ void OtherAvatar::updateSpaceProxy(workload::Transaction& transaction) const { int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) { int32_t bytesRead = Avatar::parseDataFromBuffer(buffer); for (size_t i = 0; i < _detailedMotionStates.size(); i++) { + // NOTE: we activate _detailedMotionStates is because they are KINEMATIC + // and Bullet will automagically call DetailedMotionState::getWorldTransform() when active. _detailedMotionStates[i]->forceActive(); } if (_moving && _motionState) { @@ -124,11 +126,11 @@ int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) { return bytesRead; } -btCollisionShape* OtherAvatar::createCollisionShape(int jointIndex, bool& isBound, std::vector& boundJoints) { +const btCollisionShape* OtherAvatar::createCollisionShape(int32_t jointIndex, bool& isBound, std::vector& boundJoints) { ShapeInfo shapeInfo; isBound = false; - QString jointName = ""; - if (jointIndex > -1 && jointIndex < (int)_multiSphereShapes.size()) { + QString jointName = ""; + if (jointIndex > -1 && jointIndex < (int32_t)_multiSphereShapes.size()) { jointName = _multiSphereShapes[jointIndex].getJointName(); } switch (_bodyLOD) { @@ -163,39 +165,21 @@ btCollisionShape* OtherAvatar::createCollisionShape(int jointIndex, bool& isBoun } break; } + // Note: MultiSphereLow case really means: "skip fingers and use spheres for hands, + // else fall through to MultiSphereHigh case" case BodyLOD::MultiSphereHigh: computeDetailedShapeInfo(shapeInfo, jointIndex); break; default: + assert(false); // should never reach here break; } - if (shapeInfo.getType() != SHAPE_TYPE_NONE) { - auto shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); - if (shape) { - shape->setMargin(0.001f); - } - return shape; - } - return nullptr; -} - -DetailedMotionState* OtherAvatar::createMotionState(std::shared_ptr avatar, int jointIndex) { - bool isBound = false; - std::vector boundJoints; - btCollisionShape* shape = createCollisionShape(jointIndex, isBound, boundJoints); - if (shape) { - DetailedMotionState* motionState = new DetailedMotionState(avatar, shape, jointIndex); - motionState->setMass(computeMass()); - motionState->setIsBound(isBound, boundJoints); - return motionState; - } - return nullptr; + return ObjectMotionState::getShapeManager()->getShape(shapeInfo); } void OtherAvatar::resetDetailedMotionStates() { - for (size_t i = 0; i < _detailedMotionStates.size(); i++) { - _detailedMotionStates[i] = nullptr; - } + // NOTE: the DetailedMotionStates are deleted after being added to PhysicsEngine::Transaction::_objectsToRemove + // See AvatarManager::handleProcessedPhysicsTransaction() _detailedMotionStates.clear(); } @@ -231,11 +215,11 @@ void OtherAvatar::computeShapeLOD() { } bool OtherAvatar::isInPhysicsSimulation() const { - return _motionState != nullptr && _detailedMotionStates.size() > 0; + return _motionState && _motionState->getRigidBody(); } bool OtherAvatar::shouldBeInPhysicsSimulation() const { - return !isDead() && !(isInPhysicsSimulation() && _needsReinsertion); + return !isDead() && _workloadRegion < workload::Region::R3; } bool OtherAvatar::needsPhysicsUpdate() const { @@ -245,12 +229,9 @@ bool OtherAvatar::needsPhysicsUpdate() const { void OtherAvatar::rebuildCollisionShape() { if (_motionState) { + // do not actually rebuild here, instead flag for later _motionState->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); - } - for (size_t i = 0; i < _detailedMotionStates.size(); i++) { - if (_detailedMotionStates[i]) { - _detailedMotionStates[i]->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); - } + _needsReinsertion = true; } } @@ -260,25 +241,6 @@ void OtherAvatar::setCollisionWithOtherAvatarsFlags() { } } -void OtherAvatar::createDetailedMotionStates(const std::shared_ptr& avatar) { - auto& detailedMotionStates = getDetailedMotionStates(); - assert(detailedMotionStates.empty()); - if (_bodyLOD == BodyLOD::Sphere) { - auto dMotionState = createMotionState(avatar, -1); - if (dMotionState) { - detailedMotionStates.push_back(dMotionState); - } - } else { - for (int i = 0; i < getJointCount(); i++) { - auto dMotionState = createMotionState(avatar, i); - if (dMotionState) { - detailedMotionStates.push_back(dMotionState); - } - } - } - _needsReinsertion = false; -} - void OtherAvatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "simulate"); diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index 43bfd2a9ae..498971d6ee 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -52,9 +52,7 @@ public: bool shouldBeInPhysicsSimulation() const; bool needsPhysicsUpdate() const; - btCollisionShape* createCollisionShape(int jointIndex, bool& isBound, std::vector& boundJoints); - DetailedMotionState* createMotionState(std::shared_ptr avatar, int jointIndex); - void createDetailedMotionStates(const std::shared_ptr& avatar); + const btCollisionShape* createCollisionShape(int32_t jointIndex, bool& isBound, std::vector& boundJoints); std::vector& getDetailedMotionStates() { return _detailedMotionStates; } void resetDetailedMotionStates(); BodyLOD getBodyLOD() { return _bodyLOD; } diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index e56ca984e0..479c2a5860 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -168,7 +168,7 @@ bool isEntityPhysicsReady(const EntityItemPointer& entity) { bool hasAABox; entity->getAABox(hasAABox); if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) { - return (!entity->shouldBePhysical() || entity->isReadyToComputeShape() || modelEntity->computeShapeFailedToLoad()); + return (!entity->shouldBePhysical() || entity->isInPhysicsSimulation() || modelEntity->computeShapeFailedToLoad()); } } } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index e8ce812285..fbf577755d 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -481,6 +481,12 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene } } +void EntityTreeRenderer::preUpdate() { + if (_tree && !_shuttingDown) { + _tree->preUpdate(); + } +} + void EntityTreeRenderer::update(bool simulate) { PROFILE_RANGE(simulation_physics, "ETR::update"); PerformanceTimer perfTimer("ETRupdate"); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index f4284078a3..a0243a159a 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -78,6 +78,7 @@ public: void setSetPrecisionPickingOperator(std::function setPrecisionPickingOperator) { _setPrecisionPickingOperator = setPrecisionPickingOperator; } void shutdown(); + void preUpdate(); void update(bool simulate); EntityTreePointer getTree() { return std::static_pointer_cast(_tree); } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index bfbbe12ea6..7602028e36 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -735,13 +735,15 @@ bool RenderableModelEntityItem::shouldBePhysical() const { auto model = getModel(); // If we have a model, make sure it hasn't failed to download. // If it has, we'll report back that we shouldn't be physical so that physics aren't held waiting for us to be ready. - if (model && (getShapeType() == SHAPE_TYPE_COMPOUND || getShapeType() == SHAPE_TYPE_SIMPLE_COMPOUND) && model->didCollisionGeometryRequestFail()) { - return false; - } else if (model && getShapeType() != SHAPE_TYPE_NONE && model->didVisualGeometryRequestFail()) { - return false; - } else { - return ModelEntityItem::shouldBePhysical(); + ShapeType shapeType = getShapeType(); + if (model) { + if ((shapeType == SHAPE_TYPE_COMPOUND || shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) && model->didCollisionGeometryRequestFail()) { + return false; + } else if (shapeType != SHAPE_TYPE_NONE && model->didVisualGeometryRequestFail()) { + return false; + } } + return !isDead() && shapeType != SHAPE_TYPE_NONE && QUrl(_modelURL).isValid(); } int RenderableModelEntityItem::getJointParent(int index) const { diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 7aea87535e..cca34767a4 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -72,7 +72,6 @@ public: glm::mat4 localToVoxelMatrix() const; virtual ShapeType getShapeType() const override; - virtual bool shouldBePhysical() const override { return !isDead(); } virtual bool isReadyToComputeShape() const override; virtual void computeShapeInfo(ShapeInfo& info) override; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 7cc35f8be0..d91c11e726 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1828,42 +1828,42 @@ void EntityItem::setParentID(const QUuid& value) { if (!value.isNull() && tree) { EntityItemPointer entity = tree->findEntityByEntityItemID(value); if (entity) { - newParentNoBootstrapping = entity->getSpecialFlags() & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + newParentNoBootstrapping = entity->getSpecialFlags() & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING; } } if (!oldParentID.isNull() && tree) { EntityItemPointer entity = tree->findEntityByEntityItemID(oldParentID); if (entity) { - oldParentNoBootstrapping = entity->getDirtyFlags() & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + oldParentNoBootstrapping = entity->getDirtyFlags() & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING; } } if (!value.isNull() && (value == Physics::getSessionUUID() || value == AVATAR_SELF_ID)) { - newParentNoBootstrapping |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + newParentNoBootstrapping |= Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING; } if (!oldParentID.isNull() && (oldParentID == Physics::getSessionUUID() || oldParentID == AVATAR_SELF_ID)) { - oldParentNoBootstrapping |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + oldParentNoBootstrapping |= Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING; } if ((bool)(oldParentNoBootstrapping ^ newParentNoBootstrapping)) { - if ((bool)(newParentNoBootstrapping & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { - markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + if ((bool)(newParentNoBootstrapping & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING)) { + markSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING); forEachDescendant([&](SpatiallyNestablePointer object) { if (object->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(object); entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - entity->markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + entity->markSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING); } }); } else { - clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + clearSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING); forEachDescendant([&](SpatiallyNestablePointer object) { if (object->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(object); entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + entity->clearSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING); } }); } @@ -2102,7 +2102,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int32_t& group, int32_t& mask } } - if ((bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { + if ((bool)(_flags & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING)) { userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } mask = Physics::getDefaultCollisionMask(group) & (int32_t)(userMask); @@ -2173,8 +2173,8 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityDynamicPoin } void EntityItem::enableNoBootstrap() { - if (!(bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { - _flags |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + if (!(bool)(_flags & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING)) { + _flags |= Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING; _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar // NOTE: unlike disableNoBootstrap() below, we do not call simulation->changeEntity() here @@ -2186,7 +2186,7 @@ void EntityItem::enableNoBootstrap() { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - entity->markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + entity->markSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING); } }); } @@ -2194,7 +2194,7 @@ void EntityItem::enableNoBootstrap() { void EntityItem::disableNoBootstrap() { if (!stillHasMyGrabAction()) { - _flags &= ~Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + _flags &= ~Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING; _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar EntityTreePointer entityTree = getTree(); @@ -2207,7 +2207,7 @@ void EntityItem::disableNoBootstrap() { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + entity->clearSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING); simulation->changeEntity(entity); } }); @@ -2326,7 +2326,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi if (removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) { disableNoBootstrap(); } else { - // NO-OP: we assume SPECIAL_FLAGS_NO_BOOTSTRAPPING bits and collision group are correct + // NO-OP: we assume SPECIAL_FLAG_NO_BOOTSTRAPPING bits and collision group are correct // because they should have been set correctly when the action was added // and/or when children were linked } @@ -3154,21 +3154,21 @@ DEFINE_PROPERTY_ACCESSOR(quint32, StaticCertificateVersion, staticCertificateVer uint32_t EntityItem::getDirtyFlags() const { uint32_t result; withReadLock([&] { - result = _flags & Simulation::DIRTY_FLAGS; + result = _flags & Simulation::DIRTY_FLAGS_MASK; }); return result; } void EntityItem::markDirtyFlags(uint32_t mask) { withWriteLock([&] { - mask &= Simulation::DIRTY_FLAGS; + mask &= Simulation::DIRTY_FLAGS_MASK; _flags |= mask; }); } void EntityItem::clearDirtyFlags(uint32_t mask) { withWriteLock([&] { - mask &= Simulation::DIRTY_FLAGS; + mask &= Simulation::DIRTY_FLAGS_MASK; _flags &= ~mask; }); } @@ -3176,21 +3176,21 @@ void EntityItem::clearDirtyFlags(uint32_t mask) { uint32_t EntityItem::getSpecialFlags() const { uint32_t result; withReadLock([&] { - result = _flags & Simulation::SPECIAL_FLAGS; + result = _flags & Simulation::SPECIAL_FLAGS_MASK; }); return result; } void EntityItem::markSpecialFlags(uint32_t mask) { withWriteLock([&] { - mask &= Simulation::SPECIAL_FLAGS; + mask &= Simulation::SPECIAL_FLAGS_MASK; _flags |= mask; }); } void EntityItem::clearSpecialFlags(uint32_t mask) { withWriteLock([&] { - mask &= Simulation::SPECIAL_FLAGS; + mask &= Simulation::SPECIAL_FLAGS_MASK; _flags &= ~mask; }); } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 29a1a8d73c..ea4b11e0b0 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -23,6 +23,7 @@ #include // for OctreeElement::AppendState #include #include +#include #include #include #include @@ -33,11 +34,11 @@ #include "EntityPropertyFlags.h" #include "EntityTypes.h" #include "SimulationOwner.h" -#include "SimulationFlags.h" #include "EntityDynamicInterface.h" #include "GrabPropertyGroup.h" class EntitySimulation; +using EntitySimulationPointer = std::shared_ptr; class EntityTreeElement; class EntityTreeElementExtraEncodeData; class EntityDynamicInterface; @@ -322,7 +323,7 @@ public: bool getDynamic() const; void setDynamic(bool value); - virtual bool shouldBePhysical() const { return false; } + virtual bool shouldBePhysical() const { return !isDead() && getShapeType() != SHAPE_TYPE_NONE; } bool isVisuallyReady() const { return _visuallyReady; } bool getLocked() const; @@ -423,8 +424,9 @@ public: bool isSimulated() const { return _simulated; } - void* getPhysicsInfo() const { return _physicsInfo; } + bool isInPhysicsSimulation() const { return (bool)(_flags & Simulation::SPECIAL_FLAG_IN_PHYSICS_SIMULATION); } + void* getPhysicsInfo() const { return _physicsInfo; } void setPhysicsInfo(void* data) { _physicsInfo = data; } EntityTreeElementPointer getElement() const { return _element; } diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index 05e71a8f06..b5e4fed0fd 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -176,47 +176,44 @@ void EntitySimulation::addEntity(EntityItemPointer entity) { void EntitySimulation::changeEntity(EntityItemPointer entity) { QMutexLocker lock(&_mutex); assert(entity); - if (!entity->isSimulated()) { - // This entity was either never added to the simulation or has been removed - // (probably for pending delete), so we don't want to keep a pointer to it - // on any internal lists. - return; - } + _changedEntities.insert(entity); +} - // Although it is not the responsibility of the EntitySimulation to sort the tree for EXTERNAL changes - // it IS responsibile for triggering deletes for entities that leave the bounds of the domain, hence - // we must check for that case here, however we rely on the change event to have set DIRTY_POSITION flag. +void EntitySimulation::processChangedEntities() { + QMutexLocker lock(&_mutex); + PROFILE_RANGE_EX(simulation_physics, "processChangedEntities", 0xffff00ff, (uint64_t)_changedEntities.size()); + for (auto& entity : _changedEntities) { + if (entity->isSimulated()) { + processChangedEntity(entity); + } + } + _changedEntities.clear(); +} + +void EntitySimulation::processChangedEntity(const EntityItemPointer& entity) { uint32_t dirtyFlags = entity->getDirtyFlags(); - if (dirtyFlags & Simulation::DIRTY_POSITION) { - AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE), (float)TREE_SCALE); - bool success; - AACube newCube = entity->getQueryAACube(success); - if (success && !domainBounds.touches(newCube)) { - qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds."; - entity->die(); - prepareEntityForDelete(entity); - return; - } - } - if (dirtyFlags & Simulation::DIRTY_LIFETIME) { - if (entity->isMortal()) { - _mortalEntities.insert(entity); - uint64_t expiry = entity->getExpiry(); - if (expiry < _nextExpiry) { - _nextExpiry = expiry; + if (dirtyFlags & (Simulation::DIRTY_LIFETIME | Simulation::DIRTY_UPDATEABLE)) { + if (dirtyFlags & Simulation::DIRTY_LIFETIME) { + if (entity->isMortal()) { + _mortalEntities.insert(entity); + uint64_t expiry = entity->getExpiry(); + if (expiry < _nextExpiry) { + _nextExpiry = expiry; + } + } else { + _mortalEntities.remove(entity); } - } else { - _mortalEntities.remove(entity); } - entity->clearDirtyFlags(Simulation::DIRTY_LIFETIME); + if (dirtyFlags & Simulation::DIRTY_UPDATEABLE) { + if (entity->needsToCallUpdate()) { + _entitiesToUpdate.insert(entity); + } else { + _entitiesToUpdate.remove(entity); + } + } + entity->clearDirtyFlags(Simulation::DIRTY_LIFETIME | Simulation::DIRTY_UPDATEABLE); } - if (entity->needsToCallUpdate()) { - _entitiesToUpdate.insert(entity); - } else { - _entitiesToUpdate.remove(entity); - } - changeEntityInternal(entity); } void EntitySimulation::clearEntities() { diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h index f107bcae6e..1dd0369561 100644 --- a/libraries/entities/src/EntitySimulation.h +++ b/libraries/entities/src/EntitySimulation.h @@ -13,6 +13,7 @@ #define hifi_EntitySimulation_h #include +#include #include #include @@ -82,13 +83,15 @@ public: /// \param entity pointer to EntityItem that needs to be put on the entitiesToDelete list and removed from others. virtual void prepareEntityForDelete(EntityItemPointer entity); + void processChangedEntities(); + protected: // These pure virtual methods are protected because they are not to be called will-nilly. The base class // calls them in the right places. virtual void updateEntitiesInternal(uint64_t now) = 0; virtual void addEntityInternal(EntityItemPointer entity) = 0; virtual void removeEntityInternal(EntityItemPointer entity); - virtual void changeEntityInternal(EntityItemPointer entity) = 0; + virtual void processChangedEntity(const EntityItemPointer& entity); virtual void clearEntitiesInternal() = 0; void expireMortalEntities(uint64_t now); @@ -114,11 +117,11 @@ private: // We maintain multiple lists, each for its distinct purpose. // An entity may be in more than one list. + std::unordered_set _changedEntities; // all changes this frame SetOfEntities _allEntities; // tracks all entities added the simulation SetOfEntities _mortalEntities; // entities that have an expiry uint64_t _nextExpiry; - SetOfEntities _entitiesToUpdate; // entities that need to call EntityItem::update() }; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b3dd78ae92..fe8c8c9336 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2079,7 +2079,6 @@ void EntityTree::entityChanged(EntityItemPointer entity) { } void EntityTree::fixupNeedsParentFixups() { - PROFILE_RANGE(simulation_physics, "FixupParents"); MovingEntitiesOperator moveOperator; QVector entitiesToFixup; { @@ -2189,11 +2188,19 @@ void EntityTree::addToNeedsParentFixupList(EntityItemPointer entity) { _needsParentFixup.append(entity); } +void EntityTree::preUpdate() { + withWriteLock([&] { + fixupNeedsParentFixups(); + if (_simulation) { + _simulation->processChangedEntities(); + } + }); +} + void EntityTree::update(bool simulate) { PROFILE_RANGE(simulation_physics, "UpdateTree"); PerformanceTimer perfTimer("updateTree"); withWriteLock([&] { - fixupNeedsParentFixups(); if (simulate && _simulation) { _simulation->updateEntities(); { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index c80517c82b..9108f8d8d2 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -109,9 +109,10 @@ public: virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const override; - virtual void update() override { update(true); } - - void update(bool simulate); + // Why preUpdate() and update()? + // Because sometimes we need to do stuff between the two. + void preUpdate() override; + void update(bool simulate = true) override; // The newer API... void postAddEntity(EntityItemPointer entityItem); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 505ee26c0f..cb9637acd5 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -421,11 +421,6 @@ void ModelEntityItem::setAnimationFPS(float value) { }); } -// virtual -bool ModelEntityItem::shouldBePhysical() const { - return !isDead() && getShapeType() != SHAPE_TYPE_NONE && QUrl(_modelURL).isValid(); -} - void ModelEntityItem::resizeJointArrays(int newSize) { if (newSize < 0) { return; diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index d532fefe7e..75a695f1c0 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -118,8 +118,6 @@ public: const QString getTextures() const; void setTextures(const QString& textures); - virtual bool shouldBePhysical() const override; - virtual void setJointRotations(const QVector& rotations); virtual void setJointRotationsSet(const QVector& rotationsSet); virtual void setJointTranslations(const QVector& translations); diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 52f229201e..b526298a4b 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -231,6 +231,8 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; + bool shouldBePhysical() const override { return false; } + void setColor(const glm::u8vec3& value); glm::u8vec3 getColor() const { return _particleProperties.color.gradient.target; } diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index dd05ce67b1..a6076dfda7 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -164,7 +164,6 @@ class PolyVoxEntityItem : public EntityItem { glm::vec3 getSurfacePositionAdjustment() const; virtual ShapeType getShapeType() const override; - virtual bool shouldBePhysical() const override { return !isDead(); } bool isEdged() const; diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index fc590e06a4..3622c74f50 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -84,8 +84,6 @@ public: void setUnscaledDimensions(const glm::vec3& value) override; - bool shouldBePhysical() const override { return !isDead(); } - bool supportsDetailedIntersection() const override; bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index 28dc7b26c4..b8e3df2d03 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -85,7 +85,9 @@ void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) { _entitiesThatNeedSimulationOwner.remove(entity); } -void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) { +void SimpleEntitySimulation::processChangedEntity(const EntityItemPointer& entity) { + EntitySimulation::processChangedEntity(entity); + uint32_t flags = entity->getDirtyFlags(); if ((flags & Simulation::DIRTY_SIMULATOR_ID) || (flags & Simulation::DIRTY_VELOCITIES)) { if (entity->getSimulatorID().isNull()) { diff --git a/libraries/entities/src/SimpleEntitySimulation.h b/libraries/entities/src/SimpleEntitySimulation.h index 8ac9b69e93..1b240a8bf0 100644 --- a/libraries/entities/src/SimpleEntitySimulation.h +++ b/libraries/entities/src/SimpleEntitySimulation.h @@ -31,7 +31,7 @@ protected: void updateEntitiesInternal(uint64_t now) override; void addEntityInternal(EntityItemPointer entity) override; void removeEntityInternal(EntityItemPointer entity) override; - void changeEntityInternal(EntityItemPointer entity) override; + void processChangedEntity(const EntityItemPointer& entity) override; void clearEntitiesInternal() override; void sortEntitiesThatMoved() override; diff --git a/libraries/entities/src/SimulationOwner.h b/libraries/entities/src/SimulationOwner.h index f574234354..6f37066e47 100644 --- a/libraries/entities/src/SimulationOwner.h +++ b/libraries/entities/src/SimulationOwner.h @@ -89,6 +89,8 @@ // (14) When an entity's ownership priority drops to YIELD (=1, below VOLUNTEER) other participants may // bid for it immediately at VOLUNTEER. // +/* These declarations temporarily moved to SimulationFlags.h while we unravel some spaghetti dependencies. + * The intent is to move them back here once the dust settles. const uint8_t YIELD_SIMULATION_PRIORITY = 1; const uint8_t VOLUNTEER_SIMULATION_PRIORITY = YIELD_SIMULATION_PRIORITY + 1; const uint8_t RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1; @@ -101,6 +103,7 @@ const uint8_t SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY // which really just means: things that collide with it will be bid at a priority level one lower const uint8_t PERSONAL_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY; const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = PERSONAL_SIMULATION_PRIORITY; +*/ class SimulationOwner { diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 69e3227135..34ad47f095 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -62,6 +62,7 @@ public: virtual bool isReadyToComputeShape() const override { return false; } virtual void setShapeType(ShapeType type) override; virtual ShapeType getShapeType() const override; + bool shouldBePhysical() const override { return false; } QString getCompoundShapeURL() const; virtual void setCompoundShapeURL(const QString& url); diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 82076f618b..4f994da60e 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -145,7 +145,10 @@ public: virtual bool rootElementHasData() const { return false; } virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const { } - virtual void update() { } // nothing to do by default + // Why preUpdate() and update()? + // Because EntityTree needs them. + virtual void preUpdate() { } + virtual void update(bool simulate = true) { } OctreeElementPointer getRoot() { return _rootElement; } diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 20ba3cde60..dd2cc12d50 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -242,6 +242,7 @@ bool OctreePersistThread::backupCurrentFile() { } void OctreePersistThread::process() { + _tree->preUpdate(); _tree->update(); auto now = std::chrono::steady_clock::now(); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 6abe5c3899..67aa7d2d7d 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -25,23 +25,6 @@ #include "PhysicsHelpers.h" #include "PhysicsLogging.h" -#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS -#include "EntityTree.h" - -bool EntityMotionState::entityTreeIsLocked() const { - EntityTreeElementPointer element = _entity->getElement(); - EntityTreePointer tree = element ? element->getTree() : nullptr; - if (!tree) { - return true; - } - return true; -} -#else -bool entityTreeIsLocked() { - return true; -} -#endif - const uint8_t LOOPS_FOR_SIMULATION_ORPHAN = 50; const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5; @@ -74,7 +57,6 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer _type = MOTIONSTATE_TYPE_ENTITY; assert(_entity); - assert(entityTreeIsLocked()); setMass(_entity->computeMass()); // we need the side-effects of EntityMotionState::setShape() so we call it explicitly here // rather than pass the legit shape pointer to the ObjectMotionState ctor above. @@ -143,7 +125,6 @@ void EntityMotionState::handleDeactivation() { // virtual void EntityMotionState::handleEasyChanges(uint32_t& flags) { - assert(entityTreeIsLocked()); updateServerPhysicsVariables(); ObjectMotionState::handleEasyChanges(flags); @@ -191,17 +172,10 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { } -// virtual -bool EntityMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { - updateServerPhysicsVariables(); - return ObjectMotionState::handleHardAndEasyChanges(flags, engine); -} - PhysicsMotionType EntityMotionState::computePhysicsMotionType() const { if (!_entity) { return MOTION_TYPE_STATIC; } - assert(entityTreeIsLocked()); if (_entity->getLocked()) { if (_entity->isMoving()) { @@ -226,7 +200,6 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const { } bool EntityMotionState::isMoving() const { - assert(entityTreeIsLocked()); return _entity && _entity->isMovingRelativeToParent(); } @@ -240,7 +213,6 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { if (!_entity) { return; } - assert(entityTreeIsLocked()); if (_motionType == MOTION_TYPE_KINEMATIC) { BT_PROFILE("kinematicIntegration"); uint32_t thisStep = ObjectMotionState::getWorldSimulationStep(); @@ -271,7 +243,6 @@ 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 ACTIVE. void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { - assert(entityTreeIsLocked()); measureBodyAcceleration(); // If transform or velocities are flagged as dirty it means a network or scripted change @@ -309,19 +280,6 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { } -// virtual and protected -bool EntityMotionState::isReadyToComputeShape() const { - return _entity->isReadyToComputeShape(); -} - -// virtual and protected -const btCollisionShape* EntityMotionState::computeNewShape() { - ShapeInfo shapeInfo; - assert(entityTreeIsLocked()); - _entity->computeShapeInfo(shapeInfo); - return getShapeManager()->getShape(shapeInfo); -} - const uint8_t MAX_NUM_INACTIVE_UPDATES = 20; bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { @@ -439,7 +397,6 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { DETAILED_PROFILE_RANGE(simulation_physics, "ShouldSend"); // NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called // after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL. - assert(entityTreeIsLocked()); // this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor assert(!(_entity->isAvatarEntity() && _entity->getOwningAvatarID() != Physics::getSessionUUID())); @@ -505,7 +462,6 @@ void EntityMotionState::updateSendVelocities() { void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t step) { DETAILED_PROFILE_RANGE(simulation_physics, "Bid"); - assert(entityTreeIsLocked()); updateSendVelocities(); @@ -546,7 +502,6 @@ void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t s void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) { DETAILED_PROFILE_RANGE(simulation_physics, "Send"); - assert(entityTreeIsLocked()); assert(isLocallyOwned()); updateSendVelocities(); @@ -645,8 +600,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ _bumpedPriority = 0; } -uint32_t EntityMotionState::getIncomingDirtyFlags() { - assert(entityTreeIsLocked()); +uint32_t EntityMotionState::getIncomingDirtyFlags() const { uint32_t dirtyFlags = 0; if (_body && _entity) { dirtyFlags = _entity->getDirtyFlags(); @@ -676,10 +630,9 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() { return dirtyFlags; } -void EntityMotionState::clearIncomingDirtyFlags() { - assert(entityTreeIsLocked()); +void EntityMotionState::clearIncomingDirtyFlags(uint32_t mask) { if (_body && _entity) { - _entity->clearDirtyFlags(DIRTY_PHYSICS_FLAGS); + _entity->clearDirtyFlags(mask); } } @@ -694,7 +647,6 @@ void EntityMotionState::slaveBidPriority() { // virtual QUuid EntityMotionState::getSimulatorID() const { - assert(entityTreeIsLocked()); return _entity->getSimulatorID(); } @@ -762,6 +714,10 @@ glm::vec3 EntityMotionState::getObjectLinearVelocityChange() const { return _measuredAcceleration * _measuredDeltaTime; } +bool EntityMotionState::shouldBeInPhysicsSimulation() const { + return _region < workload::Region::R3 && _entity->shouldBePhysical(); +} + // virtual void EntityMotionState::setMotionType(PhysicsMotionType motionType) { ObjectMotionState::setMotionType(motionType); @@ -770,7 +726,6 @@ void EntityMotionState::setMotionType(PhysicsMotionType motionType) { // virtual QString EntityMotionState::getName() const { - assert(entityTreeIsLocked()); return _entity->getName(); } @@ -788,6 +743,15 @@ bool EntityMotionState::shouldSendBid() const { && !_entity->getLocked(); } +void EntityMotionState::setRigidBody(btRigidBody* body) { + ObjectMotionState::setRigidBody(body); + if (_body) { + _entity->markSpecialFlags(Simulation::SPECIAL_FLAG_IN_PHYSICS_SIMULATION); + } else { + _entity->clearSpecialFlags(Simulation::SPECIAL_FLAG_IN_PHYSICS_SIMULATION); + } +} + uint8_t EntityMotionState::computeFinalBidPriority() const { return (_region == workload::Region::R1) ? glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) : 0; diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index ddf384dc77..7456837777 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -12,6 +12,7 @@ #ifndef hifi_EntityMotionState_h #define hifi_EntityMotionState_h +#include #include #include #include @@ -38,7 +39,6 @@ public: void handleDeactivation(); virtual void handleEasyChanges(uint32_t& flags) override; - virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; /// \return PhysicsMotionType based on params set in EntityItem virtual PhysicsMotionType computePhysicsMotionType() const override; @@ -55,8 +55,8 @@ public: void sendBid(OctreeEditPacketSender* packetSender, uint32_t step); void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step); - virtual uint32_t getIncomingDirtyFlags() override; - virtual void clearIncomingDirtyFlags() override; + virtual uint32_t getIncomingDirtyFlags() const override; + virtual void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) override; virtual float getObjectRestitution() const override { return _entity->getRestitution(); } virtual float getObjectFriction() const override { return _entity->getFriction(); } @@ -84,6 +84,7 @@ public: void measureBodyAcceleration(); virtual QString getName() const override; + ShapeType getShapeType() const override { return _entity->getShapeType(); } virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override; @@ -99,6 +100,8 @@ public: void saveKinematicState(btScalar timeStep) override; protected: + void setRigidBody(btRigidBody* body) override; + uint8_t computeFinalBidPriority() const; void updateSendVelocities(); uint64_t getNextBidExpiry() const { return _nextBidExpiry; } @@ -112,12 +115,8 @@ protected: void clearObjectVelocities() const; - #ifdef WANT_DEBUG_ENTITY_TREE_LOCKS - bool entityTreeIsLocked() const; - #endif - - bool isReadyToComputeShape() const override; - const btCollisionShape* computeNewShape() override; + bool isInPhysicsSimulation() const { return _body != nullptr; } + bool shouldBeInPhysicsSimulation() const; void setMotionType(PhysicsMotionType motionType) override; // EntityMotionState keeps a SharedPointer to its EntityItem which is only set in the CTOR diff --git a/libraries/physics/src/ObjectActionTractor.cpp b/libraries/physics/src/ObjectActionTractor.cpp index c7681e217c..b53c2e137a 100644 --- a/libraries/physics/src/ObjectActionTractor.cpp +++ b/libraries/physics/src/ObjectActionTractor.cpp @@ -11,7 +11,8 @@ #include "ObjectActionTractor.h" -#include "QVariantGLM.h" +#include +#include #include "PhysicsLogging.h" diff --git a/libraries/physics/src/ObjectActionTravelOriented.cpp b/libraries/physics/src/ObjectActionTravelOriented.cpp index bd32ed13ff..b27ec40ae4 100644 --- a/libraries/physics/src/ObjectActionTravelOriented.cpp +++ b/libraries/physics/src/ObjectActionTravelOriented.cpp @@ -13,7 +13,9 @@ #include -#include "QVariantGLM.h" +#include +#include + #include "PhysicsLogging.h" const uint16_t ObjectActionTravelOriented::actionVersion = 1; diff --git a/libraries/physics/src/ObjectDynamic.h b/libraries/physics/src/ObjectDynamic.h index f41d6cd51d..49fa615b88 100644 --- a/libraries/physics/src/ObjectDynamic.h +++ b/libraries/physics/src/ObjectDynamic.h @@ -17,7 +17,9 @@ #include +#include #include +#include #include "ObjectMotionState.h" #include "BulletUtil.h" diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 0ab051fa96..ad7332cb15 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -198,9 +198,14 @@ void ObjectMotionState::setShape(const btCollisionShape* shape) { getShapeManager()->releaseShape(_shape); } _shape = shape; - if (_body && _type != MOTIONSTATE_TYPE_DETAILED) { + if (_body) { + assert(_shape); + _body->setCollisionShape(const_cast(_shape)); updateCCDConfiguration(); } + } else if (shape) { + // we need to release unused reference to shape + getShapeManager()->releaseShape(shape); } } @@ -285,50 +290,6 @@ void ObjectMotionState::handleEasyChanges(uint32_t& flags) { } } -bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { - assert(_body && _shape); - if (flags & Simulation::DIRTY_SHAPE) { - // make sure the new shape is valid - if (!isReadyToComputeShape()) { - return false; - } - const btCollisionShape* newShape = computeNewShape(); - if (!newShape) { - qCDebug(physics) << "Warning: failed to generate new shape!"; - // failed to generate new shape! --> keep old shape and remove shape-change flag - flags &= ~Simulation::DIRTY_SHAPE; - // TODO: force this object out of PhysicsEngine rather than just use the old shape - if ((flags & HARD_DIRTY_PHYSICS_FLAGS) == 0) { - // no HARD flags remain, so do any EASY changes - if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - handleEasyChanges(flags); - } - return true; - } - } else { - if (_shape == newShape) { - // the shape didn't actually change, so we clear the DIRTY_SHAPE flag - flags &= ~Simulation::DIRTY_SHAPE; - // and clear the reference we just created - getShapeManager()->releaseShape(_shape); - } else { - _body->setCollisionShape(const_cast(newShape)); - setShape(newShape); - } - } - } - if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - handleEasyChanges(flags); - } - // it is possible there are no HARD flags at this point (if DIRTY_SHAPE was removed) - // so we check again before we reinsert: - if (flags & HARD_DIRTY_PHYSICS_FLAGS) { - engine->reinsertObject(this); - } - - return true; -} - void ObjectMotionState::updateBodyMaterialProperties() { _body->setRestitution(getObjectRestitution()); _body->setFriction(getObjectFriction()); diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index fe175a2c7d..415b388e70 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -18,7 +18,7 @@ #include #include -#include +#include #include "ContactInfo.h" #include "ShapeManager.h" @@ -100,7 +100,6 @@ public: virtual ~ObjectMotionState(); virtual void handleEasyChanges(uint32_t& flags); - virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine); void updateBodyMaterialProperties(); void updateBodyVelocities(); @@ -123,11 +122,12 @@ public: glm::vec3 getBodyAngularVelocity() const; virtual glm::vec3 getObjectLinearVelocityChange() const; - virtual uint32_t getIncomingDirtyFlags() = 0; - virtual void clearIncomingDirtyFlags() = 0; + virtual uint32_t getIncomingDirtyFlags() const = 0; + virtual void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) = 0; virtual PhysicsMotionType computePhysicsMotionType() const = 0; + virtual bool needsNewShape() const { return _shape == nullptr || getIncomingDirtyFlags() & Simulation::DIRTY_SHAPE; } const btCollisionShape* getShape() const { return _shape; } btRigidBody* getRigidBody() const { return _body; } @@ -154,6 +154,7 @@ public: virtual void bump(uint8_t priority) {} virtual QString getName() const { return ""; } + virtual ShapeType getShapeType() const = 0; virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const = 0; @@ -172,8 +173,6 @@ public: friend class PhysicsEngine; protected: - virtual bool isReadyToComputeShape() const = 0; - virtual const btCollisionShape* computeNewShape() = 0; virtual void setMotionType(PhysicsMotionType motionType); void updateCCDConfiguration(); @@ -187,7 +186,7 @@ protected: btRigidBody* _body { nullptr }; float _density { 1.0f }; - // ACTION_CAN_CONTROL_KINEMATIC_OBJECT_HACK: These date members allow an Action + // ACTION_CAN_CONTROL_KINEMATIC_OBJECT_HACK: These data members allow an Action // to operate on a kinematic object without screwing up our default kinematic integration // which is done in the MotionState::getWorldTransform(). mutable uint32_t _lastKinematicStep; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 8e5248f6a9..daa2b5d954 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -131,10 +131,10 @@ void PhysicalEntitySimulation::takeDeadAvatarEntities(SetOfEntities& deadEntitie _deadAvatarEntities.clear(); } -void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) { +void PhysicalEntitySimulation::processChangedEntity(const EntityItemPointer& entity) { + EntitySimulation::processChangedEntity(entity); + // queue incoming changes: from external sources (script, EntityServer, etc) to physics engine - QMutexLocker lock(&_mutex); - assert(entity); EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); uint8_t region = _space->getRegion(entity->getSpaceIndex()); bool shouldBePhysical = region < workload::Region::R3 && entity->shouldBePhysical(); @@ -156,7 +156,6 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) { // remove from the physical simulation _incomingChanges.remove(motionState); - _physicalObjects.remove(motionState); removeOwnershipData(motionState); _entitiesToRemoveFromPhysics.insert(entity); if (canBeKinematic && entity->isMovingRelativeToParent()) { @@ -227,44 +226,68 @@ void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity) } // end EntitySimulation overrides -const VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToRemoveFromPhysics() { - QMutexLocker lock(&_mutex); - for (auto entity: _entitiesToRemoveFromPhysics) { - EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); - assert(motionState); - // TODO CLEan this, just a n extra check to avoid the crash that shouldn;t happen - if (motionState) { - _entitiesToAddToPhysics.remove(entity); - if (entity->isDead() && entity->getElement()) { - _deadEntities.insert(entity); +void PhysicalEntitySimulation::buildMotionStatesForEntitiesThatNeedThem() { + // this lambda for when we decide to actually build the motionState + auto buildMotionState = [&](btCollisionShape* shape, EntityItemPointer entity) { + EntityMotionState* motionState = new EntityMotionState(shape, entity); + entity->setPhysicsInfo(static_cast(motionState)); + motionState->setRegion(_space->getRegion(entity->getSpaceIndex())); + _physicalObjects.insert(motionState); + _incomingChanges.insert(motionState); + }; + + uint32_t deliveryCount = ObjectMotionState::getShapeManager()->getWorkDeliveryCount(); + if (deliveryCount != _lastWorkDeliveryCount) { + // new off-thread shapes have arrived --> find adds whose shapes have arrived + _lastWorkDeliveryCount = deliveryCount; + ShapeRequests::iterator requestItr = _shapeRequests.begin(); + while (requestItr != _shapeRequests.end()) { + EntityItemPointer entity = requestItr->entity; + EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (!motionState) { + // this is an ADD because motionState doesn't exist yet + btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShapeByKey(requestItr->shapeHash)); + if (shape) { + // shape is ready at last! + // But the entity's desired shape might have changed since last requested + // --> rebuild the ShapeInfo to verify hash + // TODO? is there a better way to do this? + ShapeInfo shapeInfo; + entity->computeShapeInfo(shapeInfo); + if (shapeInfo.getHash() != requestItr->shapeHash) { + // bummer, the hashes are different and we no longer want the shape we've received + ObjectMotionState::getShapeManager()->releaseShape(shape); + // try again + shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); + if (shape) { + buildMotionState(shape, entity); + requestItr = _shapeRequests.erase(requestItr); + } else { + requestItr->shapeHash = shapeInfo.getHash(); + ++requestItr; + } + } else { + buildMotionState(shape, entity); + requestItr = _shapeRequests.erase(requestItr); + } + } else { + // shape not ready + ++requestItr; + } + } else { + // this is a CHANGE because motionState already exists + if (ObjectMotionState::getShapeManager()->hasShapeWithKey(requestItr->shapeHash)) { + entity->markDirtyFlags(Simulation::DIRTY_SHAPE); + _incomingChanges.insert(motionState); + requestItr = _shapeRequests.erase(requestItr); + } else { + // shape not ready + ++requestItr; + } } - - _incomingChanges.remove(motionState); - removeOwnershipData(motionState); - _physicalObjects.remove(motionState); - - // remember this motionState and delete it later (after removing its RigidBody from the PhysicsEngine) - _objectsToDelete.push_back(motionState); } } - _entitiesToRemoveFromPhysics.clear(); - return _objectsToDelete; -} -void PhysicalEntitySimulation::deleteObjectsRemovedFromPhysics() { - QMutexLocker lock(&_mutex); - for (auto motionState : _objectsToDelete) { - // someday when we invert the entities/physics lib dependencies we can let EntityItem delete its own PhysicsInfo - // until then we must do it here - // NOTE: a reference to the EntityItemPointer is released in the EntityMotionState::dtor - delete motionState; - } - _objectsToDelete.clear(); -} - -void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& result) { - result.clear(); - QMutexLocker lock(&_mutex); SetOfEntities::iterator entityItr = _entitiesToAddToPhysics.begin(); while (entityItr != _entitiesToAddToPhysics.end()) { EntityItemPointer entity = (*entityItr); @@ -273,7 +296,7 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re prepareEntityForDelete(entity); entityItr = _entitiesToAddToPhysics.erase(entityItr); } else if (!entity->shouldBePhysical()) { - // this entity should no longer be on the internal _entitiesToAddToPhysics + // this entity should no longer be on _entitiesToAddToPhysics entityItr = _entitiesToAddToPhysics.erase(entityItr); if (entity->isMovingRelativeToParent()) { SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity); @@ -282,53 +305,149 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re } } } else if (entity->isReadyToComputeShape()) { - ShapeInfo shapeInfo; - entity->computeShapeInfo(shapeInfo); - int numPoints = shapeInfo.getLargestSubshapePointCount(); - if (shapeInfo.getType() == SHAPE_TYPE_COMPOUND) { - if (numPoints > MAX_HULL_POINTS) { - qWarning() << "convex hull with" << numPoints - << "points for entity" << entity->getName() - << "at" << entity->getWorldPosition() << " will be reduced"; + ShapeRequest shapeRequest(entity); + ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest); + if (requestItr == _shapeRequests.end()) { + // not waiting for a shape (yet) + ShapeInfo shapeInfo; + entity->computeShapeInfo(shapeInfo); + uint32_t requestCount = ObjectMotionState::getShapeManager()->getWorkRequestCount(); + btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); + if (shape) { + EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (!motionState) { + buildMotionState(shape, entity); + } else { + // Is it possible to fall in here? + // entity shouldn't be on _entitiesToAddToPhysics list if it already has a motionState. + // but just in case... + motionState->setShape(shape); + motionState->setRegion(_space->getRegion(entity->getSpaceIndex())); + _physicalObjects.insert(motionState); + _incomingChanges.insert(motionState); + } + } else if (requestCount != ObjectMotionState::getShapeManager()->getWorkRequestCount()) { + // shape doesn't exist but a new worker has been spawned to build it --> add to shapeRequests and wait + shapeRequest.shapeHash = shapeInfo.getHash(); + _shapeRequests.insert(shapeRequest); + } else { + // failed to build shape --> will not be added } } - btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); - if (shape) { - EntityMotionState* motionState = new EntityMotionState(shape, entity); - entity->setPhysicsInfo(static_cast(motionState)); - _physicalObjects.insert(motionState); - result.push_back(motionState); - entityItr = _entitiesToAddToPhysics.erase(entityItr); - - // make sure the motionState's region is up-to-date before it is actually added to physics - motionState->setRegion(_space->getRegion(entity->getSpaceIndex())); - } else { - //qWarning() << "Failed to generate new shape for entity." << entity->getName(); - ++entityItr; - } + entityItr = _entitiesToAddToPhysics.erase(entityItr); } else { ++entityItr; } } } -void PhysicalEntitySimulation::setObjectsToChange(const VectorOfMotionStates& objectsToChange) { +void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) { QMutexLocker lock(&_mutex); - for (auto object : objectsToChange) { - _incomingChanges.insert(static_cast(object)); + // entities being removed + for (auto entity : _entitiesToRemoveFromPhysics) { + EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (motionState) { + transaction.objectsToRemove.push_back(motionState); + _incomingChanges.remove(motionState); + } + if (_shapeRequests.size() > 0) { + ShapeRequest shapeRequest(entity); + ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest); + if (requestItr != _shapeRequests.end()) { + _shapeRequests.erase(requestItr); + } + } } -} + _entitiesToRemoveFromPhysics.clear(); -void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result) { - result.clear(); - QMutexLocker lock(&_mutex); - for (auto stateItr : _incomingChanges) { - EntityMotionState* motionState = &(*stateItr); - result.push_back(motionState); + // entities to add + buildMotionStatesForEntitiesThatNeedThem(); + + // motionStates with changed entities: delete, add, or change + for (auto& object : _incomingChanges) { + uint32_t unhandledFlags = object->getIncomingDirtyFlags(); + + uint32_t handledFlags = EASY_DIRTY_PHYSICS_FLAGS; + bool isInPhysicsSimulation = object->isInPhysicsSimulation(); + bool shouldBeInPhysicsSimulation = object->shouldBeInPhysicsSimulation(); + if (!shouldBeInPhysicsSimulation && isInPhysicsSimulation) { + transaction.objectsToRemove.push_back(object); + continue; + } + + bool needsNewShape = object->needsNewShape(); + if (needsNewShape) { + ShapeType shapeType = object->getShapeType(); + if (shapeType == SHAPE_TYPE_STATIC_MESH) { + ShapeRequest shapeRequest(object->_entity); + ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest); + if (requestItr == _shapeRequests.end()) { + ShapeInfo shapeInfo; + object->_entity->computeShapeInfo(shapeInfo); + uint32_t requestCount = ObjectMotionState::getShapeManager()->getWorkRequestCount(); + btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); + if (shape) { + object->setShape(shape); + handledFlags |= Simulation::DIRTY_SHAPE; + needsNewShape = false; + } else if (requestCount != ObjectMotionState::getShapeManager()->getWorkRequestCount()) { + // shape doesn't exist but a new worker has been spawned to build it --> add to shapeRequests and wait + shapeRequest.shapeHash = shapeInfo.getHash(); + _shapeRequests.insert(shapeRequest); + } else { + // failed to build shape --> will not be added/updated + handledFlags |= Simulation::DIRTY_SHAPE; + } + } else { + // continue waiting for shape request + } + } else { + ShapeInfo shapeInfo; + object->_entity->computeShapeInfo(shapeInfo); + btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); + if (shape) { + object->setShape(shape); + handledFlags |= Simulation::DIRTY_SHAPE; + needsNewShape = false; + } else { + // failed to build shape --> will not be added + } + } + } + if (!isInPhysicsSimulation) { + if (needsNewShape) { + // skip it + continue; + } else { + transaction.objectsToAdd.push_back(object); + handledFlags = DIRTY_PHYSICS_FLAGS; + unhandledFlags = 0; + } + } + + if (unhandledFlags & EASY_DIRTY_PHYSICS_FLAGS) { + object->handleEasyChanges(unhandledFlags); + } + if (unhandledFlags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP | (handledFlags & Simulation::DIRTY_SHAPE))) { + transaction.objectsToReinsert.push_back(object); + handledFlags |= HARD_DIRTY_PHYSICS_FLAGS; + } else if (unhandledFlags & Simulation::DIRTY_PHYSICS_ACTIVATION && object->getRigidBody()->isStaticObject()) { + transaction.activeStaticObjects.push_back(object); + } + object->clearIncomingDirtyFlags(handledFlags); } _incomingChanges.clear(); } +void PhysicalEntitySimulation::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) { + // things on objectsToRemove are ready for delete + for (auto object : transaction.objectsToRemove) { + _physicalObjects.remove(object); + delete object; + } + transaction.clear(); +} + void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates) { bool serverlessMode = getEntityTree()->isServerlessMode(); for (auto stateItr : motionStates) { diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 843069e247..817f92cb3c 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -13,6 +13,8 @@ #define hifi_PhysicalEntitySimulation_h #include +#include +#include #include #include @@ -70,7 +72,7 @@ protected: // only called by EntitySimulation virtual void updateEntitiesInternal(uint64_t now) override; virtual void addEntityInternal(EntityItemPointer entity) override; virtual void removeEntityInternal(EntityItemPointer entity) override; - virtual void changeEntityInternal(EntityItemPointer entity) override; + void processChangedEntity(const EntityItemPointer& entity) override; virtual void clearEntitiesInternal() override; void removeOwnershipData(EntityMotionState* motionState); @@ -79,12 +81,8 @@ protected: // only called by EntitySimulation public: virtual void prepareEntityForDelete(EntityItemPointer entity) override; - const VectorOfMotionStates& getObjectsToRemoveFromPhysics(); - void deleteObjectsRemovedFromPhysics(); - - void getObjectsToAddToPhysics(VectorOfMotionStates& result); - void setObjectsToChange(const VectorOfMotionStates& objectsToChange); - void getObjectsToChange(VectorOfMotionStates& result); + void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction); + void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction); void handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates); void handleChangedMotionStates(const VectorOfMotionStates& motionStates); @@ -98,16 +96,25 @@ public: void sendOwnedUpdates(uint32_t numSubsteps); private: - SetOfEntities _entitiesToAddToPhysics; + void buildMotionStatesForEntitiesThatNeedThem(); + + class ShapeRequest { + public: + ShapeRequest() { } + ShapeRequest(const EntityItemPointer& e) : entity(e) { } + bool operator<(const ShapeRequest& other) const { return entity.get() < other.entity.get(); } + bool operator==(const ShapeRequest& other) const { return entity.get() == other.entity.get(); } + EntityItemPointer entity { nullptr }; + mutable uint64_t shapeHash { 0 }; + }; + SetOfEntities _entitiesToAddToPhysics; // we could also call this: _entitiesThatNeedMotionStates SetOfEntities _entitiesToRemoveFromPhysics; - - VectorOfMotionStates _objectsToDelete; - - SetOfEntityMotionStates _incomingChanges; // EntityMotionStates that have changed from external sources - // and need their RigidBodies updated - + SetOfEntityMotionStates _incomingChanges; // EntityMotionStates changed by external events SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine + using ShapeRequests = std::set; + ShapeRequests _shapeRequests; + PhysicsEnginePointer _physicsEngine = nullptr; EntityEditPacketSender* _entityPacketSender = nullptr; @@ -117,6 +124,7 @@ private: workload::SpacePointer _space; uint64_t _nextBidExpiry; uint32_t _lastStepSendPackets { 0 }; + uint32_t _lastWorkDeliveryCount { 0 }; }; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 4453a7d9f0..976c547c5e 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -178,8 +178,6 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { int32_t group, mask; motionState->computeCollisionGroupAndMask(group, mask); _dynamicsWorld->addRigidBody(body, group, mask); - - motionState->clearIncomingDirtyFlags(); } QList PhysicsEngine::removeDynamicsForBody(btRigidBody* body) { @@ -252,6 +250,7 @@ void PhysicsEngine::removeSetOfObjects(const SetOfMotionStates& objects) { } object->clearIncomingDirtyFlags(); } + _activeStaticBodies.clear(); } void PhysicsEngine::addObjects(const VectorOfMotionStates& objects) { @@ -260,35 +259,6 @@ void PhysicsEngine::addObjects(const VectorOfMotionStates& objects) { } } -VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& objects) { - VectorOfMotionStates stillNeedChange; - for (auto object : objects) { - uint32_t flags = object->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS; - if (flags & HARD_DIRTY_PHYSICS_FLAGS) { - if (object->handleHardAndEasyChanges(flags, this)) { - object->clearIncomingDirtyFlags(); - } else { - stillNeedChange.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()); - } - } - // active static bodies 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; - } - return stillNeedChange; -} - void PhysicsEngine::reinsertObject(ObjectMotionState* object) { // remove object from DynamicsWorld bumpAndPruneContacts(object); @@ -320,7 +290,6 @@ void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) body->setMotionState(nullptr); delete body; } - object->clearIncomingDirtyFlags(); } // adds @@ -328,34 +297,16 @@ void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) 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()); - } + // reinserts + for (auto object : transaction.objectsToReinsert) { + reinsertObject(object); } - // 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; + + for (auto object : transaction.activeStaticObjects) { + btRigidBody* body = object->getRigidBody(); + _dynamicsWorld->updateSingleAabb(body); + _activeStaticBodies.insert(body); } - // we replace objectsToChange with any that failed - transaction.objectsToChange.swap(failedChanges); } void PhysicsEngine::removeContacts(ObjectMotionState* motionState) { diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 43cc0d2176..d11b52f1af 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -79,11 +79,13 @@ public: void clear() { objectsToRemove.clear(); objectsToAdd.clear(); - objectsToChange.clear(); + objectsToReinsert.clear(); + activeStaticObjects.clear(); } std::vector objectsToRemove; std::vector objectsToAdd; - std::vector objectsToChange; + std::vector objectsToReinsert; + std::vector activeStaticObjects; }; PhysicsEngine(const glm::vec3& offset); @@ -97,7 +99,7 @@ public: void removeSetOfObjects(const SetOfMotionStates& objects); // only called during teardown void addObjects(const VectorOfMotionStates& objects); - VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects); + void changeObjects(const VectorOfMotionStates& objects); void reinsertObject(ObjectMotionState* object); void processTransaction(Transaction& transaction); diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 5808a539d6..ef5213df8f 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -293,6 +293,8 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) radiuses.push_back(sphereData.w); } shape = new btMultiSphereShape(positions.data(), radiuses.data(), (int)positions.size()); + const float MULTI_SPHERE_MARGIN = 0.001f; + shape->setMargin(MULTI_SPHERE_MARGIN); } break; case SHAPE_TYPE_ELLIPSOID: { @@ -433,6 +435,8 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) } } break; + default: + break; } if (shape) { if (glm::length2(info.getOffset()) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET) { @@ -457,6 +461,8 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = compound; } } + } else { + // TODO: warn about this case } return shape; } @@ -481,3 +487,8 @@ void ShapeFactory::deleteShape(const btCollisionShape* shape) { } delete nonConstShape; } + +void ShapeFactory::Worker::run() { + shape = ShapeFactory::createShapeFromInfo(shapeInfo); + emit submitWork(this); +} diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h index 704a7804b3..d23785d513 100644 --- a/libraries/physics/src/ShapeFactory.h +++ b/libraries/physics/src/ShapeFactory.h @@ -14,6 +14,8 @@ #include #include +#include +#include #include @@ -22,6 +24,17 @@ namespace ShapeFactory { const btCollisionShape* createShapeFromInfo(const ShapeInfo& info); void deleteShape(const btCollisionShape* shape); + + class Worker : public QObject, public QRunnable { + Q_OBJECT + public: + Worker(const ShapeInfo& info) : shapeInfo(info), shape(nullptr) {} + void run() override; + ShapeInfo shapeInfo; + const btCollisionShape* shape; + signals: + void submitWork(Worker*); + }; }; #endif // hifi_ShapeFactory_h diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index 8acbe51540..c37f95b5f1 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -12,15 +12,15 @@ #include "ShapeManager.h" #include +#include -#include - -#include "ShapeFactory.h" +#include const int MAX_RING_SIZE = 256; ShapeManager::ShapeManager() { _garbageRing.reserve(MAX_RING_SIZE); + _nextOrphanExpiry = std::chrono::steady_clock::now(); } ShapeManager::~ShapeManager() { @@ -30,6 +30,10 @@ ShapeManager::~ShapeManager() { ShapeFactory::deleteShape(shapeRef->shape); } _shapeMap.clear(); + if (_deadWorker) { + delete _deadWorker; + _deadWorker = nullptr; + } } const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { @@ -42,17 +46,84 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { shapeRef->refCount++; return shapeRef->shape; } - const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); - if (shape) { - ShapeReference newRef; - newRef.refCount = 1; - newRef.shape = shape; - newRef.key = info.getHash(); - _shapeMap.insert(hashKey, newRef); + const btCollisionShape* shape = nullptr; + if (info.getType() == SHAPE_TYPE_STATIC_MESH) { + uint64_t hash = info.getHash(); + const auto itr = std::find(_pendingMeshShapes.begin(), _pendingMeshShapes.end(), hash); + if (itr == _pendingMeshShapes.end()) { + // start a worker + _pendingMeshShapes.push_back(hash); + ++_workRequestCount; + // try to recycle old deadWorker + ShapeFactory::Worker* worker = _deadWorker; + if (!worker) { + worker = new ShapeFactory::Worker(info); + } else { + worker->shapeInfo = info; + _deadWorker = nullptr; + } + // we will delete worker manually later + worker->setAutoDelete(false); + QObject::connect(worker, &ShapeFactory::Worker::submitWork, this, &ShapeManager::acceptWork); + QThreadPool::globalInstance()->start(worker); + } + // else we're still waiting for the shape to be created on another thread + } else { + shape = ShapeFactory::createShapeFromInfo(info); + if (shape) { + ShapeReference newRef; + newRef.refCount = 1; + newRef.shape = shape; + newRef.key = info.getHash(); + _shapeMap.insert(hashKey, newRef); + } } return shape; } +const btCollisionShape* ShapeManager::getShapeByKey(uint64_t key) { + HashKey hashKey(key); + ShapeReference* shapeRef = _shapeMap.find(hashKey); + if (shapeRef) { + shapeRef->refCount++; + return shapeRef->shape; + } + return nullptr; +} + +bool ShapeManager::hasShapeWithKey(uint64_t key) const { + HashKey hashKey(key); + const ShapeReference* shapeRef = _shapeMap.find(hashKey); + return (bool)shapeRef; +} + +void ShapeManager::addToGarbage(uint64_t key) { + // look for existing entry in _garbageRing + int32_t ringSize = (int32_t)(_garbageRing.size()); + for (int32_t i = 0; i < ringSize; ++i) { + int32_t j = (_ringIndex + ringSize) % ringSize; + if (_garbageRing[j] == key) { + // already on the list, don't add it again + return; + } + } + if (ringSize == MAX_RING_SIZE) { + // remove one + HashKey hashKeyToRemove(_garbageRing[_ringIndex]); + ShapeReference* shapeRef = _shapeMap.find(hashKeyToRemove); + if (shapeRef && shapeRef->refCount == 0) { + ShapeFactory::deleteShape(shapeRef->shape); + _shapeMap.remove(hashKeyToRemove); + } + // replace at _ringIndex and advance + _garbageRing[_ringIndex] = key; + _ringIndex = (_ringIndex + 1) % ringSize; + } else { + // add one + _garbageRing.push_back(key); + } +} + // private helper method bool ShapeManager::releaseShapeByKey(uint64_t key) { HashKey hashKey(key); @@ -61,30 +132,7 @@ bool ShapeManager::releaseShapeByKey(uint64_t key) { if (shapeRef->refCount > 0) { shapeRef->refCount--; if (shapeRef->refCount == 0) { - // look for existing entry in _garbageRing - int32_t ringSize = (int32_t)(_garbageRing.size()); - for (int32_t i = 0; i < ringSize; ++i) { - int32_t j = (_ringIndex + ringSize) % ringSize; - if (_garbageRing[j] == key) { - // already on the list, don't add it again - return true; - } - } - if (ringSize == MAX_RING_SIZE) { - // remove one - HashKey hashKeyToRemove(_garbageRing[_ringIndex]); - ShapeReference* shapeRef = _shapeMap.find(hashKeyToRemove); - if (shapeRef && shapeRef->refCount == 0) { - ShapeFactory::deleteShape(shapeRef->shape); - _shapeMap.remove(hashKeyToRemove); - } - // replace at _ringIndex and advance - _garbageRing[_ringIndex] = key; - _ringIndex = (_ringIndex + 1) % ringSize; - } else { - // add one - _garbageRing.push_back(key); - } + addToGarbage(key); } return true; } else { @@ -153,3 +201,78 @@ bool ShapeManager::hasShape(const btCollisionShape* shape) const { } return false; } + +// slot: called when ShapeFactory::Worker is done building shape +void ShapeManager::acceptWork(ShapeFactory::Worker* worker) { + auto itr = std::find(_pendingMeshShapes.begin(), _pendingMeshShapes.end(), worker->shapeInfo.getHash()); + if (itr == _pendingMeshShapes.end()) { + // we've received a shape but don't remember asking for it + // (should not fall in here, but if we do: delete the unwanted shape) + if (worker->shape) { + ShapeFactory::deleteShape(worker->shape); + } + } else { + // clear pending status + *itr = _pendingMeshShapes.back(); + _pendingMeshShapes.pop_back(); + + // cache the new shape + if (worker->shape) { + ShapeReference newRef; + // refCount is zero because nothing is using the shape yet + newRef.refCount = 0; + newRef.shape = worker->shape; + newRef.key = worker->shapeInfo.getHash(); + HashKey hashKey(newRef.key); + _shapeMap.insert(hashKey, newRef); + + // This shape's refCount is zero because an object requested it but is not yet using it. We expect it to be + // used later but there is a possibility it will never be used (e.g. the object that wanted it was removed + // before the shape could be added, or has changed its mind and now wants a different shape). + // Normally zero refCount shapes belong on _garbageRing for possible cleanup but we don't want to add it there + // because it might get reaped too soon. So we add it to _orphans to check later. If it still has zero + // refCount on expiry we will move it to _garbageRing. + const int64_t SHAPE_EXPIRY = USECS_PER_SECOND; + auto now = std::chrono::steady_clock::now(); + auto newExpiry = now + std::chrono::microseconds(SHAPE_EXPIRY); + if (_nextOrphanExpiry < now) { + _nextOrphanExpiry = newExpiry; + // check for expired orphan shapes + size_t i = 0; + while (i < _orphans.size()) { + auto expiry = _orphans[i].expiry; + if (expiry < now) { + uint64_t key = _orphans[i].key; + HashKey hashKey(key); + ShapeReference* shapeRef = _shapeMap.find(hashKey); + if (shapeRef) { + if (shapeRef->refCount == 0) { + // shape unused after expiry + addToGarbage(key); + } + } + _orphans[i] = _orphans.back(); + _orphans.pop_back(); + } else { + if (expiry < _nextOrphanExpiry) { + _nextOrphanExpiry = expiry; + } + ++i; + } + } + } + _orphans.push_back(KeyExpiry(newRef.key, newExpiry)); + } + } + disconnect(worker, &ShapeFactory::Worker::submitWork, this, &ShapeManager::acceptWork); + + if (_deadWorker) { + // delete the previous deadWorker manually + delete _deadWorker; + } + // save this dead worker for later + worker->shapeInfo.clear(); + worker->shape = nullptr; + _deadWorker = worker; + ++_workDeliveryCount; +} diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index c1fb57e017..1f39fd56be 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -12,13 +12,17 @@ #ifndef hifi_ShapeManager_h #define hifi_ShapeManager_h +#include +#include #include +#include #include #include #include +#include "ShapeFactory.h" #include "HashKey.h" // The ShapeManager handles the ref-counting on shared shapes: @@ -44,7 +48,8 @@ // entries that still have zero ref-count. -class ShapeManager { +class ShapeManager : public QObject { + Q_OBJECT public: ShapeManager(); @@ -52,6 +57,8 @@ public: /// \return pointer to shape const btCollisionShape* getShape(const ShapeInfo& info); + const btCollisionShape* getShapeByKey(uint64_t key); + bool hasShapeWithKey(uint64_t key) const; /// \return true if shape was found and released bool releaseShape(const btCollisionShape* shape); @@ -64,8 +71,14 @@ public: int getNumReferences(const ShapeInfo& info) const; int getNumReferences(const btCollisionShape* shape) const; bool hasShape(const btCollisionShape* shape) const; + uint32_t getWorkRequestCount() const { return _workRequestCount; } + uint32_t getWorkDeliveryCount() const { return _workDeliveryCount; } + +protected slots: + void acceptWork(ShapeFactory::Worker* worker); private: + void addToGarbage(uint64_t key); bool releaseShapeByKey(uint64_t key); class ShapeReference { @@ -76,10 +89,24 @@ private: ShapeReference() : refCount(0), shape(nullptr) {} }; + using TimePoint = std::chrono::time_point; + class KeyExpiry { + public: + KeyExpiry(uint64_t k, std::chrono::time_point e) : expiry(e), key(k) {} + TimePoint expiry; + uint64_t key; + }; + // btHashMap is required because it supports memory alignment of the btCollisionShapes btHashMap _shapeMap; std::vector _garbageRing; + std::vector _pendingMeshShapes; + std::vector _orphans; + ShapeFactory::Worker* _deadWorker { nullptr }; + TimePoint _nextOrphanExpiry; uint32_t _ringIndex { 0 }; + std::atomic_uint _workRequestCount { 0 }; + std::atomic_uint _workDeliveryCount { 0 }; }; #endif // hifi_ShapeManager_h diff --git a/libraries/shared/src/HashKey.cpp b/libraries/shared/src/HashKey.cpp index 488eccb1bf..8c5303028b 100644 --- a/libraries/shared/src/HashKey.cpp +++ b/libraries/shared/src/HashKey.cpp @@ -51,15 +51,15 @@ float HashKey::getNumQuantizedValuesPerMeter() { return QUANTIZED_VALUES_PER_METER; } -void HashKey::hashUint64(uint64_t data) { +void HashKey::Hasher::hashUint64(uint64_t data) { _hash += squirrel3_64(data, ++_hashCount); } -void HashKey::hashFloat(float data) { +void HashKey::Hasher::hashFloat(float data) { _hash += squirrel3_64((uint64_t)((int64_t)(data * QUANTIZED_VALUES_PER_METER)), ++_hashCount); } -void HashKey::hashVec3(const glm::vec3& data) { +void HashKey::Hasher::hashVec3(const glm::vec3& data) { _hash += squirrel3_64((uint64_t)((int64_t)(data[0] * QUANTIZED_VALUES_PER_METER)), ++_hashCount); _hash *= squirrel3_64((uint64_t)((int64_t)(data[1] * QUANTIZED_VALUES_PER_METER)), ++_hashCount); _hash ^= squirrel3_64((uint64_t)((int64_t)(data[2] * QUANTIZED_VALUES_PER_METER)), ++_hashCount); diff --git a/libraries/shared/src/HashKey.h b/libraries/shared/src/HashKey.h index 446eb4c25f..071b162a50 100644 --- a/libraries/shared/src/HashKey.h +++ b/libraries/shared/src/HashKey.h @@ -30,6 +30,18 @@ class HashKey { public: + class Hasher { + public: + Hasher() {} + void hashUint64(uint64_t data); + void hashFloat(float data); + void hashVec3(const glm::vec3& data); + uint64_t getHash64() const { return _hash; } + private: + uint64_t _hash { 0 }; + uint8_t _hashCount { 0 }; + }; + static float getNumQuantizedValuesPerMeter(); HashKey() {} @@ -39,16 +51,9 @@ public: bool equals(const HashKey& other) const { return _hash == other._hash; } int32_t getHash() const { return (int32_t)((uint32_t)_hash); } - // These methods for accumulating a hash. - void hashUint64(uint64_t data); - void hashFloat(float data); - void hashVec3(const glm::vec3& data); - uint64_t getHash64() const { return _hash; } - private: uint64_t _hash { 0 }; - uint8_t _hashCount { 0 }; }; #endif // hifi_HashKey_h diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index bf51e455c5..1b9054c4d6 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -273,18 +273,18 @@ float ShapeInfo::computeVolume() const { uint64_t ShapeInfo::getHash() const { // NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance. if (_hash64 == 0 && _type != SHAPE_TYPE_NONE) { - HashKey hashKey; + HashKey::Hasher hasher; // The key is not yet cached therefore we must compute it. - hashKey.hashUint64((uint64_t)_type); + hasher.hashUint64((uint64_t)_type); if (_type == SHAPE_TYPE_MULTISPHERE) { for (auto &sphereData : _sphereCollection) { - hashKey.hashVec3(glm::vec3(sphereData)); - hashKey.hashFloat(sphereData.w); + hasher.hashVec3(glm::vec3(sphereData)); + hasher.hashFloat(sphereData.w); } } else if (_type != SHAPE_TYPE_SIMPLE_HULL) { - hashKey.hashVec3(_halfExtents); - hashKey.hashVec3(_offset); + hasher.hashVec3(_halfExtents); + hasher.hashVec3(_offset); } else { // TODO: we could avoid hashing all of these points if we were to supply the ShapeInfo with a unique // descriptive string. Shapes that are uniquely described by their type and URL could just put their @@ -294,7 +294,7 @@ uint64_t ShapeInfo::getHash() const { const int numPoints = (int)points.size(); for (int i = 0; i < numPoints; ++i) { - hashKey.hashVec3(points[i]); + hasher.hashVec3(points[i]); } } @@ -302,19 +302,19 @@ uint64_t ShapeInfo::getHash() const { if (!url.isEmpty()) { QByteArray baUrl = url.toLocal8Bit(); uint32_t urlHash = qChecksum(baUrl.data(), baUrl.size()); - hashKey.hashUint64((uint64_t)urlHash); + hasher.hashUint64((uint64_t)urlHash); } if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_SIMPLE_COMPOUND) { uint64_t numHulls = (uint64_t)_pointCollection.size(); - hashKey.hashUint64(numHulls); + hasher.hashUint64(numHulls); } else if (_type == SHAPE_TYPE_MULTISPHERE) { uint64_t numSpheres = (uint64_t)_sphereCollection.size(); - hashKey.hashUint64(numSpheres); + hasher.hashUint64(numSpheres); } else if (_type == SHAPE_TYPE_SIMPLE_HULL) { - hashKey.hashUint64(1); + hasher.hashUint64(1); } - _hash64 = hashKey.getHash64(); + _hash64 = hasher.getHash64(); } return _hash64; } diff --git a/libraries/entities/src/SimulationFlags.h b/libraries/shared/src/SimulationFlags.h similarity index 68% rename from libraries/entities/src/SimulationFlags.h rename to libraries/shared/src/SimulationFlags.h index c45b333b29..9dbb719af2 100644 --- a/libraries/entities/src/SimulationFlags.h +++ b/libraries/shared/src/SimulationFlags.h @@ -12,6 +12,17 @@ #ifndef hifi_SimulationFlags_h #define hifi_SimulationFlags_h +const uint8_t YIELD_SIMULATION_PRIORITY = 1; +const uint8_t VOLUNTEER_SIMULATION_PRIORITY = YIELD_SIMULATION_PRIORITY + 1; +const uint8_t RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1; + +const uint8_t SCRIPT_GRAB_SIMULATION_PRIORITY = 128; +const uint8_t SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY - 1; + +const uint8_t PERSONAL_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY; +const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = PERSONAL_SIMULATION_PRIORITY; + + namespace Simulation { const uint32_t DIRTY_POSITION = 0x0001; const uint32_t DIRTY_ROTATION = 0x0002; @@ -29,13 +40,14 @@ namespace Simulation { const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed // bits 17-32 are reservied for special flags - const uint32_t SPECIAL_FLAGS_NO_BOOTSTRAPPING = 0x10000; + const uint32_t SPECIAL_FLAG_NO_BOOTSTRAPPING = 0x10000; + const uint32_t SPECIAL_FLAG_IN_PHYSICS_SIMULATION = 0x20000; const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION; const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY; - const uint32_t SPECIAL_FLAGS = SPECIAL_FLAGS_NO_BOOTSTRAPPING; + const uint32_t SPECIAL_FLAGS_MASK = SPECIAL_FLAG_NO_BOOTSTRAPPING | SPECIAL_FLAG_IN_PHYSICS_SIMULATION; - const uint32_t DIRTY_FLAGS = DIRTY_POSITION | + const uint32_t DIRTY_FLAGS_MASK = DIRTY_POSITION | DIRTY_ROTATION | DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY | diff --git a/libraries/workload/src/workload/View.cpp b/libraries/workload/src/workload/View.cpp index 8cb593d2a7..a11b1890fd 100644 --- a/libraries/workload/src/workload/View.cpp +++ b/libraries/workload/src/workload/View.cpp @@ -37,8 +37,8 @@ View View::evalFromFrustum(const ViewFrustum& frustum, const glm::vec3& offset) Sphere View::evalRegionSphere(const View& view, float originRadius, float maxDistance) { float radius = (maxDistance + originRadius) / 2.0f; - float center = radius - originRadius; - return Sphere(view.origin + view.direction * center, radius); + float distanceToCenter = radius - originRadius; + return Sphere(view.origin + view.direction * distanceToCenter, radius); } void View::updateRegionsDefault(View& view) {