From 7fb7e503f9e66a426056bc7657f5e1163efdacd0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 17 Apr 2019 14:43:49 -0700 Subject: [PATCH 01/29] reduce footprint of ShapeManager::_shapeMap --- libraries/shared/src/HashKey.cpp | 6 +++--- libraries/shared/src/HashKey.h | 19 ++++++++++++------- libraries/shared/src/ShapeInfo.cpp | 24 ++++++++++++------------ 3 files changed, 27 insertions(+), 22 deletions(-) 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; } From fbd4db55057caf622b1db101c354a4bc272d5932 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 17 Apr 2019 14:48:24 -0700 Subject: [PATCH 02/29] build StaticMeshShapes outside of mainloop --- libraries/physics/src/ShapeFactory.cpp | 5 ++ libraries/physics/src/ShapeFactory.h | 13 +++++ libraries/physics/src/ShapeManager.cpp | 77 ++++++++++++++++++++++---- libraries/physics/src/ShapeManager.h | 10 +++- 4 files changed, 94 insertions(+), 11 deletions(-) diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 5808a539d6..d988e88bbe 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -481,3 +481,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..00af50813e 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -12,10 +12,8 @@ #include "ShapeManager.h" #include +#include -#include - -#include "ShapeFactory.h" const int MAX_RING_SIZE = 256; @@ -42,13 +40,36 @@ 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); + // 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; } @@ -153,3 +174,39 @@ 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; + newRef.refCount = 1; + newRef.shape = worker->shape; + newRef.key = worker->shapeInfo.getHash(); + HashKey hashKey(newRef.key); + _shapeMap.insert(hashKey, newRef); + } + } + 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; +} diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index c1fb57e017..898d1b0f37 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -14,11 +14,13 @@ #include +#include #include #include #include +#include "ShapeFactory.h" #include "HashKey.h" // The ShapeManager handles the ref-counting on shared shapes: @@ -44,7 +46,8 @@ // entries that still have zero ref-count. -class ShapeManager { +class ShapeManager : public QObject { + Q_OBJECT public: ShapeManager(); @@ -65,6 +68,9 @@ public: int getNumReferences(const btCollisionShape* shape) const; bool hasShape(const btCollisionShape* shape) const; +protected slots: + void acceptWork(ShapeFactory::Worker* worker); + private: bool releaseShapeByKey(uint64_t key); @@ -79,6 +85,8 @@ private: // btHashMap is required because it supports memory alignment of the btCollisionShapes btHashMap _shapeMap; std::vector _garbageRing; + std::vector _pendingMeshShapes; + ShapeFactory::Worker* _deadWorker { nullptr }; uint32_t _ringIndex { 0 }; }; From 514d598797a4dda0a2d4de4d49e665ff9e3357e7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 17 Apr 2019 14:53:08 -0700 Subject: [PATCH 03/29] remove unhelpful profiling with wrong category --- libraries/entities/src/EntityTree.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b3dd78ae92..688f10cdee 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; { From 8445eaf310706030733837a5ea984aa01025125a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 17 Apr 2019 14:54:29 -0700 Subject: [PATCH 04/29] protect against orphaned off-thread-assembled shapes --- libraries/physics/src/ShapeManager.cpp | 90 +++++++++++++++++++------- libraries/physics/src/ShapeManager.h | 11 ++++ 2 files changed, 76 insertions(+), 25 deletions(-) diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index 00af50813e..1de92f67a4 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -14,11 +14,13 @@ #include #include +#include const int MAX_RING_SIZE = 256; ShapeManager::ShapeManager() { _garbageRing.reserve(MAX_RING_SIZE); + _nextOrphanExpiry = std::chrono::steady_clock::now(); } ShapeManager::~ShapeManager() { @@ -74,6 +76,33 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { return shape; } +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); @@ -82,30 +111,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 { @@ -192,11 +198,45 @@ void ShapeManager::acceptWork(ShapeFactory::Worker* worker) { // cache the new shape if (worker->shape) { ShapeReference newRef; - newRef.refCount = 1; + // 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 expiry = now + std::chrono::microseconds(SHAPE_EXPIRY); + if (_nextOrphanExpiry < now) { + // check for expired orphan shapes + size_t i = 0; + while (i < _orphans.size()) { + if (_orphans[i].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 { + ++i; + } + } + } + _nextOrphanExpiry = expiry; + _orphans.push_back(KeyExpiry(newRef.key, expiry)); } } disconnect(worker, &ShapeFactory::Worker::submitWork, this, &ShapeManager::acceptWork); diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index 898d1b0f37..f3423a5ac6 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -72,6 +72,7 @@ protected slots: void acceptWork(ShapeFactory::Worker* worker); private: + void addToGarbage(uint64_t key); bool releaseShapeByKey(uint64_t key); class ShapeReference { @@ -82,11 +83,21 @@ 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 }; }; From 7dfa8a26a3ec7c3200a67d4986acb64dda017b60 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 17 Apr 2019 14:58:38 -0700 Subject: [PATCH 05/29] cleanup EntityItem::shouldBePhysical() and derivations --- .../src/RenderableModelEntityItem.cpp | 14 ++++++++------ .../src/RenderablePolyVoxEntityItem.h | 1 - libraries/entities/src/EntityItem.h | 2 +- libraries/entities/src/ModelEntityItem.cpp | 5 ----- libraries/entities/src/ModelEntityItem.h | 2 -- libraries/entities/src/PolyVoxEntityItem.h | 1 - libraries/entities/src/ShapeEntityItem.h | 2 -- 7 files changed, 9 insertions(+), 18 deletions(-) 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.h b/libraries/entities/src/EntityItem.h index 29a1a8d73c..dc3c467ca1 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -322,7 +322,7 @@ public: bool getDynamic() const; void setDynamic(bool value); - virtual bool shouldBePhysical() const { return false; } + virtual bool shouldBePhysical() const { return !isDead(); } bool isVisuallyReady() const { return _visuallyReady; } bool getLocked() const; 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/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, From deee1598923eca51e78d9ca3613e2ae8f06b095c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 19 Apr 2019 08:37:39 -0700 Subject: [PATCH 06/29] unravel physics-vs-entities lib dependencies part 1 --- libraries/entities/src/EntityItem.h | 3 ++- libraries/entities/src/SimulationOwner.h | 3 +++ libraries/physics/src/EntityMotionState.h | 1 + libraries/physics/src/ObjectActionTractor.cpp | 3 ++- libraries/physics/src/ObjectActionTravelOriented.cpp | 4 +++- libraries/physics/src/ObjectDynamic.h | 2 ++ libraries/physics/src/ObjectMotionState.h | 2 +- libraries/{entities => shared}/src/SimulationFlags.h | 11 +++++++++++ 8 files changed, 25 insertions(+), 4 deletions(-) rename libraries/{entities => shared}/src/SimulationFlags.h (79%) diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index dc3c467ca1..303b12cffd 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; 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/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index ddf384dc77..6e1ea66685 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 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.h b/libraries/physics/src/ObjectMotionState.h index fe175a2c7d..a53215753e 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" diff --git a/libraries/entities/src/SimulationFlags.h b/libraries/shared/src/SimulationFlags.h similarity index 79% rename from libraries/entities/src/SimulationFlags.h rename to libraries/shared/src/SimulationFlags.h index c45b333b29..af8c0e5fce 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; From 3c8c68d18787f6f40a34e0ccf4ab5296cb7aaf21 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 23 Apr 2019 13:51:55 -0700 Subject: [PATCH 07/29] track EntityItem adds to physcis with pending shape --- libraries/entities/src/EntityItem.h | 2 +- .../physics/src/PhysicalEntitySimulation.cpp | 122 +++++++++++++----- .../physics/src/PhysicalEntitySimulation.h | 14 ++ libraries/physics/src/ShapeFactory.cpp | 2 + libraries/physics/src/ShapeManager.cpp | 18 +++ libraries/physics/src/ShapeManager.h | 7 + 6 files changed, 133 insertions(+), 32 deletions(-) diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 303b12cffd..b99536f4dc 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -323,7 +323,7 @@ public: bool getDynamic() const; void setDynamic(bool value); - virtual bool shouldBePhysical() const { return !isDead(); } + virtual bool shouldBePhysical() const { return !isDead() && getShapeType() != SHAPE_TYPE_NONE; } bool isVisuallyReady() const { return _visuallyReady; } bool getLocked() const; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 8e5248f6a9..515a99929c 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -230,19 +230,16 @@ void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity) 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); - } + _entitiesToAddToPhysics.remove(entity); + if (entity->isDead() && entity->getElement()) { + _deadEntities.insert(entity); + } + EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (motionState) { _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); } @@ -264,7 +261,75 @@ void PhysicalEntitySimulation::deleteObjectsRemovedFromPhysics() { void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& result) { result.clear(); + + // 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); + result.push_back(motionState); + }; + + // TODO: + // (1) make all changes to MotionState in "managers" (PhysicalEntitySimulation and AvatarManager) + // (2) store relevant change-flags on MotionState (maybe just EASY or HARD?) + // (3) remove knowledge of PhysicsEngine from ObjectMotionState + // (4) instead PhysicsEngine gets list of changed MotionStates, reads change-flags and applies changes accordingly + QMutexLocker lock(&_mutex); + 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)) { + // TODO? reset DIRTY_SHAPE flag? + _incomingChanges.insert(motionState); + requestItr = _shapeRequests.erase(requestItr); + } else { + // shape not ready + ++requestItr; + } + } + } + } + SetOfEntities::iterator entityItr = _entitiesToAddToPhysics.begin(); while (entityItr != _entitiesToAddToPhysics.end()) { EntityItemPointer entity = (*entityItr); @@ -282,30 +347,25 @@ 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"; + // check to see if we're waiting for a shape + ShapeRequest shapeRequest(entity); + ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest); + if (requestItr == _shapeRequests.end()) { + ShapeInfo shapeInfo; + entity->computeShapeInfo(shapeInfo); + uint32_t requestCount = ObjectMotionState::getShapeManager()->getWorkRequestCount(); + btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); + if (shape) { + buildMotionState(shape, entity); + } 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; } diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 843069e247..65a2b8f90d 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -13,6 +13,7 @@ #define hifi_PhysicalEntitySimulation_h #include +#include #include #include @@ -98,6 +99,15 @@ public: void sendOwnedUpdates(uint32_t numSubsteps); private: + class ShapeRequest { + public: + ShapeRequest() : entity(), shapeHash(0) {} + ShapeRequest(const EntityItemPointer& e) : entity(e), shapeHash(0) {} + 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; + mutable uint64_t shapeHash; + }; SetOfEntities _entitiesToAddToPhysics; SetOfEntities _entitiesToRemoveFromPhysics; @@ -108,6 +118,9 @@ private: SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine + using ShapeRequests = std::set; + ShapeRequests _shapeRequests; + PhysicsEnginePointer _physicsEngine = nullptr; EntityEditPacketSender* _entityPacketSender = nullptr; @@ -117,6 +130,7 @@ private: workload::SpacePointer _space; uint64_t _nextBidExpiry; uint32_t _lastStepSendPackets { 0 }; + uint32_t _lastWorkDeliveryCount { 0 }; }; diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index d988e88bbe..9e0103e859 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -433,6 +433,8 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) } } break; + default: + break; } if (shape) { if (glm::length2(info.getOffset()) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET) { diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index 1de92f67a4..85587a6c67 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -49,6 +49,7 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { if (itr == _pendingMeshShapes.end()) { // start a worker _pendingMeshShapes.push_back(hash); + ++_workRequestCount; // try to recycle old deadWorker ShapeFactory::Worker* worker = _deadWorker; if (!worker) { @@ -76,6 +77,22 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { 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()); @@ -249,4 +266,5 @@ void ShapeManager::acceptWork(ShapeFactory::Worker* worker) { worker->shapeInfo.clear(); worker->shape = nullptr; _deadWorker = worker; + ++_workDeliveryCount; } diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index f3423a5ac6..b07999ec64 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -12,6 +12,7 @@ #ifndef hifi_ShapeManager_h #define hifi_ShapeManager_h +#include #include #include @@ -55,6 +56,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); @@ -67,6 +70,8 @@ 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); @@ -99,6 +104,8 @@ private: ShapeFactory::Worker* _deadWorker { nullptr }; TimePoint _nextOrphanExpiry; uint32_t _ringIndex { 0 }; + std::atomic_uint32_t _workRequestCount { 0 }; + std::atomic_uint32_t _workDeliveryCount { 0 }; }; #endif // hifi_ShapeManager_h From 3eed8218ca556b37f25e34b1d7c3105395898a8a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 1 May 2019 11:41:40 -0700 Subject: [PATCH 08/29] overhaul of MotionState and shape creations --- interface/src/Application.cpp | 44 +----- interface/src/avatar/AvatarManager.cpp | 137 ++++++++++++------ interface/src/avatar/AvatarManager.h | 4 +- interface/src/avatar/AvatarMotionState.cpp | 13 +- interface/src/avatar/AvatarMotionState.h | 49 +++---- interface/src/avatar/DetailedMotionState.cpp | 27 +--- interface/src/avatar/DetailedMotionState.h | 59 ++++---- .../src/avatar/MyCharacterController.cpp | 26 +--- interface/src/avatar/MyCharacterController.h | 10 +- interface/src/avatar/OtherAvatar.cpp | 68 ++------- interface/src/avatar/OtherAvatar.h | 4 +- libraries/physics/src/EntityMotionState.cpp | 55 +------ libraries/physics/src/EntityMotionState.h | 12 +- libraries/physics/src/ObjectMotionState.cpp | 47 +----- libraries/physics/src/ObjectMotionState.h | 9 +- .../physics/src/PhysicalEntitySimulation.cpp | 137 ++++++++++++------ .../physics/src/PhysicalEntitySimulation.h | 27 ++-- libraries/physics/src/PhysicsEngine.cpp | 64 +------- libraries/physics/src/PhysicsEngine.h | 8 +- libraries/physics/src/ShapeFactory.cpp | 4 + 20 files changed, 318 insertions(+), 486 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d84c3e8b85..4b10c23628 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2782,21 +2782,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 @@ -6412,32 +6406,11 @@ void Application::update(float deltaTime) { 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, "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); - }); + PROFILE_RANGE(simulation_physics, "Entities"); + PhysicsEngine::Transaction transaction; + _entitySimulation->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + _entitySimulation->handleProcessedPhysicsTransaction(transaction); } _entitySimulation->applyDynamicChanges(); @@ -6450,9 +6423,6 @@ void Application::update(float deltaTime) { 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()); } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 00e743312f..8537217756 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() { @@ -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,112 @@ 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) { + //std::shared_ptr avatar = shared_from_this(); + //std::shared_ptr avatar = getThisPointer(); + 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); + detailedMotionStates.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); + } + } + } + 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; + if (motionState->needsNewShape()) { + rebuildAvatarPhysics(transaction, avatar); + } else { + uint32_t flags = motionState->getIncomingDirtyFlags(); + motionState->clearIncomingDirtyFlags(); + if (flags | EASY_DIRTY_PHYSICS_FLAGS) { + motionState->handleEasyChanges(flags); + } + // NOTE: we don't call detailedMotionState->handleEasyChanges() here is because they are KINEMATIC + // and Bullet will automagically call DetailedMotionState::getWorldTransform() on all that are active. + + if (flags | (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { + transaction.objectsToReinsert.push_back(motionState); + for (auto detailedMotionState : avatar->getDetailedMotionStates()) { + transaction.objectsToReinsert.push_back(detailedMotionState); + } } } - } } - _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 +613,7 @@ void AvatarManager::clearOtherAvatars() { ++avatarIterator; } } - } + } for (auto& av : removedAvatars) { handleRemovedAvatar(av); @@ -578,7 +621,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 +631,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..ae229dc66f 100755 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -29,17 +29,13 @@ 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; } @@ -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..5f26b5114d 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() 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..4cfbbf032c 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,17 +33,13 @@ 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; } @@ -54,26 +50,9 @@ void DetailedMotionState::clearIncomingDirtyFlags() { } 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; @@ -185,4 +164,4 @@ 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..95b0600cf9 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() 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/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 6abe5c3899..24ce17d6fb 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(); @@ -677,7 +631,6 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() { } void EntityMotionState::clearIncomingDirtyFlags() { - assert(entityTreeIsLocked()); if (_body && _entity) { _entity->clearDirtyFlags(DIRTY_PHYSICS_FLAGS); } @@ -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(); } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 6e1ea66685..0d5a97eeb8 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -39,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; @@ -56,7 +55,7 @@ public: void sendBid(OctreeEditPacketSender* packetSender, uint32_t step); void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step); - virtual uint32_t getIncomingDirtyFlags() override; + virtual uint32_t getIncomingDirtyFlags() const override; virtual void clearIncomingDirtyFlags() override; virtual float getObjectRestitution() const override { return _entity->getRestitution(); } @@ -85,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; @@ -113,12 +113,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/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 0ab051fa96..92482437e2 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -201,6 +201,9 @@ void ObjectMotionState::setShape(const btCollisionShape* shape) { if (_body && _type != MOTIONSTATE_TYPE_DETAILED) { updateCCDConfiguration(); } + } else if (shape) { + // we need to release unused reference to shape + getShapeManager()->releaseShape(shape); } } @@ -285,50 +288,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 a53215753e..7d204194b2 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.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 uint32_t getIncomingDirtyFlags() const = 0; virtual void clearIncomingDirtyFlags() = 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 515a99929c..10b5532cd8 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -227,56 +227,16 @@ void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity) } // end EntitySimulation overrides -const VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToRemoveFromPhysics() { - QMutexLocker lock(&_mutex); - for (auto entity: _entitiesToRemoveFromPhysics) { - _entitiesToAddToPhysics.remove(entity); - if (entity->isDead() && entity->getElement()) { - _deadEntities.insert(entity); - } - - EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); - if (motionState) { - _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(); - +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); - result.push_back(motionState); + _incomingChanges.insert(motionState); }; - // TODO: - // (1) make all changes to MotionState in "managers" (PhysicalEntitySimulation and AvatarManager) - // (2) store relevant change-flags on MotionState (maybe just EASY or HARD?) - // (3) remove knowledge of PhysicsEngine from ObjectMotionState - // (4) instead PhysicsEngine gets list of changed MotionStates, reads change-flags and applies changes accordingly - QMutexLocker lock(&_mutex); uint32_t deliveryCount = ObjectMotionState::getShapeManager()->getWorkDeliveryCount(); if (deliveryCount != _lastWorkDeliveryCount) { @@ -319,7 +279,7 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re } else { // this is a CHANGE because motionState already exists if (ObjectMotionState::getShapeManager()->hasShapeWithKey(requestItr->shapeHash)) { - // TODO? reset DIRTY_SHAPE flag? + entity->markDirtyFlags(Simulation::DIRTY_SHAPE); _incomingChanges.insert(motionState); requestItr = _shapeRequests.erase(requestItr); } else { @@ -338,7 +298,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); @@ -347,16 +307,27 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re } } } else if (entity->isReadyToComputeShape()) { - // check to see if we're waiting for a shape 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) { - buildMotionState(shape, entity); + 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(); @@ -389,6 +360,80 @@ void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result) _incomingChanges.clear(); } +void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) { + buildMotionStatesForEntitiesThatNeedThem(); + for (auto& object : _incomingChanges) { + uint32_t flags = object->getIncomingDirtyFlags(); + object->clearIncomingDirtyFlags(); + + bool isInPhysicsSimulation = object->isInPhysicsSimulation(); + if (isInPhysicsSimulation != object->shouldBeInPhysicsSimulation()) { + if (isInPhysicsSimulation) { + transaction.objectsToRemove.push_back(object); + continue; + } else { + transaction.objectsToAdd.push_back(object); + } + } + + bool reinsert = false; + if (object->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); + reinsert = true; + } 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 + } + } 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); + reinsert = true; + } else { + // failed to build shape --> will not be added + } + } + } + if (!isInPhysicsSimulation) { + continue; + } + if (flags | EASY_DIRTY_PHYSICS_FLAGS) { + object->handleEasyChanges(flags); + } + if (flags | (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP) || reinsert) { + transaction.objectsToReinsert.push_back(object); + } else if (flags & Simulation::DIRTY_PHYSICS_ACTIVATION && object->getRigidBody()->isStaticObject()) { + transaction.activeStaticObjects.push_back(object); + } + } +} + +void PhysicalEntitySimulation::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) { + // things on objectsToRemove are ready for delete + for (auto object : transaction.objectsToRemove) { + 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 65a2b8f90d..d7d8adffac 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -80,13 +81,12 @@ 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); void handleCollisionEvents(const CollisionEvents& collisionEvents); @@ -99,23 +99,20 @@ public: void sendOwnedUpdates(uint32_t numSubsteps); private: + void buildMotionStatesForEntitiesThatNeedThem(); + class ShapeRequest { public: - ShapeRequest() : entity(), shapeHash(0) {} - ShapeRequest(const EntityItemPointer& e) : entity(e), shapeHash(0) {} + 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; - mutable uint64_t shapeHash; + EntityItemPointer entity { nullptr }; + mutable uint64_t shapeHash { 0 }; }; - SetOfEntities _entitiesToAddToPhysics; + 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; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 4453a7d9f0..bae9ef2485 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -260,35 +260,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 +291,6 @@ void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) body->setMotionState(nullptr); delete body; } - object->clearIncomingDirtyFlags(); } // adds @@ -328,34 +298,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 9e0103e859..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: { @@ -459,6 +461,8 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = compound; } } + } else { + // TODO: warn about this case } return shape; } From 915cbb69dfb96f4aea1bd4f27661749cf50ec6c7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 26 Apr 2019 11:55:50 -0700 Subject: [PATCH 09/29] split work out of EntityTree::update(), call it preUpdate() --- .../src/entities/EntityTreeHeadlessViewer.cpp | 1 + interface/src/Application.cpp | 2 + .../src/EntityTreeRenderer.cpp | 6 ++ .../src/EntityTreeRenderer.h | 1 + libraries/entities/src/EntitySimulation.cpp | 57 ++++++++++++------- libraries/entities/src/EntitySimulation.h | 7 ++- libraries/entities/src/EntityTree.cpp | 10 +++- libraries/entities/src/EntityTree.h | 7 ++- .../entities/src/SimpleEntitySimulation.cpp | 4 +- .../entities/src/SimpleEntitySimulation.h | 2 +- libraries/octree/src/Octree.h | 5 +- libraries/octree/src/OctreePersistThread.cpp | 1 + .../physics/src/PhysicalEntitySimulation.cpp | 6 +- .../physics/src/PhysicalEntitySimulation.h | 2 +- 14 files changed, 76 insertions(+), 35 deletions(-) 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 4b10c23628..1474a954fb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6399,6 +6399,8 @@ void Application::update(float deltaTime) { PROFILE_RANGE(simulation_physics, "Simulation"); PerformanceTimer perfTimer("simulation"); + getEntities()->preUpdate(); + if (_physicsEnabled) { auto t0 = std::chrono::high_resolution_clock::now(); auto t1 = t0; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 7d130125b3..7953b7f4bc 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -474,6 +474,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 05105a2c7f..9391c47b5f 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/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index 05e71a8f06..e0a6d7af7c 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -176,17 +176,26 @@ 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); +} +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(); + /* TODO? maybe add to _entitiesToSort when DIRTY_POSITION is set? // 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. - uint32_t dirtyFlags = entity->getDirtyFlags(); if (dirtyFlags & Simulation::DIRTY_POSITION) { AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE), (float)TREE_SCALE); bool success; @@ -198,25 +207,29 @@ void EntitySimulation::changeEntity(EntityItemPointer 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 688f10cdee..fe8c8c9336 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2188,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/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/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/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 10b5532cd8..daf2ce0298 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(); diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index d7d8adffac..956674c070 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -72,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); From a0841c937cb79823c8ad536efc47f40ddef937be Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 1 May 2019 17:48:23 -0700 Subject: [PATCH 10/29] fix logic around updating off-thread shapes --- interface/src/avatar/AvatarManager.cpp | 4 +- interface/src/avatar/AvatarMotionState.cpp | 4 +- interface/src/avatar/AvatarMotionState.h | 2 +- interface/src/avatar/DetailedMotionState.cpp | 18 +++++- interface/src/avatar/DetailedMotionState.h | 2 +- libraries/physics/src/EntityMotionState.cpp | 4 +- libraries/physics/src/EntityMotionState.h | 2 +- libraries/physics/src/ObjectMotionState.cpp | 4 +- libraries/physics/src/ObjectMotionState.h | 2 +- .../physics/src/PhysicalEntitySimulation.cpp | 59 ++++++++----------- .../physics/src/PhysicalEntitySimulation.h | 3 - libraries/physics/src/PhysicsEngine.cpp | 2 - 12 files changed, 54 insertions(+), 52 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 8537217756..c49db0345f 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -507,13 +507,13 @@ void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transact } else { uint32_t flags = motionState->getIncomingDirtyFlags(); motionState->clearIncomingDirtyFlags(); - if (flags | EASY_DIRTY_PHYSICS_FLAGS) { + if (flags & EASY_DIRTY_PHYSICS_FLAGS) { motionState->handleEasyChanges(flags); } // NOTE: we don't call detailedMotionState->handleEasyChanges() here is because they are KINEMATIC // and Bullet will automagically call DetailedMotionState::getWorldTransform() on all that are active. - if (flags | (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { + if (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { transaction.objectsToReinsert.push_back(motionState); for (auto detailedMotionState : avatar->getDetailedMotionStates()) { transaction.objectsToReinsert.push_back(detailedMotionState); diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index ae229dc66f..97085c8443 100755 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -39,9 +39,9 @@ uint32_t AvatarMotionState::getIncomingDirtyFlags() const { return _body ? _dirtyFlags : 0; } -void AvatarMotionState::clearIncomingDirtyFlags() { +void AvatarMotionState::clearIncomingDirtyFlags(uint32_t mask) { if (_body) { - _dirtyFlags = 0; + _dirtyFlags &= ~mask; } } diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 5f26b5114d..a752d2c8fb 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -28,7 +28,7 @@ public: PhysicsMotionType getMotionType() const override { return _motionType; } uint32_t getIncomingDirtyFlags() const override; - void clearIncomingDirtyFlags() override; + void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) override; PhysicsMotionType computePhysicsMotionType() const override; diff --git a/interface/src/avatar/DetailedMotionState.cpp b/interface/src/avatar/DetailedMotionState.cpp index 4cfbbf032c..02a2b9d425 100644 --- a/interface/src/avatar/DetailedMotionState.cpp +++ b/interface/src/avatar/DetailedMotionState.cpp @@ -43,9 +43,9 @@ uint32_t DetailedMotionState::getIncomingDirtyFlags() const { return _body ? _dirtyFlags : 0; } -void DetailedMotionState::clearIncomingDirtyFlags() { +void DetailedMotionState::clearIncomingDirtyFlags(uint32_t mask) { if (_body) { - _dirtyFlags = 0; + _dirtyFlags &= ~mask; } } @@ -157,7 +157,19 @@ 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() { diff --git a/interface/src/avatar/DetailedMotionState.h b/interface/src/avatar/DetailedMotionState.h index 95b0600cf9..de59f41310 100644 --- a/interface/src/avatar/DetailedMotionState.h +++ b/interface/src/avatar/DetailedMotionState.h @@ -28,7 +28,7 @@ public: PhysicsMotionType getMotionType() const override { return _motionType; } uint32_t getIncomingDirtyFlags() const override; - void clearIncomingDirtyFlags() override; + void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) override; PhysicsMotionType computePhysicsMotionType() const override; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 24ce17d6fb..69da64b899 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -630,9 +630,9 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() const { return dirtyFlags; } -void EntityMotionState::clearIncomingDirtyFlags() { +void EntityMotionState::clearIncomingDirtyFlags(uint32_t mask) { if (_body && _entity) { - _entity->clearDirtyFlags(DIRTY_PHYSICS_FLAGS); + _entity->clearDirtyFlags(mask); } } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 0d5a97eeb8..7d42c2fc7a 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -56,7 +56,7 @@ public: void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step); virtual uint32_t getIncomingDirtyFlags() const override; - virtual void clearIncomingDirtyFlags() 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(); } diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 92482437e2..ad7332cb15 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -198,7 +198,9 @@ 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) { diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 7d204194b2..415b388e70 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -123,7 +123,7 @@ public: virtual glm::vec3 getObjectLinearVelocityChange() const; virtual uint32_t getIncomingDirtyFlags() const = 0; - virtual void clearIncomingDirtyFlags() = 0; + virtual void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) = 0; virtual PhysicsMotionType computePhysicsMotionType() const = 0; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index daf2ce0298..f1f356cef7 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -237,7 +237,6 @@ void PhysicalEntitySimulation::buildMotionStatesForEntitiesThatNeedThem() { _incomingChanges.insert(motionState); }; - QMutexLocker lock(&_mutex); uint32_t deliveryCount = ObjectMotionState::getShapeManager()->getWorkDeliveryCount(); if (deliveryCount != _lastWorkDeliveryCount) { // new off-thread shapes have arrived --> find adds whose shapes have arrived @@ -343,41 +342,22 @@ void PhysicalEntitySimulation::buildMotionStatesForEntitiesThatNeedThem() { } } -void PhysicalEntitySimulation::setObjectsToChange(const VectorOfMotionStates& objectsToChange) { - QMutexLocker lock(&_mutex); - for (auto object : objectsToChange) { - _incomingChanges.insert(static_cast(object)); - } -} - -void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result) { - result.clear(); - QMutexLocker lock(&_mutex); - for (auto stateItr : _incomingChanges) { - EntityMotionState* motionState = &(*stateItr); - result.push_back(motionState); - } - _incomingChanges.clear(); -} - void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) { + QMutexLocker lock(&_mutex); buildMotionStatesForEntitiesThatNeedThem(); for (auto& object : _incomingChanges) { uint32_t flags = object->getIncomingDirtyFlags(); - object->clearIncomingDirtyFlags(); + uint32_t handledFlags = EASY_DIRTY_PHYSICS_FLAGS; bool isInPhysicsSimulation = object->isInPhysicsSimulation(); - if (isInPhysicsSimulation != object->shouldBeInPhysicsSimulation()) { - if (isInPhysicsSimulation) { - transaction.objectsToRemove.push_back(object); - continue; - } else { - transaction.objectsToAdd.push_back(object); - } + bool shouldBeInPhysicsSimulation = object->shouldBeInPhysicsSimulation(); + if (!shouldBeInPhysicsSimulation && isInPhysicsSimulation) { + transaction.objectsToRemove.push_back(object); + continue; } - bool reinsert = false; - if (object->needsNewShape()) { + bool needsNewShape = object->needsNewShape(); + if (needsNewShape) { ShapeType shapeType = object->getShapeType(); if (shapeType == SHAPE_TYPE_STATIC_MESH) { ShapeRequest shapeRequest(object->_entity); @@ -389,13 +369,15 @@ void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transactio btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); if (shape) { object->setShape(shape); - reinsert = true; + 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 @@ -406,24 +388,35 @@ void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transactio btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); if (shape) { object->setShape(shape); - reinsert = true; + handledFlags |= Simulation::DIRTY_SHAPE; + needsNewShape = false; } else { // failed to build shape --> will not be added } } } if (!isInPhysicsSimulation) { - continue; + if (needsNewShape) { + // skip it + continue; + } else { + transaction.objectsToAdd.push_back(object); + handledFlags = DIRTY_PHYSICS_FLAGS; + } } - if (flags | EASY_DIRTY_PHYSICS_FLAGS) { + + if (flags & EASY_DIRTY_PHYSICS_FLAGS) { object->handleEasyChanges(flags); } - if (flags | (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP) || reinsert) { + if ((flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) || (handledFlags & Simulation::DIRTY_SHAPE)) { transaction.objectsToReinsert.push_back(object); + handledFlags |= (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP); } else if (flags & Simulation::DIRTY_PHYSICS_ACTIVATION && object->getRigidBody()->isStaticObject()) { transaction.activeStaticObjects.push_back(object); } + object->clearIncomingDirtyFlags(handledFlags); } + _incomingChanges.clear(); } void PhysicalEntitySimulation::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) { diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 956674c070..817f92cb3c 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -81,9 +81,6 @@ protected: // only called by EntitySimulation public: virtual void prepareEntityForDelete(EntityItemPointer entity) override; - void setObjectsToChange(const VectorOfMotionStates& objectsToChange); - void getObjectsToChange(VectorOfMotionStates& result); - void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction); void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index bae9ef2485..2e1a1a401e 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) { From af1f4364efeb4e9be38b23f73a780fc013104c57 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 2 May 2019 13:24:25 -0700 Subject: [PATCH 11/29] don't forget to add detailedMotionStates to simulation --- interface/src/avatar/AvatarManager.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c49db0345f..364ba2d5f0 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -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. @@ -471,13 +471,17 @@ void AvatarManager::rebuildAvatarPhysics(PhysicsEngine::Transaction& transaction OtherAvatar::BodyLOD lod = avatar->getBodyLOD(); if (lod == OtherAvatar::BodyLOD::Sphere) { auto dMotionState = createDetailedMotionState(avatar, -1); - detailedMotionStates.push_back(dMotionState); + 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); } } } From 6e27b4d1c5946fd946a394a5dd8e4371349d5fd1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 2 May 2019 15:40:26 -0700 Subject: [PATCH 12/29] remove bodies of deleted entities --- .../physics/src/PhysicalEntitySimulation.cpp | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index f1f356cef7..4e73b6f966 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -344,9 +344,29 @@ void PhysicalEntitySimulation::buildMotionStatesForEntitiesThatNeedThem() { void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) { QMutexLocker lock(&_mutex); + // 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(); + + // entities to add buildMotionStatesForEntitiesThatNeedThem(); + + // motionStates with changed entities: delete, add, or change for (auto& object : _incomingChanges) { - uint32_t flags = object->getIncomingDirtyFlags(); + uint32_t unhandledFlags = object->getIncomingDirtyFlags(); uint32_t handledFlags = EASY_DIRTY_PHYSICS_FLAGS; bool isInPhysicsSimulation = object->isInPhysicsSimulation(); @@ -402,16 +422,17 @@ void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transactio } else { transaction.objectsToAdd.push_back(object); handledFlags = DIRTY_PHYSICS_FLAGS; + unhandledFlags = 0; } } - if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - object->handleEasyChanges(flags); + if (unhandledFlags & EASY_DIRTY_PHYSICS_FLAGS) { + object->handleEasyChanges(unhandledFlags); } - if ((flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) || (handledFlags & Simulation::DIRTY_SHAPE)) { + if (unhandledFlags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP | (handledFlags & Simulation::DIRTY_SHAPE))) { transaction.objectsToReinsert.push_back(object); handledFlags |= (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP); - } else if (flags & Simulation::DIRTY_PHYSICS_ACTIVATION && object->getRigidBody()->isStaticObject()) { + } else if (unhandledFlags & Simulation::DIRTY_PHYSICS_ACTIVATION && object->getRigidBody()->isStaticObject()) { transaction.activeStaticObjects.push_back(object); } object->clearIncomingDirtyFlags(handledFlags); From e770bd6142013b80f7d801b42a05595ee2512051 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 3 May 2019 13:39:51 -0700 Subject: [PATCH 13/29] build CollisionShapes and load RigidBodies even when not yet stepping --- interface/src/Application.cpp | 62 ++++++++++----------- interface/src/octree/SafeLanding.cpp | 2 +- libraries/entities/src/EntityItem.cpp | 44 +++++++-------- libraries/entities/src/EntityItem.h | 3 +- libraries/physics/src/EntityMotionState.cpp | 9 +++ libraries/physics/src/EntityMotionState.h | 2 + libraries/shared/src/SimulationFlags.h | 7 ++- 7 files changed, 71 insertions(+), 58 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1474a954fb..81bfd6127b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6401,40 +6401,40 @@ void Application::update(float deltaTime) { getEntities()->preUpdate(); - if (_physicsEnabled) { - auto t0 = std::chrono::high_resolution_clock::now(); - auto t1 = t0; + 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, "Entities"); - PhysicsEngine::Transaction transaction; - _entitySimulation->buildPhysicsTransaction(transaction); - _physicsEngine->processTransaction(transaction); - _entitySimulation->handleProcessedPhysicsTransaction(transaction); - } + PROFILE_RANGE(simulation_physics, "Entities"); + PhysicsEngine::Transaction transaction; + _entitySimulation->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + _entitySimulation->handleProcessedPhysicsTransaction(transaction); + } + 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->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/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/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 b99536f4dc..ea4b11e0b0 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -424,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/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 69da64b899..67aa7d2d7d 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -743,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 7d42c2fc7a..7456837777 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -100,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; } diff --git a/libraries/shared/src/SimulationFlags.h b/libraries/shared/src/SimulationFlags.h index af8c0e5fce..9dbb719af2 100644 --- a/libraries/shared/src/SimulationFlags.h +++ b/libraries/shared/src/SimulationFlags.h @@ -40,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 | From f6916edac458ec6fa507685a8b1d526deac1e6a0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 3 May 2019 14:48:10 -0700 Subject: [PATCH 14/29] cleanup and removing some accidental C++-20-isms --- interface/src/avatar/AvatarManager.cpp | 4 +--- libraries/entities/src/EntitySimulation.cpp | 16 ---------------- libraries/physics/src/ShapeManager.h | 4 ++-- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 364ba2d5f0..ac74d0ab20 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -427,8 +427,6 @@ DetailedMotionState* AvatarManager::createDetailedMotionState(OtherAvatarPointer std::vector boundJoints; const btCollisionShape* shape = avatar->createCollisionShape(jointIndex, isBound, boundJoints); if (shape) { - //std::shared_ptr avatar = shared_from_this(); - //std::shared_ptr avatar = getThisPointer(); DetailedMotionState* motionState = new DetailedMotionState(avatar, shape, jointIndex); motionState->setMass(0.0f); // DetailedMotionState has KINEMATIC MotionType, so zero mass is ok motionState->setIsBound(isBound, boundJoints); @@ -514,7 +512,7 @@ void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transact if (flags & EASY_DIRTY_PHYSICS_FLAGS) { motionState->handleEasyChanges(flags); } - // NOTE: we don't call detailedMotionState->handleEasyChanges() here is because they are KINEMATIC + // 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 (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index e0a6d7af7c..b5e4fed0fd 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -192,22 +192,6 @@ void EntitySimulation::processChangedEntities() { void EntitySimulation::processChangedEntity(const EntityItemPointer& entity) { uint32_t dirtyFlags = entity->getDirtyFlags(); - /* TODO? maybe add to _entitiesToSort when DIRTY_POSITION is set? - // 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. - 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 | Simulation::DIRTY_UPDATEABLE)) { if (dirtyFlags & Simulation::DIRTY_LIFETIME) { diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index b07999ec64..d895ad204e 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -104,8 +104,8 @@ private: ShapeFactory::Worker* _deadWorker { nullptr }; TimePoint _nextOrphanExpiry; uint32_t _ringIndex { 0 }; - std::atomic_uint32_t _workRequestCount { 0 }; - std::atomic_uint32_t _workDeliveryCount { 0 }; + std::atomic_uint _workRequestCount { 0 }; + std::atomic_uint _workDeliveryCount { 0 }; }; #endif // hifi_ShapeManager_h From 53ad41e5eea089269b021b01418ab635ff43c0b5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 3 May 2019 16:55:40 -0700 Subject: [PATCH 15/29] include in ShapeManager.h --- libraries/physics/src/ShapeManager.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index d895ad204e..1f39fd56be 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -13,6 +13,7 @@ #define hifi_ShapeManager_h #include +#include #include #include From d6a20a4abd4e7b0d001b0d474372fe5936fcc691 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 3 May 2019 17:00:40 -0700 Subject: [PATCH 16/29] fix inverted logic typo --- libraries/physics/src/PhysicalEntitySimulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 4e73b6f966..9672d53f92 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -354,7 +354,7 @@ void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transactio if (_shapeRequests.size() > 0) { ShapeRequest shapeRequest(entity); ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest); - if (requestItr == _shapeRequests.end()) { + if (requestItr != _shapeRequests.end()) { _shapeRequests.erase(requestItr); } } From bef053584b9a30a44b9447d3a2a9b151e6a38300 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 6 May 2019 14:45:56 -0700 Subject: [PATCH 17/29] don't forget to clear _activeStaticBodies --- libraries/physics/src/PhysicsEngine.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 2e1a1a401e..976c547c5e 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -250,6 +250,7 @@ void PhysicsEngine::removeSetOfObjects(const SetOfMotionStates& objects) { } object->clearIncomingDirtyFlags(); } + _activeStaticBodies.clear(); } void PhysicsEngine::addObjects(const VectorOfMotionStates& objects) { From 81f960a4bdb7bc7b95c6e84c2837ea34f05c676c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 6 May 2019 14:47:00 -0700 Subject: [PATCH 18/29] remove from _physicalObjects list after transaction --- libraries/physics/src/PhysicalEntitySimulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 9672d53f92..08ea98eef1 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -156,7 +156,6 @@ void PhysicalEntitySimulation::processChangedEntity(const EntityItemPointer& ent // remove from the physical simulation _incomingChanges.remove(motionState); - _physicalObjects.remove(motionState); removeOwnershipData(motionState); _entitiesToRemoveFromPhysics.insert(entity); if (canBeKinematic && entity->isMovingRelativeToParent()) { @@ -443,6 +442,7 @@ void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transactio 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(); From 8e6394c95f10b7dcdfd7fed5e5bc6809788bdc73 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 6 May 2019 17:34:51 -0700 Subject: [PATCH 19/29] ParticleEffectEntityItem should not be in physics simulation --- libraries/entities/src/ParticleEffectEntityItem.h | 2 ++ 1 file changed, 2 insertions(+) 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; } From a28d185afc7a5322fe352b03caf0bf79168a1ae6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 7 May 2019 10:39:44 -0700 Subject: [PATCH 20/29] make explicit: zones shouldn't be in physics simulation --- libraries/entities/src/ZoneEntityItem.h | 1 + 1 file changed, 1 insertion(+) 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); From 883b83ec59a743191527044c725157bdc23f681a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 7 May 2019 15:15:12 -0700 Subject: [PATCH 21/29] minor name change for more clarity --- libraries/workload/src/workload/View.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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) { From 72c6cad581acabb44ea3c4d7f685ec2163230edc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 7 May 2019 15:15:53 -0700 Subject: [PATCH 22/29] cleanup _deadWorker in dtor, more correct _nextOrphanExpiry --- libraries/physics/src/ShapeManager.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index 85587a6c67..c37f95b5f1 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -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) { @@ -230,12 +234,14 @@ void ShapeManager::acceptWork(ShapeFactory::Worker* worker) { // 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 expiry = now + std::chrono::microseconds(SHAPE_EXPIRY); + 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()) { - if (_orphans[i].expiry < now) { + auto expiry = _orphans[i].expiry; + if (expiry < now) { uint64_t key = _orphans[i].key; HashKey hashKey(key); ShapeReference* shapeRef = _shapeMap.find(hashKey); @@ -248,12 +254,14 @@ void ShapeManager::acceptWork(ShapeFactory::Worker* worker) { _orphans[i] = _orphans.back(); _orphans.pop_back(); } else { + if (expiry < _nextOrphanExpiry) { + _nextOrphanExpiry = expiry; + } ++i; } } } - _nextOrphanExpiry = expiry; - _orphans.push_back(KeyExpiry(newRef.key, expiry)); + _orphans.push_back(KeyExpiry(newRef.key, newExpiry)); } } disconnect(worker, &ShapeFactory::Worker::submitWork, this, &ShapeManager::acceptWork); From f60dbda25e5e0293918b54f44f7965cbdbe02d6f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 8 May 2019 14:48:49 -0700 Subject: [PATCH 23/29] clear all HARD flags when reinserting object in PhysicsEngine --- libraries/physics/src/PhysicalEntitySimulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 08ea98eef1..daa2b5d954 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -430,7 +430,7 @@ void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transactio } if (unhandledFlags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP | (handledFlags & Simulation::DIRTY_SHAPE))) { transaction.objectsToReinsert.push_back(object); - handledFlags |= (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP); + handledFlags |= HARD_DIRTY_PHYSICS_FLAGS; } else if (unhandledFlags & Simulation::DIRTY_PHYSICS_ACTIVATION && object->getRigidBody()->isStaticObject()) { transaction.activeStaticObjects.push_back(object); } From 0fae615cb4917fd2f2b9665df8c9f601a2dee74c Mon Sep 17 00:00:00 2001 From: danteruiz Date: Wed, 8 May 2019 17:42:22 -0700 Subject: [PATCH 24/29] fix dock widget context --- interface/src/Application.h | 3 ++- interface/src/ui/InteractiveWindow.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index 300769b349..373e2c16a2 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -211,6 +211,8 @@ public: float getNumCollisionObjects() const; float getTargetRenderFrameRate() const; // frames/second + static void setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties); + float getFieldOfView() { return _fieldOfView.get(); } void setFieldOfView(float fov); @@ -604,7 +606,6 @@ private: void maybeToggleMenuVisible(QMouseEvent* event) const; void toggleTabletUI(bool shouldOpen = false) const; - static void setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties); void userKickConfirmation(const QUuid& nodeID); MainWindow* _window; diff --git a/interface/src/ui/InteractiveWindow.cpp b/interface/src/ui/InteractiveWindow.cpp index 3e0aee47c7..810d85fa89 100644 --- a/interface/src/ui/InteractiveWindow.cpp +++ b/interface/src/ui/InteractiveWindow.cpp @@ -92,6 +92,8 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap auto mainWindow = qApp->getWindow(); _dockWidget = std::shared_ptr(new DockWidget(title, mainWindow), dockWidgetDeleter); + auto quickView = _dockWidget->getQuickView(); + Application::setupQmlSurface(quickView->rootContext(), true); if (nativeWindowInfo.contains(DOCK_AREA_PROPERTY)) { DockArea dockedArea = (DockArea) nativeWindowInfo[DOCK_AREA_PROPERTY].toInt(); @@ -119,7 +121,6 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap } } - auto quickView = _dockWidget->getQuickView(); QObject::connect(quickView.get(), &QQuickView::statusChanged, [&, this] (QQuickView::Status status) { if (status == QQuickView::Ready) { QQuickItem* rootItem = _dockWidget->getRootItem(); From 85f425a93b2d676c9dcc8c6d51e8675ab51ab241 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 9 May 2019 10:05:02 -0700 Subject: [PATCH 25/29] Apply variable output gain using cubic smoothing --- libraries/audio-client/src/AudioClient.cpp | 37 ++++++++++++++++++++++ libraries/audio-client/src/AudioClient.h | 2 ++ 2 files changed, 39 insertions(+) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 4d3311b065..cef6adcab0 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -221,6 +221,35 @@ static float computeLoudness(int16_t* samples, int numSamples, int numChannels, return (float)loudness * scale; } +template +static void applyGainSmoothing(float* buffer, int numFrames, float gain0, float gain1) { + + // fast path for unity gain + if (gain0 == 1.0f && gain1 == 1.0f) { + return; + } + + // cubic poly from gain0 to gain1 + float c3 = -2.0f * (gain1 - gain0); + float c2 = 3.0f * (gain1 - gain0); + float c0 = gain0; + + float t = 0.0f; + float tStep = 1.0f / numFrames; + + for (int i = 0; i < numFrames; i++) { + + // evaluate poly over t=[0,1) + float gain = (c3 * t + c2) * t * t + c0; + t += tStep; + + // apply gain to all channels + for (int ch = 0; ch < NUM_CHANNELS; ch++) { + buffer[NUM_CHANNELS*i + ch] *= gain; + } + } +} + static inline float convertToFloat(int16_t sample) { return (float)sample * (1 / 32768.0f); } @@ -2109,6 +2138,14 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { int framesPopped = samplesPopped / AudioConstants::STEREO; int bytesWritten; if (samplesPopped > 0) { + + // apply output gain + float newGain = _audio->_outputGain.load(std::memory_order_acquire); + float oldGain = _audio->_lastOutputGain; + _audio->_lastOutputGain = newGain; + + applyGainSmoothing(mixBuffer, framesPopped, oldGain, newGain); + if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) { // limit the audio _audio->_audioLimiter.render(mixBuffer, (int16_t*)data, framesPopped); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 7608bf5cdb..00dd7ae299 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -395,6 +395,8 @@ private: int _outputPeriod { 0 }; float* _outputMixBuffer { NULL }; int16_t* _outputScratchBuffer { NULL }; + std::atomic _outputGain { 1.0f }; + float _lastOutputGain { 1.0f }; // for local audio (used by audio injectors thread) std::atomic _localInjectorGain { 1.0f }; From 1b733d3fb3ed9063e81cb4b0876fec8976634f71 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 9 May 2019 12:56:07 -0700 Subject: [PATCH 26/29] While PTT is active, duck the audio output by 20dB to reduce echo --- interface/src/scripting/Audio.cpp | 2 ++ libraries/audio-client/src/AudioClient.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index b406c097e7..cb8b211352 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -366,8 +366,10 @@ void Audio::onContextChanged() { void Audio::handlePushedToTalk(bool enabled) { if (getPTT()) { if (enabled) { + DependencyManager::get()->setOutputGain(0.1f); // duck the output by 20dB setMuted(false); } else { + DependencyManager::get()->setOutputGain(1.0f); setMuted(true); } } diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 00dd7ae299..db70e2f7b3 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -241,8 +241,10 @@ public slots: void setInputVolume(float volume, bool emitSignal = true); void setReverb(bool reverb); void setReverbOptions(const AudioEffectOptions* options); + void setLocalInjectorGain(float gain) { _localInjectorGain = gain; }; void setSystemInjectorGain(float gain) { _systemInjectorGain = gain; }; + void setOutputGain(float gain) { _outputGain = gain; }; void outputNotify(); From c7432ffe5f483638ceb50794476873e21ff353c9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 9 May 2019 13:48:07 -0700 Subject: [PATCH 27/29] also update easy flags when rebuilding avatar shape --- interface/src/avatar/AvatarManager.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index ac74d0ab20..51fcaf0357 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -504,23 +504,18 @@ void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transact } } else if (isInPhysics) { 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 { - uint32_t flags = motionState->getIncomingDirtyFlags(); + } else if (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { + transaction.objectsToReinsert.push_back(motionState); motionState->clearIncomingDirtyFlags(); - 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 (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { - transaction.objectsToReinsert.push_back(motionState); - for (auto detailedMotionState : avatar->getDetailedMotionStates()) { - transaction.objectsToReinsert.push_back(detailedMotionState); - } - } } } } From 2bb522eea68dc7d91ca18212d766aa2fa1ea71a9 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 9 May 2019 14:16:59 -0700 Subject: [PATCH 28/29] entity icons ignorePickIntersection when not visible --- scripts/system/libraries/entityIconOverlayManager.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/system/libraries/entityIconOverlayManager.js b/scripts/system/libraries/entityIconOverlayManager.js index fc4612ae80..68104ff4bb 100644 --- a/scripts/system/libraries/entityIconOverlayManager.js +++ b/scripts/system/libraries/entityIconOverlayManager.js @@ -64,7 +64,8 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { visible = isVisible; for (var id in entityOverlays) { Overlays.editOverlay(entityOverlays[id], { - visible: visible + visible: visible, + ignorePickIntersection: !visible }); } } @@ -85,7 +86,8 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { function releaseOverlay(overlay) { unusedOverlays.push(overlay); Overlays.editOverlay(overlay, { - visible: false + visible: false, + ignorePickIntersection: true }); } @@ -99,6 +101,7 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { position: properties.position, rotation: Quat.fromPitchYawRollDegrees(0, 0, 270), visible: visible, + ignorePickIntersection: !visible, alpha: 0.9, scale: 0.5, drawInFront: true, From a0b34e4f15d04e442e719a7788b529dd11b39346 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 9 May 2019 14:45:36 -0700 Subject: [PATCH 29/29] more correct clearing of dirty flags for AvatarMotionState --- interface/src/avatar/AvatarManager.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 51fcaf0357..e01f9339f4 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -513,8 +513,10 @@ void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transact if (motionState->needsNewShape()) { rebuildAvatarPhysics(transaction, avatar); - } else if (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { - transaction.objectsToReinsert.push_back(motionState); + } else { + if (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) { + transaction.objectsToReinsert.push_back(motionState); + } motionState->clearIncomingDirtyFlags(); } }