mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 06:44:17 +02:00
Merge pull request #6756 from AndrewMeadows/physics-cleanup-003
more correct management of MotionState pointers
This commit is contained in:
commit
bc538a0f7d
28 changed files with 328 additions and 324 deletions
interface/src
Application.cpp
avatar
libraries
avatars/src
entities-renderer/src
entities/src
BoxEntityItem.hEntityItem.hEntitySimulation.cppEntitySimulation.hEntityTree.cppModelEntityItem.cppSimpleEntitySimulation.cppSimpleEntitySimulation.hSphereEntityItem.hZoneEntityItem.h
physics/src
EntityMotionState.cppEntityMotionState.hObjectMotionState.cppObjectMotionState.hPhysicalEntitySimulation.cppPhysicalEntitySimulation.hPhysicsEngine.cppPhysicsEngine.h
shared/src
|
@ -1084,8 +1084,8 @@ Application::~Application() {
|
|||
// remove avatars from physics engine
|
||||
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
|
||||
VectorOfMotionStates motionStates;
|
||||
DependencyManager::get<AvatarManager>()->getObjectsToDelete(motionStates);
|
||||
_physicsEngine->deleteObjects(motionStates);
|
||||
DependencyManager::get<AvatarManager>()->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
|
||||
DependencyManager::destroy<OffscreenUi>();
|
||||
DependencyManager::destroy<AvatarManager>();
|
||||
|
@ -3085,11 +3085,11 @@ void Application::update(float deltaTime) {
|
|||
PerformanceTimer perfTimer("physics");
|
||||
|
||||
static VectorOfMotionStates motionStates;
|
||||
_entitySimulation.getObjectsToDelete(motionStates);
|
||||
_physicsEngine->deleteObjects(motionStates);
|
||||
_entitySimulation.getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_entitySimulation.getObjectsToAdd(motionStates);
|
||||
_entitySimulation.getObjectsToAddToPhysics(motionStates);
|
||||
_physicsEngine->addObjects(motionStates);
|
||||
|
||||
});
|
||||
|
@ -3102,9 +3102,9 @@ void Application::update(float deltaTime) {
|
|||
_entitySimulation.applyActionChanges();
|
||||
|
||||
AvatarManager* avatarManager = DependencyManager::get<AvatarManager>().data();
|
||||
avatarManager->getObjectsToDelete(motionStates);
|
||||
_physicsEngine->deleteObjects(motionStates);
|
||||
avatarManager->getObjectsToAdd(motionStates);
|
||||
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
avatarManager->getObjectsToAddToPhysics(motionStates);
|
||||
_physicsEngine->addObjects(motionStates);
|
||||
avatarManager->getObjectsToChange(motionStates);
|
||||
_physicsEngine->changeObjects(motionStates);
|
||||
|
@ -4083,7 +4083,7 @@ bool Application::nearbyEntitiesAreReadyForPhysics() {
|
|||
});
|
||||
|
||||
foreach (EntityItemPointer entity, entities) {
|
||||
if (!entity->isReadyToComputeShape()) {
|
||||
if (entity->shouldBePhysical() && !entity->isReadyToComputeShape()) {
|
||||
static QString repeatedMessage =
|
||||
LogHandler::getInstance().addRepeatedMessageRegex("Physics disabled until entity loads: .*");
|
||||
qCDebug(interfaceapp) << "Physics disabled until entity loads: " << entity->getID() << entity->getName();
|
||||
|
|
|
@ -108,7 +108,11 @@ Avatar::Avatar(RigPointer rig) :
|
|||
}
|
||||
|
||||
Avatar::~Avatar() {
|
||||
assert(_motionState == nullptr);
|
||||
assert(isDead()); // mark dead before calling the dtor
|
||||
if (_motionState) {
|
||||
delete _motionState;
|
||||
_motionState = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const float BILLBOARD_LOD_DISTANCE = 40.0f;
|
||||
|
|
|
@ -76,6 +76,10 @@ AvatarManager::AvatarManager(QObject* parent) :
|
|||
packetReceiver.registerListener(PacketType::AvatarBillboard, this, "processAvatarBillboardPacket");
|
||||
}
|
||||
|
||||
AvatarManager::~AvatarManager() {
|
||||
_myAvatar->die();
|
||||
}
|
||||
|
||||
void AvatarManager::init() {
|
||||
_myAvatar->init();
|
||||
{
|
||||
|
@ -165,7 +169,12 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE);
|
||||
if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
|
||||
avatar->removeFromScene(*fadingIterator, scene, pendingChanges);
|
||||
fadingIterator = _avatarFades.erase(fadingIterator);
|
||||
// only remove from _avatarFades if we're sure its motionState has been removed from PhysicsEngine
|
||||
if (_motionStatesToRemoveFromPhysics.empty()) {
|
||||
fadingIterator = _avatarFades.erase(fadingIterator);
|
||||
} else {
|
||||
++fadingIterator;
|
||||
}
|
||||
} else {
|
||||
avatar->simulate(deltaTime);
|
||||
++fadingIterator;
|
||||
|
@ -193,20 +202,6 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe
|
|||
return newAvatar;
|
||||
}
|
||||
|
||||
// protected
|
||||
void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) {
|
||||
auto rawPointer = std::static_pointer_cast<Avatar>(avatar);
|
||||
AvatarMotionState* motionState = rawPointer->getMotionState();
|
||||
if (motionState) {
|
||||
// clean up physics stuff
|
||||
motionState->clearObjectBackPointer();
|
||||
rawPointer->setMotionState(nullptr);
|
||||
_avatarMotionStates.remove(motionState);
|
||||
_motionStatesToAdd.remove(motionState);
|
||||
_motionStatesToDelete.push_back(motionState);
|
||||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
void AvatarManager::removeAvatar(const QUuid& sessionUUID) {
|
||||
QWriteLocker locker(&_hashLock);
|
||||
|
@ -220,8 +215,18 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) {
|
|||
void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar) {
|
||||
AvatarHashMap::handleRemovedAvatar(removedAvatar);
|
||||
|
||||
removedAvatar->die();
|
||||
removeAvatarMotionState(removedAvatar);
|
||||
// removedAvatar is a shared pointer to an AvatarData but we need to get to the derived Avatar
|
||||
// class in this context so we can call methods that don't exist at the base class.
|
||||
Avatar* avatar = static_cast<Avatar*>(removedAvatar.get());
|
||||
avatar->die();
|
||||
|
||||
AvatarMotionState* motionState = avatar->getMotionState();
|
||||
if (motionState) {
|
||||
_motionStatesThatMightUpdate.remove(motionState);
|
||||
_motionStatesToAddToPhysics.remove(motionState);
|
||||
_motionStatesToRemoveFromPhysics.push_back(motionState);
|
||||
}
|
||||
|
||||
_avatarFades.push_back(removedAvatar);
|
||||
}
|
||||
|
||||
|
@ -274,22 +279,22 @@ AvatarData* AvatarManager::getAvatar(QUuid avatarID) {
|
|||
}
|
||||
|
||||
|
||||
void AvatarManager::getObjectsToDelete(VectorOfMotionStates& result) {
|
||||
void AvatarManager::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
result.swap(_motionStatesToDelete);
|
||||
result.swap(_motionStatesToRemoveFromPhysics);
|
||||
}
|
||||
|
||||
void AvatarManager::getObjectsToAdd(VectorOfMotionStates& result) {
|
||||
void AvatarManager::getObjectsToAddToPhysics(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
for (auto motionState : _motionStatesToAdd) {
|
||||
for (auto motionState : _motionStatesToAddToPhysics) {
|
||||
result.push_back(motionState);
|
||||
}
|
||||
_motionStatesToAdd.clear();
|
||||
_motionStatesToAddToPhysics.clear();
|
||||
}
|
||||
|
||||
void AvatarManager::getObjectsToChange(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
for (auto state : _avatarMotionStates) {
|
||||
for (auto state : _motionStatesThatMightUpdate) {
|
||||
if (state->_dirtyFlags > 0) {
|
||||
result.push_back(state);
|
||||
}
|
||||
|
@ -344,8 +349,8 @@ void AvatarManager::addAvatarToSimulation(Avatar* avatar) {
|
|||
// we don't add to the simulation now, we put it on a list to be added later
|
||||
AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
|
||||
avatar->setMotionState(motionState);
|
||||
_motionStatesToAdd.insert(motionState);
|
||||
_avatarMotionStates.insert(motionState);
|
||||
_motionStatesToAddToPhysics.insert(motionState);
|
||||
_motionStatesThatMightUpdate.insert(motionState);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ public:
|
|||
/// Registers the script types associated with the avatar manager.
|
||||
static void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
virtual ~AvatarManager();
|
||||
|
||||
void init();
|
||||
|
||||
MyAvatar* getMyAvatar() { return _myAvatar.get(); }
|
||||
|
@ -59,8 +61,8 @@ public:
|
|||
Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID);
|
||||
|
||||
|
||||
void getObjectsToDelete(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToAdd(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToAddToPhysics(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToChange(VectorOfMotionStates& motionStates);
|
||||
void handleOutgoingChanges(const VectorOfMotionStates& motionStates);
|
||||
void handleCollisionEvents(const CollisionEvents& collisionEvents);
|
||||
|
@ -80,7 +82,6 @@ private:
|
|||
// virtual overrides
|
||||
virtual AvatarSharedPointer newSharedAvatar();
|
||||
virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
||||
void removeAvatarMotionState(AvatarSharedPointer avatar);
|
||||
|
||||
virtual void removeAvatar(const QUuid& sessionUUID);
|
||||
virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar);
|
||||
|
@ -93,9 +94,9 @@ private:
|
|||
|
||||
bool _shouldShowReceiveStats = false;
|
||||
|
||||
SetOfAvatarMotionStates _avatarMotionStates;
|
||||
SetOfMotionStates _motionStatesToAdd;
|
||||
VectorOfMotionStates _motionStatesToDelete;
|
||||
SetOfAvatarMotionStates _motionStatesThatMightUpdate;
|
||||
SetOfMotionStates _motionStatesToAddToPhysics;
|
||||
VectorOfMotionStates _motionStatesToRemoveFromPhysics;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(AvatarManager::LocalLight)
|
||||
|
|
|
@ -25,20 +25,17 @@ AvatarMotionState::AvatarMotionState(Avatar* avatar, btCollisionShape* shape) :
|
|||
}
|
||||
|
||||
AvatarMotionState::~AvatarMotionState() {
|
||||
assert(_avatar);
|
||||
_avatar = nullptr;
|
||||
}
|
||||
|
||||
// virtual
|
||||
uint32_t AvatarMotionState::getIncomingDirtyFlags() {
|
||||
uint32_t dirtyFlags = 0;
|
||||
if (_body && _avatar) {
|
||||
dirtyFlags = _dirtyFlags;
|
||||
}
|
||||
return dirtyFlags;
|
||||
return _body ? _dirtyFlags : 0;
|
||||
}
|
||||
|
||||
void AvatarMotionState::clearIncomingDirtyFlags() {
|
||||
if (_body && _avatar) {
|
||||
if (_body) {
|
||||
_dirtyFlags = 0;
|
||||
}
|
||||
}
|
||||
|
@ -50,12 +47,9 @@ MotionType AvatarMotionState::computeObjectMotionType() const {
|
|||
|
||||
// virtual and protected
|
||||
btCollisionShape* AvatarMotionState::computeNewShape() {
|
||||
if (_avatar) {
|
||||
ShapeInfo shapeInfo;
|
||||
_avatar->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
return nullptr;
|
||||
ShapeInfo shapeInfo;
|
||||
_avatar->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
@ -65,9 +59,6 @@ bool AvatarMotionState::isMoving() const {
|
|||
|
||||
// virtual
|
||||
void AvatarMotionState::getWorldTransform(btTransform& worldTrans) const {
|
||||
if (!_avatar) {
|
||||
return;
|
||||
}
|
||||
worldTrans.setOrigin(glmToBullet(getObjectPosition()));
|
||||
worldTrans.setRotation(glmToBullet(getObjectRotation()));
|
||||
if (_body) {
|
||||
|
@ -76,11 +67,8 @@ void AvatarMotionState::getWorldTransform(btTransform& worldTrans) const {
|
|||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
// virtual
|
||||
void AvatarMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||
if (!_avatar) {
|
||||
return;
|
||||
}
|
||||
// HACK: The PhysicsEngine does not actually move OTHER avatars -- instead it slaves their local RigidBody to the transform
|
||||
// as specified by a remote simulation. However, to give the remote simulation time to respond to our own objects we tie
|
||||
// the other avatar's body to its true position with a simple spring. This is a HACK that will have to be improved later.
|
||||
|
@ -154,15 +142,8 @@ QUuid AvatarMotionState::getSimulatorID() const {
|
|||
return _avatar->getSessionUUID();
|
||||
}
|
||||
|
||||
// virtual
|
||||
int16_t AvatarMotionState::computeCollisionGroup() {
|
||||
// virtual
|
||||
int16_t AvatarMotionState::computeCollisionGroup() const {
|
||||
return COLLISION_GROUP_OTHER_AVATAR;
|
||||
}
|
||||
|
||||
// virtual
|
||||
void AvatarMotionState::clearObjectBackPointer() {
|
||||
ObjectMotionState::clearObjectBackPointer();
|
||||
_avatar = nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -21,55 +21,65 @@ class Avatar;
|
|||
class AvatarMotionState : public ObjectMotionState {
|
||||
public:
|
||||
AvatarMotionState(Avatar* avatar, btCollisionShape* shape);
|
||||
~AvatarMotionState();
|
||||
|
||||
virtual MotionType getMotionType() const { return _motionType; }
|
||||
virtual MotionType getMotionType() const override { return _motionType; }
|
||||
|
||||
virtual uint32_t getIncomingDirtyFlags();
|
||||
virtual void clearIncomingDirtyFlags();
|
||||
virtual uint32_t getIncomingDirtyFlags() override;
|
||||
virtual void clearIncomingDirtyFlags() override;
|
||||
|
||||
virtual MotionType computeObjectMotionType() const;
|
||||
virtual MotionType computeObjectMotionType() const override;
|
||||
|
||||
virtual bool isMoving() const;
|
||||
virtual bool isMoving() const override;
|
||||
|
||||
// this relays incoming position/rotation to the RigidBody
|
||||
virtual void getWorldTransform(btTransform& worldTrans) const;
|
||||
virtual void getWorldTransform(btTransform& worldTrans) const override;
|
||||
|
||||
// this relays outgoing position/rotation to the EntityItem
|
||||
virtual void setWorldTransform(const btTransform& worldTrans);
|
||||
virtual 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.
|
||||
|
||||
virtual float getObjectRestitution() const;
|
||||
virtual float getObjectFriction() const;
|
||||
virtual float getObjectLinearDamping() const;
|
||||
virtual float getObjectAngularDamping() const;
|
||||
// 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;
|
||||
|
||||
virtual glm::vec3 getObjectPosition() const;
|
||||
virtual glm::quat getObjectRotation() const;
|
||||
virtual glm::vec3 getObjectLinearVelocity() const;
|
||||
virtual glm::vec3 getObjectAngularVelocity() const;
|
||||
virtual glm::vec3 getObjectGravity() const;
|
||||
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;
|
||||
|
||||
virtual const QUuid& getObjectID() const;
|
||||
virtual const QUuid& getObjectID() const override;
|
||||
|
||||
virtual QUuid getSimulatorID() const;
|
||||
virtual QUuid getSimulatorID() const override;
|
||||
|
||||
void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal);
|
||||
|
||||
void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; }
|
||||
|
||||
virtual int16_t computeCollisionGroup();
|
||||
virtual int16_t computeCollisionGroup() const override;
|
||||
|
||||
friend class AvatarManager;
|
||||
friend class Avatar;
|
||||
|
||||
protected:
|
||||
virtual bool isReadyToComputeShape() { return true; }
|
||||
// the dtor had been made protected to force the compiler to verify that it is only
|
||||
// ever called by the Avatar class dtor.
|
||||
~AvatarMotionState();
|
||||
|
||||
virtual bool isReadyToComputeShape() const override { return true; }
|
||||
virtual btCollisionShape* computeNewShape();
|
||||
virtual void clearObjectBackPointer();
|
||||
Avatar* _avatar;
|
||||
|
||||
// The AvatarMotionState keeps a RAW backpointer to its Avatar because all AvatarMotionState
|
||||
// instances are "owned" by their corresponding Avatar instance and are deleted in the Avatar dtor.
|
||||
// In other words, it is impossible for the Avatar to be deleted out from under its MotionState.
|
||||
// In conclusion: weak pointer shennanigans would be pure overhead.
|
||||
Avatar* _avatar; // do NOT use smartpointer here, no need for weakpointer
|
||||
|
||||
uint32_t _dirtyFlags;
|
||||
};
|
||||
|
||||
|
|
|
@ -344,9 +344,6 @@ public:
|
|||
|
||||
glm::vec3 getClientGlobalPosition() { return _globalPosition; }
|
||||
|
||||
void die() { _isDead = true; }
|
||||
bool isDead() const { return _isDead; }
|
||||
|
||||
public slots:
|
||||
void sendAvatarDataPacket();
|
||||
void sendIdentityPacket();
|
||||
|
@ -423,8 +420,6 @@ protected:
|
|||
// updates about one avatar to another.
|
||||
glm::vec3 _globalPosition;
|
||||
|
||||
bool _isDead { false };
|
||||
|
||||
private:
|
||||
friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar);
|
||||
static QUrl _defaultFullAvatarModelUrl;
|
||||
|
|
|
@ -122,13 +122,19 @@ void RenderablePolyLineEntityItem::updateVertices() {
|
|||
glm::vec3 v1, v2, tangent, binormal, point;
|
||||
|
||||
int finalIndex = minVectorSize - 1;
|
||||
|
||||
// Guard against an empty polyline
|
||||
if (finalIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < finalIndex; i++) {
|
||||
float width = _strokeWidths.at(i);
|
||||
point = _points.at(i);
|
||||
|
||||
tangent = _points.at(i);
|
||||
|
||||
tangent = _points.at(i + 1) - point;
|
||||
tangent = _points.at(i + 1) - point;
|
||||
glm::vec3 normal = _normals.at(i);
|
||||
binormal = glm::normalize(glm::cross(tangent, normal)) * width;
|
||||
|
||||
|
@ -141,11 +147,6 @@ void RenderablePolyLineEntityItem::updateVertices() {
|
|||
_vertices << v1 << v2;
|
||||
}
|
||||
|
||||
// Guard against an empty polyline
|
||||
if (finalIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For last point we can assume binormals are the same since it represents the last two vertices of quad
|
||||
point = _points.at(finalIndex);
|
||||
v1 = point + binormal;
|
||||
|
|
|
@ -77,7 +77,7 @@ public:
|
|||
glm::mat4 localToVoxelMatrix() const;
|
||||
|
||||
virtual ShapeType getShapeType() const;
|
||||
virtual bool shouldBePhysical() const { return true; }
|
||||
virtual bool shouldBePhysical() const { return !isDead(); }
|
||||
virtual bool isReadyToComputeShape();
|
||||
virtual void computeShapeInfo(ShapeInfo& info);
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ public:
|
|||
}
|
||||
|
||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
|
||||
virtual bool shouldBePhysical() const { return true; }
|
||||
virtual bool shouldBePhysical() const { return !isDead(); }
|
||||
|
||||
virtual void debugDump() const;
|
||||
|
||||
|
|
|
@ -302,7 +302,7 @@ public:
|
|||
|
||||
virtual bool contains(const glm::vec3& point) const;
|
||||
|
||||
virtual bool isReadyToComputeShape() { return true; }
|
||||
virtual bool isReadyToComputeShape() { return !isDead(); }
|
||||
virtual void computeShapeInfo(ShapeInfo& info);
|
||||
virtual float getVolumeEstimate() const { return getDimensions().x * getDimensions().y * getDimensions().z; }
|
||||
|
||||
|
@ -337,6 +337,8 @@ public:
|
|||
|
||||
bool isMoving() const;
|
||||
|
||||
bool isSimulated() const { return _simulated; }
|
||||
|
||||
void* getPhysicsInfo() const { return _physicsInfo; }
|
||||
|
||||
void setPhysicsInfo(void* data) { _physicsInfo = data; }
|
||||
|
@ -391,6 +393,8 @@ public:
|
|||
|
||||
protected:
|
||||
|
||||
void setSimulated(bool simulated) { _simulated = simulated; }
|
||||
|
||||
const QByteArray getActionDataInternal() const;
|
||||
void setActionDataInternal(QByteArray actionData);
|
||||
|
||||
|
|
|
@ -38,15 +38,35 @@ void EntitySimulation::updateEntities() {
|
|||
sortEntitiesThatMoved();
|
||||
}
|
||||
|
||||
void EntitySimulation::getEntitiesToDelete(VectorOfEntities& entitiesToDelete) {
|
||||
void EntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
for (auto entity : _entitiesToDelete) {
|
||||
// this entity is still in its tree, so we insert into the external list
|
||||
// push this entity onto the external list
|
||||
entitiesToDelete.push_back(entity);
|
||||
}
|
||||
_entitiesToDelete.clear();
|
||||
}
|
||||
|
||||
void EntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||
// remove from all internal lists except _entitiesToDelete
|
||||
_mortalEntities.remove(entity);
|
||||
_entitiesToUpdate.remove(entity);
|
||||
_entitiesToSort.remove(entity);
|
||||
_simpleKinematicEntities.remove(entity);
|
||||
_allEntities.remove(entity);
|
||||
entity->setSimulated(false);
|
||||
}
|
||||
|
||||
void EntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
|
||||
assert(entity);
|
||||
assert(entity->isDead());
|
||||
if (entity->isSimulated()) {
|
||||
entity->clearActions(this);
|
||||
removeEntityInternal(entity);
|
||||
_entitiesToDelete.insert(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void EntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||
if (entity->isMoving() && !entity->getPhysicsInfo()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
|
@ -71,15 +91,9 @@ void EntitySimulation::expireMortalEntities(const quint64& now) {
|
|||
EntityItemPointer entity = *itemItr;
|
||||
quint64 expiry = entity->getExpiry();
|
||||
if (expiry < now) {
|
||||
_entitiesToDelete.insert(entity);
|
||||
itemItr = _mortalEntities.erase(itemItr);
|
||||
_entitiesToUpdate.remove(entity);
|
||||
_entitiesToSort.remove(entity);
|
||||
_simpleKinematicEntities.remove(entity);
|
||||
removeEntityInternal(entity);
|
||||
|
||||
_allEntities.remove(entity);
|
||||
entity->_simulated = false;
|
||||
entity->die();
|
||||
prepareEntityForDelete(entity);
|
||||
} else {
|
||||
if (expiry < _nextExpiry) {
|
||||
// remeber the smallest _nextExpiry so we know when to start the next search
|
||||
|
@ -97,7 +111,7 @@ void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
|
|||
SetOfEntities::iterator itemItr = _entitiesToUpdate.begin();
|
||||
while (itemItr != _entitiesToUpdate.end()) {
|
||||
EntityItemPointer entity = *itemItr;
|
||||
// TODO: catch transition from needing update to not as a "change"
|
||||
// TODO: catch transition from needing update to not as a "change"
|
||||
// so we don't have to scan for it here.
|
||||
if (!entity->needsToCallUpdate()) {
|
||||
itemItr = _entitiesToUpdate.erase(itemItr);
|
||||
|
@ -123,16 +137,9 @@ void EntitySimulation::sortEntitiesThatMoved() {
|
|||
AACube newCube = entity->getQueryAACube(success);
|
||||
if (success && !domainBounds.touches(newCube)) {
|
||||
qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
|
||||
_entitiesToDelete.insert(entity);
|
||||
_mortalEntities.remove(entity);
|
||||
_entitiesToUpdate.remove(entity);
|
||||
_simpleKinematicEntities.remove(entity);
|
||||
removeEntityInternal(entity);
|
||||
|
||||
_allEntities.remove(entity);
|
||||
entity->_simulated = false;
|
||||
|
||||
itemItr = _entitiesToSort.erase(itemItr);
|
||||
entity->die();
|
||||
prepareEntityForDelete(entity);
|
||||
} else {
|
||||
moveOperator.addEntityToMoveList(entity, newCube);
|
||||
++itemItr;
|
||||
|
@ -163,39 +170,25 @@ void EntitySimulation::addEntity(EntityItemPointer entity) {
|
|||
addEntityInternal(entity);
|
||||
|
||||
_allEntities.insert(entity);
|
||||
entity->_simulated = true;
|
||||
entity->setSimulated(true);
|
||||
|
||||
// DirtyFlags are used to signal changes to entities that have already been added,
|
||||
// DirtyFlags are used to signal changes to entities that have already been added,
|
||||
// so we can clear them for this entity which has just been added.
|
||||
entity->clearDirtyFlags();
|
||||
}
|
||||
|
||||
void EntitySimulation::removeEntity(EntityItemPointer entity) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
assert(entity);
|
||||
_entitiesToUpdate.remove(entity);
|
||||
_mortalEntities.remove(entity);
|
||||
_entitiesToSort.remove(entity);
|
||||
_simpleKinematicEntities.remove(entity);
|
||||
_entitiesToDelete.remove(entity);
|
||||
removeEntityInternal(entity);
|
||||
|
||||
_allEntities.remove(entity);
|
||||
entity->_simulated = false;
|
||||
}
|
||||
|
||||
void EntitySimulation::changeEntity(EntityItemPointer entity) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
assert(entity);
|
||||
if (!entity->_simulated) {
|
||||
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
|
||||
// (probably for pending delete), so we don't want to keep a pointer to it
|
||||
// on any internal lists.
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
bool wasRemoved = false;
|
||||
uint32_t dirtyFlags = entity->getDirtyFlags();
|
||||
|
@ -205,13 +198,8 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) {
|
|||
AACube newCube = entity->getQueryAACube(success);
|
||||
if (success && !domainBounds.touches(newCube)) {
|
||||
qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
|
||||
_entitiesToDelete.insert(entity);
|
||||
_mortalEntities.remove(entity);
|
||||
_entitiesToUpdate.remove(entity);
|
||||
_entitiesToSort.remove(entity);
|
||||
_simpleKinematicEntities.remove(entity);
|
||||
removeEntityInternal(entity);
|
||||
entity->_simulated = false;
|
||||
entity->die();
|
||||
prepareEntityForDelete(entity);
|
||||
wasRemoved = true;
|
||||
}
|
||||
}
|
||||
|
@ -244,14 +232,15 @@ void EntitySimulation::clearEntities() {
|
|||
_entitiesToUpdate.clear();
|
||||
_entitiesToSort.clear();
|
||||
_simpleKinematicEntities.clear();
|
||||
_entitiesToDelete.clear();
|
||||
|
||||
clearEntitiesInternal();
|
||||
|
||||
for (auto entityItr : _allEntities) {
|
||||
entityItr->_simulated = false;
|
||||
for (auto entity : _allEntities) {
|
||||
entity->setSimulated(false);
|
||||
entity->die();
|
||||
}
|
||||
_allEntities.clear();
|
||||
_entitiesToDelete.clear();
|
||||
}
|
||||
|
||||
void EntitySimulation::moveSimpleKinematics(const quint64& now) {
|
||||
|
|
|
@ -63,25 +63,20 @@ public:
|
|||
/// \sideeffect sets relevant backpointers in entity, but maybe later when appropriate data structures are locked
|
||||
void addEntity(EntityItemPointer entity);
|
||||
|
||||
/// \param entity pointer to EntityItem to be removed
|
||||
/// \brief the actual removal may happen later when appropriate data structures are locked
|
||||
/// \sideeffect nulls relevant backpointers in entity
|
||||
void removeEntity(EntityItemPointer entity);
|
||||
|
||||
/// \param entity pointer to EntityItem to that may have changed in a way that would affect its simulation
|
||||
/// \param entity pointer to EntityItem that may have changed in a way that would affect its simulation
|
||||
/// call this whenever an entity was changed from some EXTERNAL event (NOT by the EntitySimulation itself)
|
||||
void changeEntity(EntityItemPointer entity);
|
||||
|
||||
void clearEntities();
|
||||
|
||||
void moveSimpleKinematics(const quint64& now);
|
||||
protected: // these only called by the EntityTree?
|
||||
|
||||
public:
|
||||
|
||||
EntityTreePointer getEntityTree() { return _entityTree; }
|
||||
|
||||
void getEntitiesToDelete(VectorOfEntities& entitiesToDelete);
|
||||
virtual void takeEntitiesToDelete(VectorOfEntities& entitiesToDelete);
|
||||
|
||||
/// \param entity pointer to EntityItem that needs to be put on the entitiesToDelete list and removed from others.
|
||||
virtual void prepareEntityForDelete(EntityItemPointer entity);
|
||||
|
||||
signals:
|
||||
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||
|
@ -106,6 +101,9 @@ protected:
|
|||
QList<EntityActionPointer> _actionsToAdd;
|
||||
QSet<QUuid> _actionsToRemove;
|
||||
|
||||
protected:
|
||||
SetOfEntities _entitiesToDelete; // entities simulation decided needed to be deleted (EntityTree will actually delete)
|
||||
|
||||
private:
|
||||
void moveSimpleKinematics();
|
||||
|
||||
|
@ -120,7 +118,6 @@ private:
|
|||
|
||||
|
||||
SetOfEntities _entitiesToUpdate; // entities that need to call EntityItem::update()
|
||||
SetOfEntities _entitiesToDelete; // entities simulation decided needed to be deleted (EntityTree will actually delete)
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -442,6 +442,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
|
|||
const RemovedEntities& entities = theOperator.getEntities();
|
||||
foreach(const EntityToDeleteDetails& details, entities) {
|
||||
EntityItemPointer theEntity = details.entity;
|
||||
theEntity->die();
|
||||
|
||||
if (getIsServer()) {
|
||||
// set up the deleted entities ID
|
||||
|
@ -453,8 +454,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
|
|||
}
|
||||
|
||||
if (_simulation) {
|
||||
theEntity->clearActions(_simulation);
|
||||
_simulation->removeEntity(theEntity);
|
||||
_simulation->prepareEntityForDelete(theEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1006,7 +1006,7 @@ void EntityTree::update() {
|
|||
withWriteLock([&] {
|
||||
_simulation->updateEntities();
|
||||
VectorOfEntities pendingDeletes;
|
||||
_simulation->getEntitiesToDelete(pendingDeletes);
|
||||
_simulation->takeEntitiesToDelete(pendingDeletes);
|
||||
|
||||
if (pendingDeletes.size() > 0) {
|
||||
// translate into list of ID's
|
||||
|
|
|
@ -377,7 +377,7 @@ void ModelEntityItem::setAnimationFPS(float value) {
|
|||
|
||||
// virtual
|
||||
bool ModelEntityItem::shouldBePhysical() const {
|
||||
return getShapeType() != SHAPE_TYPE_NONE;
|
||||
return !isDead() && getShapeType() != SHAPE_TYPE_NONE;
|
||||
}
|
||||
|
||||
void ModelEntityItem::resizeJointArrays(int newSize) {
|
||||
|
|
|
@ -53,6 +53,7 @@ void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
|||
}
|
||||
|
||||
void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||
EntitySimulation::removeEntityInternal(entity);
|
||||
_entitiesWithSimulator.remove(entity);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,11 +22,11 @@ public:
|
|||
virtual ~SimpleEntitySimulation() { clearEntitiesInternal(); }
|
||||
|
||||
protected:
|
||||
virtual void updateEntitiesInternal(const quint64& now);
|
||||
virtual void addEntityInternal(EntityItemPointer entity);
|
||||
virtual void removeEntityInternal(EntityItemPointer entity);
|
||||
virtual void changeEntityInternal(EntityItemPointer entity);
|
||||
virtual void clearEntitiesInternal();
|
||||
virtual void updateEntitiesInternal(const quint64& now) override;
|
||||
virtual void addEntityInternal(EntityItemPointer entity) override;
|
||||
virtual void removeEntityInternal(EntityItemPointer entity) override;
|
||||
virtual void changeEntityInternal(EntityItemPointer entity) override;
|
||||
virtual void clearEntitiesInternal() override;
|
||||
|
||||
SetOfEntities _entitiesWithSimulator;
|
||||
};
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
}
|
||||
|
||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_SPHERE; }
|
||||
virtual bool shouldBePhysical() const { return true; }
|
||||
virtual bool shouldBePhysical() const { return !isDead(); }
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
static bool getDrawZoneBoundaries() { return _drawZoneBoundaries; }
|
||||
static void setDrawZoneBoundaries(bool value) { _drawZoneBoundaries = value; }
|
||||
|
||||
virtual bool isReadyToComputeShape() { return true; }
|
||||
virtual bool isReadyToComputeShape() { return false; }
|
||||
void updateShapeType(ShapeType type) { _shapeType = type; }
|
||||
virtual ShapeType getShapeType() const;
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5;
|
|||
|
||||
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
|
||||
bool EntityMotionState::entityTreeIsLocked() const {
|
||||
EntityTreeElementPointer element = _entity ? _entity->getElement() : nullptr;
|
||||
EntityTreeElementPointer element = _entity->getElement();
|
||||
EntityTreePointer tree = element ? element->getTree() : nullptr;
|
||||
if (!tree) {
|
||||
return true;
|
||||
|
@ -50,7 +50,8 @@ bool entityTreeIsLocked() {
|
|||
|
||||
EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer entity) :
|
||||
ObjectMotionState(shape),
|
||||
_entity(entity),
|
||||
_entityPtr(entity),
|
||||
_entity(entity.get()),
|
||||
_sentInactive(true),
|
||||
_lastStep(0),
|
||||
_serverPosition(0.0f),
|
||||
|
@ -69,14 +70,14 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
|
|||
_loopsWithoutOwner(0)
|
||||
{
|
||||
_type = MOTIONSTATE_TYPE_ENTITY;
|
||||
assert(_entity != nullptr);
|
||||
assert(_entity);
|
||||
assert(entityTreeIsLocked());
|
||||
setMass(_entity->computeMass());
|
||||
}
|
||||
|
||||
EntityMotionState::~EntityMotionState() {
|
||||
// be sure to clear _entity before calling the destructor
|
||||
assert(!_entity);
|
||||
assert(_entity);
|
||||
_entity = nullptr;
|
||||
}
|
||||
|
||||
void EntityMotionState::updateServerPhysicsVariables(const QUuid& sessionID) {
|
||||
|
@ -138,11 +139,6 @@ bool EntityMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine*
|
|||
return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
|
||||
}
|
||||
|
||||
void EntityMotionState::clearObjectBackPointer() {
|
||||
ObjectMotionState::clearObjectBackPointer();
|
||||
_entity = nullptr;
|
||||
}
|
||||
|
||||
MotionType EntityMotionState::computeObjectMotionType() const {
|
||||
if (!_entity) {
|
||||
return MOTION_TYPE_STATIC;
|
||||
|
@ -221,22 +217,16 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
|||
|
||||
|
||||
// virtual and protected
|
||||
bool EntityMotionState::isReadyToComputeShape() {
|
||||
if (_entity) {
|
||||
return _entity->isReadyToComputeShape();
|
||||
}
|
||||
return false;
|
||||
bool EntityMotionState::isReadyToComputeShape() const {
|
||||
return _entity->isReadyToComputeShape();
|
||||
}
|
||||
|
||||
// virtual and protected
|
||||
btCollisionShape* EntityMotionState::computeNewShape() {
|
||||
if (_entity) {
|
||||
ShapeInfo shapeInfo;
|
||||
assert(entityTreeIsLocked());
|
||||
_entity->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
return nullptr;
|
||||
ShapeInfo shapeInfo;
|
||||
assert(entityTreeIsLocked());
|
||||
_entity->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
|
||||
bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const {
|
||||
|
@ -553,26 +543,17 @@ void EntityMotionState::clearIncomingDirtyFlags() {
|
|||
|
||||
// virtual
|
||||
quint8 EntityMotionState::getSimulationPriority() const {
|
||||
if (_entity) {
|
||||
return _entity->getSimulationPriority();
|
||||
}
|
||||
return NO_PRORITY;
|
||||
return _entity->getSimulationPriority();
|
||||
}
|
||||
|
||||
// virtual
|
||||
QUuid EntityMotionState::getSimulatorID() const {
|
||||
if (_entity) {
|
||||
assert(entityTreeIsLocked());
|
||||
return _entity->getSimulatorID();
|
||||
}
|
||||
return QUuid();
|
||||
assert(entityTreeIsLocked());
|
||||
return _entity->getSimulatorID();
|
||||
}
|
||||
|
||||
// virtual
|
||||
void EntityMotionState::bump(quint8 priority) {
|
||||
if (_entity) {
|
||||
setOutgoingPriority(glm::max(VOLUNTEER_SIMULATION_PRIORITY, --priority));
|
||||
}
|
||||
setOutgoingPriority(glm::max(VOLUNTEER_SIMULATION_PRIORITY, --priority));
|
||||
}
|
||||
|
||||
void EntityMotionState::resetMeasuredBodyAcceleration() {
|
||||
|
@ -623,19 +604,13 @@ void EntityMotionState::setMotionType(MotionType motionType) {
|
|||
|
||||
|
||||
// virtual
|
||||
QString EntityMotionState::getName() {
|
||||
if (_entity) {
|
||||
assert(entityTreeIsLocked());
|
||||
return _entity->getName();
|
||||
}
|
||||
return "";
|
||||
QString EntityMotionState::getName() const {
|
||||
assert(entityTreeIsLocked());
|
||||
return _entity->getName();
|
||||
}
|
||||
|
||||
// virtual
|
||||
int16_t EntityMotionState::computeCollisionGroup() {
|
||||
if (!_entity) {
|
||||
return COLLISION_GROUP_STATIC;
|
||||
}
|
||||
int16_t EntityMotionState::computeCollisionGroup() const {
|
||||
if (_entity->getIgnoreForCollisions()) {
|
||||
return COLLISION_GROUP_COLLISIONLESS;
|
||||
}
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
#ifndef hifi_EntityMotionState_h
|
||||
#define hifi_EntityMotionState_h
|
||||
|
||||
#include <EntityTypes.h>
|
||||
#include <AACube.h>
|
||||
|
||||
#include "ObjectMotionState.h"
|
||||
|
||||
class EntityItem;
|
||||
|
||||
// From the MotionState's perspective:
|
||||
// Inside = physics simulation
|
||||
|
@ -38,10 +38,10 @@ public:
|
|||
virtual bool isMoving() const;
|
||||
|
||||
// this relays incoming position/rotation to the RigidBody
|
||||
virtual void getWorldTransform(btTransform& worldTrans) const;
|
||||
virtual void getWorldTransform(btTransform& worldTrans) const override;
|
||||
|
||||
// this relays outgoing position/rotation to the EntityItem
|
||||
virtual void setWorldTransform(const btTransform& worldTrans);
|
||||
virtual void setWorldTransform(const btTransform& worldTrans) override;
|
||||
|
||||
bool isCandidateForOwnership(const QUuid& sessionID) const;
|
||||
bool remoteSimulationOutOfSync(uint32_t simulationStep);
|
||||
|
@ -55,32 +55,32 @@ public:
|
|||
void resetAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount = 0; }
|
||||
quint8 getAccelerationNearlyGravityCount() { return _accelerationNearlyGravityCount; }
|
||||
|
||||
virtual float getObjectRestitution() const { return _entity->getRestitution(); }
|
||||
virtual float getObjectFriction() const { return _entity->getFriction(); }
|
||||
virtual float getObjectLinearDamping() const { return _entity->getDamping(); }
|
||||
virtual float getObjectAngularDamping() const { return _entity->getAngularDamping(); }
|
||||
virtual float getObjectRestitution() const override { return _entity->getRestitution(); }
|
||||
virtual float getObjectFriction() const override { return _entity->getFriction(); }
|
||||
virtual float getObjectLinearDamping() const override { return _entity->getDamping(); }
|
||||
virtual float getObjectAngularDamping() const override { return _entity->getAngularDamping(); }
|
||||
|
||||
virtual glm::vec3 getObjectPosition() const { return _entity->getPosition() - ObjectMotionState::getWorldOffset(); }
|
||||
virtual glm::quat getObjectRotation() const { return _entity->getRotation(); }
|
||||
virtual glm::vec3 getObjectLinearVelocity() const { return _entity->getVelocity(); }
|
||||
virtual glm::vec3 getObjectAngularVelocity() const { return _entity->getAngularVelocity(); }
|
||||
virtual glm::vec3 getObjectGravity() const { return _entity->getGravity(); }
|
||||
virtual glm::vec3 getObjectLinearVelocityChange() const;
|
||||
virtual glm::vec3 getObjectPosition() const override { return _entity->getPosition() - ObjectMotionState::getWorldOffset(); }
|
||||
virtual glm::quat getObjectRotation() const override { return _entity->getRotation(); }
|
||||
virtual glm::vec3 getObjectLinearVelocity() const override { return _entity->getVelocity(); }
|
||||
virtual glm::vec3 getObjectAngularVelocity() const override { return _entity->getAngularVelocity(); }
|
||||
virtual glm::vec3 getObjectGravity() const override { return _entity->getGravity(); }
|
||||
virtual glm::vec3 getObjectLinearVelocityChange() const override;
|
||||
|
||||
virtual const QUuid& getObjectID() const { return _entity->getID(); }
|
||||
virtual const QUuid& getObjectID() const override { return _entity->getID(); }
|
||||
|
||||
virtual quint8 getSimulationPriority() const;
|
||||
virtual QUuid getSimulatorID() const;
|
||||
virtual void bump(quint8 priority);
|
||||
virtual quint8 getSimulationPriority() const override;
|
||||
virtual QUuid getSimulatorID() const override;
|
||||
virtual void bump(quint8 priority) override;
|
||||
|
||||
EntityItemPointer getEntity() const { return _entity; }
|
||||
EntityItemPointer getEntity() const { return _entityPtr.lock(); }
|
||||
|
||||
void resetMeasuredBodyAcceleration();
|
||||
void measureBodyAcceleration();
|
||||
|
||||
virtual QString getName();
|
||||
virtual QString getName() const override;
|
||||
|
||||
virtual int16_t computeCollisionGroup();
|
||||
virtual int16_t computeCollisionGroup() const override;
|
||||
|
||||
// eternal logic can suggest a simuator priority bid for the next outgoing update
|
||||
void setOutgoingPriority(quint8 priority);
|
||||
|
@ -92,12 +92,19 @@ protected:
|
|||
bool entityTreeIsLocked() const;
|
||||
#endif
|
||||
|
||||
virtual bool isReadyToComputeShape();
|
||||
virtual bool isReadyToComputeShape() const override;
|
||||
virtual btCollisionShape* computeNewShape();
|
||||
virtual void clearObjectBackPointer();
|
||||
virtual void setMotionType(MotionType motionType);
|
||||
|
||||
EntityItemPointer _entity;
|
||||
// In the glorious future (when entities lib depends on physics lib) the EntityMotionState will be
|
||||
// properly "owned" by the EntityItem and will be deleted by it in the dtor. In pursuit of that
|
||||
// state of affairs we can't keep a real EntityItemPointer as data member (it would produce a
|
||||
// recursive dependency). Instead we keep a EntityItemWeakPointer to break that dependency while
|
||||
// still granting us the capability to generate EntityItemPointers as necessary (for external data
|
||||
// structures that use the MotionState to get to the EntityItem).
|
||||
EntityItemWeakPointer _entityPtr;
|
||||
// Meanwhile we also keep a raw EntityItem* for internal stuff where the pointer is guaranteed valid.
|
||||
EntityItem* _entity;
|
||||
|
||||
bool _sentInactive; // true if body was inactive when we sent last update
|
||||
|
||||
|
|
|
@ -72,7 +72,8 @@ ObjectMotionState::ObjectMotionState(btCollisionShape* shape) :
|
|||
|
||||
ObjectMotionState::~ObjectMotionState() {
|
||||
assert(!_body);
|
||||
assert(!_shape);
|
||||
releaseShape();
|
||||
_type = MOTIONSTATE_TYPE_INVALID;
|
||||
}
|
||||
|
||||
void ObjectMotionState::setBodyLinearVelocity(const glm::vec3& velocity) const {
|
||||
|
|
|
@ -134,9 +134,9 @@ public:
|
|||
virtual QUuid getSimulatorID() const = 0;
|
||||
virtual void bump(quint8 priority) {}
|
||||
|
||||
virtual QString getName() { return ""; }
|
||||
virtual QString getName() const { return ""; }
|
||||
|
||||
virtual int16_t computeCollisionGroup() = 0;
|
||||
virtual int16_t computeCollisionGroup() const = 0;
|
||||
|
||||
bool isActive() const { return _body ? _body->isActive() : false; }
|
||||
|
||||
|
@ -148,14 +148,11 @@ public:
|
|||
friend class PhysicsEngine;
|
||||
|
||||
protected:
|
||||
virtual bool isReadyToComputeShape() = 0;
|
||||
virtual bool isReadyToComputeShape() const = 0;
|
||||
virtual btCollisionShape* computeNewShape() = 0;
|
||||
void setMotionType(MotionType motionType);
|
||||
void updateCCDConfiguration();
|
||||
|
||||
// clearObjectBackPointer() overrrides should call the base method, then actually clear the object back pointer.
|
||||
virtual void clearObjectBackPointer() { _type = MOTIONSTATE_TYPE_INVALID; }
|
||||
|
||||
void setRigidBody(btRigidBody* body);
|
||||
|
||||
MotionStateType _type = MOTIONSTATE_TYPE_INVALID; // type of MotionState
|
||||
|
|
|
@ -44,10 +44,11 @@ void PhysicalEntitySimulation::updateEntitiesInternal(const quint64& now) {
|
|||
|
||||
void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||
assert(entity);
|
||||
assert(!entity->isDead());
|
||||
if (entity->shouldBePhysical()) {
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (!motionState) {
|
||||
_pendingAdds.insert(entity);
|
||||
_entitiesToAddToPhysics.insert(entity);
|
||||
}
|
||||
} else if (entity->isMoving()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
|
@ -55,14 +56,33 @@ void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
|||
}
|
||||
|
||||
void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||
EntitySimulation::removeEntityInternal(entity);
|
||||
_entitiesToAddToPhysics.remove(entity);
|
||||
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
motionState->clearObjectBackPointer();
|
||||
entity->setPhysicsInfo(nullptr);
|
||||
_pendingRemoves.insert(motionState);
|
||||
_outgoingChanges.remove(motionState);
|
||||
_entitiesToRemoveFromPhysics.insert(entity);
|
||||
} else {
|
||||
_entitiesToDelete.insert(entity);
|
||||
}
|
||||
_pendingAdds.remove(entity);
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
for (auto entity : _entitiesToDelete) {
|
||||
// this entity is still in its tree, so we insert into the external list
|
||||
entitiesToDelete.push_back(entity);
|
||||
|
||||
// Someday when we invert the entities/physics lib dependencies we can let EntityItem delete its own PhysicsInfo
|
||||
// rather than do it here
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
delete motionState;
|
||||
entity->setPhysicsInfo(nullptr);
|
||||
}
|
||||
}
|
||||
_entitiesToDelete.clear();
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||
|
@ -74,8 +94,8 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
|||
// the entity should be removed from the physical simulation
|
||||
_pendingChanges.remove(motionState);
|
||||
_physicalObjects.remove(motionState);
|
||||
_pendingRemoves.insert(motionState);
|
||||
_outgoingChanges.remove(motionState);
|
||||
_entitiesToRemoveFromPhysics.insert(entity);
|
||||
if (entity->isMoving()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
}
|
||||
|
@ -85,7 +105,7 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
|||
} else if (entity->shouldBePhysical()) {
|
||||
// The intent is for this object to be in the PhysicsEngine, but it has no MotionState yet.
|
||||
// Perhaps it's shape has changed and it can now be added?
|
||||
_pendingAdds.insert(entity);
|
||||
_entitiesToAddToPhysics.insert(entity);
|
||||
_simpleKinematicEntities.remove(entity); // just in case it's non-physical-kinematic
|
||||
} else if (entity->isMoving()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
|
@ -102,55 +122,70 @@ void PhysicalEntitySimulation::clearEntitiesInternal() {
|
|||
// first disconnect each MotionStates from its Entity
|
||||
for (auto stateItr : _physicalObjects) {
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(&(*stateItr));
|
||||
EntityItemPointer entity = motionState->getEntity();
|
||||
if (entity) {
|
||||
entity->setPhysicsInfo(nullptr);
|
||||
}
|
||||
motionState->clearObjectBackPointer();
|
||||
_entitiesToDelete.insert(motionState->getEntity());
|
||||
}
|
||||
|
||||
// then delete the objects (aka MotionStates)
|
||||
_physicsEngine->deleteObjects(_physicalObjects);
|
||||
// then remove the objects (aka MotionStates) from physics
|
||||
_physicsEngine->removeObjects(_physicalObjects);
|
||||
|
||||
// finally clear all lists (which now have only dangling pointers)
|
||||
// delete the MotionStates
|
||||
// TODO: after we invert the entities/physics lib dependencies we will let EntityItem delete
|
||||
// its own PhysicsInfo rather than do it here
|
||||
for (auto entity : _entitiesToDelete) {
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
delete motionState;
|
||||
entity->setPhysicsInfo(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// finally clear all lists maintained by this class
|
||||
_physicalObjects.clear();
|
||||
_pendingRemoves.clear();
|
||||
_pendingAdds.clear();
|
||||
_entitiesToRemoveFromPhysics.clear();
|
||||
_entitiesToAddToPhysics.clear();
|
||||
_pendingChanges.clear();
|
||||
_outgoingChanges.clear();
|
||||
}
|
||||
|
||||
// virtual
|
||||
void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
|
||||
assert(entity);
|
||||
assert(entity->isDead());
|
||||
entity->clearActions(this);
|
||||
removeEntityInternal(entity);
|
||||
}
|
||||
// end EntitySimulation overrides
|
||||
|
||||
|
||||
void PhysicalEntitySimulation::getObjectsToDelete(VectorOfMotionStates& result) {
|
||||
void PhysicalEntitySimulation::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
QMutexLocker lock(&_mutex);
|
||||
for (auto stateItr : _pendingRemoves) {
|
||||
EntityMotionState* motionState = &(*stateItr);
|
||||
_pendingChanges.remove(motionState);
|
||||
_physicalObjects.remove(motionState);
|
||||
|
||||
EntityItemPointer entity = motionState->getEntity();
|
||||
if (entity) {
|
||||
_pendingAdds.remove(entity);
|
||||
entity->setPhysicsInfo(nullptr);
|
||||
motionState->clearObjectBackPointer();
|
||||
for (auto entity: _entitiesToRemoveFromPhysics) {
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
_pendingChanges.remove(motionState);
|
||||
_physicalObjects.remove(motionState);
|
||||
result.push_back(motionState);
|
||||
}
|
||||
_entitiesToAddToPhysics.remove(entity);
|
||||
if (entity->isDead()) {
|
||||
_entitiesToDelete.insert(entity);
|
||||
}
|
||||
result.push_back(motionState);
|
||||
}
|
||||
_pendingRemoves.clear();
|
||||
_entitiesToRemoveFromPhysics.clear();
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::getObjectsToAdd(VectorOfMotionStates& result) {
|
||||
void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
QMutexLocker lock(&_mutex);
|
||||
SetOfEntities::iterator entityItr = _pendingAdds.begin();
|
||||
while (entityItr != _pendingAdds.end()) {
|
||||
EntityItemPointer entity = *entityItr;
|
||||
SetOfEntities::iterator entityItr = _entitiesToAddToPhysics.begin();
|
||||
while (entityItr != _entitiesToAddToPhysics.end()) {
|
||||
EntityItemPointer entity = (*entityItr);
|
||||
assert(!entity->getPhysicsInfo());
|
||||
if (!entity->shouldBePhysical()) {
|
||||
// this entity should no longer be on the internal _pendingAdds
|
||||
entityItr = _pendingAdds.erase(entityItr);
|
||||
if (entity->isDead()) {
|
||||
prepareEntityForDelete(entity);
|
||||
} else if (!entity->shouldBePhysical()) {
|
||||
// this entity should no longer be on the internal _entitiesToAddToPhysics
|
||||
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
||||
if (entity->isMoving()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
}
|
||||
|
@ -163,7 +198,7 @@ void PhysicalEntitySimulation::getObjectsToAdd(VectorOfMotionStates& result) {
|
|||
entity->setPhysicsInfo(static_cast<void*>(motionState));
|
||||
_physicalObjects.insert(motionState);
|
||||
result.push_back(motionState);
|
||||
entityItr = _pendingAdds.erase(entityItr);
|
||||
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
||||
} else {
|
||||
//qDebug() << "Warning! Failed to generate new shape for entity." << entity->getName();
|
||||
++entityItr;
|
||||
|
@ -199,12 +234,11 @@ void PhysicalEntitySimulation::handleOutgoingChanges(const VectorOfMotionStates&
|
|||
if (state && state->getType() == MOTIONSTATE_TYPE_ENTITY) {
|
||||
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
||||
EntityItemPointer entity = entityState->getEntity();
|
||||
if (entity) {
|
||||
if (entityState->isCandidateForOwnership(sessionID)) {
|
||||
_outgoingChanges.insert(entityState);
|
||||
}
|
||||
_entitiesToSort.insert(entityState->getEntity());
|
||||
assert(entity.get());
|
||||
if (entityState->isCandidateForOwnership(sessionID)) {
|
||||
_outgoingChanges.insert(entityState);
|
||||
}
|
||||
_entitiesToSort.insert(entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ public:
|
|||
virtual void addAction(EntityActionPointer action) override;
|
||||
virtual void applyActionChanges() override;
|
||||
|
||||
virtual void takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) override;
|
||||
|
||||
protected: // only called by EntitySimulation
|
||||
// overrides for EntitySimulation
|
||||
virtual void updateEntitiesInternal(const quint64& now) override;
|
||||
|
@ -44,8 +46,10 @@ protected: // only called by EntitySimulation
|
|||
virtual void clearEntitiesInternal() override;
|
||||
|
||||
public:
|
||||
void getObjectsToDelete(VectorOfMotionStates& result);
|
||||
void getObjectsToAdd(VectorOfMotionStates& result);
|
||||
virtual void prepareEntityForDelete(EntityItemPointer entity) override;
|
||||
|
||||
void getObjectsToRemoveFromPhysics(VectorOfMotionStates& result);
|
||||
void getObjectsToAddToPhysics(VectorOfMotionStates& result);
|
||||
void setObjectsToChange(const VectorOfMotionStates& objectsToChange);
|
||||
void getObjectsToChange(VectorOfMotionStates& result);
|
||||
|
||||
|
@ -55,12 +59,10 @@ public:
|
|||
EntityEditPacketSender* getPacketSender() { return _entityPacketSender; }
|
||||
|
||||
private:
|
||||
// incoming changes
|
||||
SetOfEntityMotionStates _pendingRemoves; // EntityMotionStates to be removed from PhysicsEngine (and deleted)
|
||||
SetOfEntities _pendingAdds; // entities to be be added to PhysicsEngine (and a their EntityMotionState created)
|
||||
SetOfEntityMotionStates _pendingChanges; // EntityMotionStates already in PhysicsEngine that need their physics changed
|
||||
SetOfEntities _entitiesToRemoveFromPhysics;
|
||||
SetOfEntities _entitiesToAddToPhysics;
|
||||
|
||||
// outgoing changes
|
||||
SetOfEntityMotionStates _pendingChanges; // EntityMotionStates already in PhysicsEngine that need their physics changed
|
||||
SetOfEntityMotionStates _outgoingChanges; // EntityMotionStates for which we need to send updates to entity-server
|
||||
|
||||
SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine
|
||||
|
|
|
@ -67,7 +67,8 @@ void PhysicsEngine::init() {
|
|||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::addObject(ObjectMotionState* motionState) {
|
||||
// private
|
||||
void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
||||
assert(motionState);
|
||||
|
||||
btVector3 inertia(0.0f, 0.0f, 0.0f);
|
||||
|
@ -144,7 +145,8 @@ void PhysicsEngine::addObject(ObjectMotionState* motionState) {
|
|||
motionState->clearIncomingDirtyFlags();
|
||||
}
|
||||
|
||||
void PhysicsEngine::removeObject(ObjectMotionState* object) {
|
||||
// private
|
||||
void PhysicsEngine::removeObjectFromDynamicsWorld(ObjectMotionState* object) {
|
||||
// wake up anything touching this object
|
||||
bump(object);
|
||||
removeContacts(object);
|
||||
|
@ -154,38 +156,34 @@ void PhysicsEngine::removeObject(ObjectMotionState* object) {
|
|||
_dynamicsWorld->removeRigidBody(body);
|
||||
}
|
||||
|
||||
void PhysicsEngine::deleteObjects(const VectorOfMotionStates& objects) {
|
||||
void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) {
|
||||
for (auto object : objects) {
|
||||
removeObject(object);
|
||||
removeObjectFromDynamicsWorld(object);
|
||||
|
||||
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
object->setRigidBody(nullptr);
|
||||
body->setMotionState(nullptr);
|
||||
delete body;
|
||||
object->releaseShape();
|
||||
delete object;
|
||||
}
|
||||
}
|
||||
|
||||
// Same as above, but takes a Set instead of a Vector. Should only be called during teardown.
|
||||
void PhysicsEngine::deleteObjects(const SetOfMotionStates& objects) {
|
||||
void PhysicsEngine::removeObjects(const SetOfMotionStates& objects) {
|
||||
for (auto object : objects) {
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
removeObject(object);
|
||||
removeObjectFromDynamicsWorld(object);
|
||||
|
||||
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
|
||||
object->setRigidBody(nullptr);
|
||||
body->setMotionState(nullptr);
|
||||
delete body;
|
||||
object->releaseShape();
|
||||
delete object;
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::addObjects(const VectorOfMotionStates& objects) {
|
||||
for (auto object : objects) {
|
||||
addObject(object);
|
||||
addObjectToDynamicsWorld(object);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,8 +209,8 @@ VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& ob
|
|||
}
|
||||
|
||||
void PhysicsEngine::reinsertObject(ObjectMotionState* object) {
|
||||
removeObject(object);
|
||||
addObject(object);
|
||||
removeObjectFromDynamicsWorld(object);
|
||||
addObjectToDynamicsWorld(object);
|
||||
}
|
||||
|
||||
void PhysicsEngine::removeContacts(ObjectMotionState* motionState) {
|
||||
|
|
|
@ -54,11 +54,9 @@ public:
|
|||
void setSessionUUID(const QUuid& sessionID) { _sessionID = sessionID; }
|
||||
const QUuid& getSessionID() const { return _sessionID; }
|
||||
|
||||
void addObject(ObjectMotionState* motionState);
|
||||
void removeObject(ObjectMotionState* motionState);
|
||||
void removeObjects(const VectorOfMotionStates& objects);
|
||||
void removeObjects(const SetOfMotionStates& objects); // only called during teardown
|
||||
|
||||
void deleteObjects(const VectorOfMotionStates& objects);
|
||||
void deleteObjects(const SetOfMotionStates& objects); // only called during teardown
|
||||
void addObjects(const VectorOfMotionStates& objects);
|
||||
VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects);
|
||||
void reinsertObject(ObjectMotionState* object);
|
||||
|
@ -86,8 +84,6 @@ public:
|
|||
/// \brief call bump on any objects that touch the object corresponding to motionState
|
||||
void bump(ObjectMotionState* motionState);
|
||||
|
||||
void removeRigidBody(btRigidBody* body);
|
||||
|
||||
void setCharacterController(CharacterController* character);
|
||||
|
||||
void dumpNextStats() { _dumpNextStats = true; }
|
||||
|
@ -100,6 +96,9 @@ public:
|
|||
void forEachAction(std::function<void(EntityActionPointer)> actor);
|
||||
|
||||
private:
|
||||
void addObjectToDynamicsWorld(ObjectMotionState* motionState);
|
||||
void removeObjectFromDynamicsWorld(ObjectMotionState* motionState);
|
||||
|
||||
void removeContacts(ObjectMotionState* motionState);
|
||||
|
||||
void doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB);
|
||||
|
@ -116,7 +115,6 @@ private:
|
|||
|
||||
ContactMap _contactMap;
|
||||
uint32_t _numContactFrames = 0;
|
||||
uint32_t _lastNumSubstepsAtUpdateInternal = 0;
|
||||
|
||||
/// character collisions
|
||||
CharacterController* _myAvatarController;
|
||||
|
|
|
@ -115,6 +115,9 @@ public:
|
|||
void forEachChild(std::function<void(SpatiallyNestablePointer)> actor);
|
||||
void forEachDescendant(std::function<void(SpatiallyNestablePointer)> actor);
|
||||
|
||||
void die() { _isDead = true; }
|
||||
bool isDead() const { return _isDead; }
|
||||
|
||||
protected:
|
||||
const NestableType _nestableType; // EntityItem or an AvatarData
|
||||
QUuid _id;
|
||||
|
@ -141,7 +144,8 @@ protected:
|
|||
private:
|
||||
mutable ReadWriteLockable _transformLock;
|
||||
Transform _transform; // this is to be combined with parent's world-transform to produce this' world-transform.
|
||||
mutable bool _parentKnowsMe = false;
|
||||
mutable bool _parentKnowsMe { false };
|
||||
bool _isDead { false };
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue