HifiExperimentsfix lifetime in serverlessa295cf71 year agoHistory
703 lines · 29.4 KB · C++
RawDownload
////  PhysicalEntitySimulation.cpp//  libraries/physics/src////  Created by Andrew Meadows 2015.04.27//  Copyright 2015 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//  #include "PhysicalEntitySimulation.h" #include <Profile.h> #include "PhysicsHelpers.h"#include "PhysicsLogging.h"#include "ShapeManager.h"  PhysicalEntitySimulation::PhysicalEntitySimulation() {} PhysicalEntitySimulation::~PhysicalEntitySimulation() {} void PhysicalEntitySimulation::init(        EntityTreePointer tree,        PhysicsEnginePointer physicsEngine,        EntityEditPacketSender* packetSender) {    assert(tree);    setEntityTree(tree);     assert(physicsEngine);    _physicsEngine = physicsEngine;     assert(packetSender);    _entityPacketSender = packetSender;} // begin EntitySimulation overridesvoid PhysicalEntitySimulation::addEntityToInternalLists(EntityItemPointer entity) {    EntitySimulation::addEntityToInternalLists(entity);    entity->deserializeActions(); // TODO: do this elsewhere    uint8_t region = _space->getRegion(entity->getSpaceIndex());    bool maybeShouldBePhysical = (region < workload::Region::R3 || region == workload::Region::UNKNOWN) && entity->shouldBePhysical();    bool canBeKinematic = region <= workload::Region::R3;    if (maybeShouldBePhysical) {        EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());        if (motionState) {            motionState->setRegion(region);        } else {            _entitiesToAddToPhysics.insert(entity);        }    } else if (canBeKinematic && entity->isMovingRelativeToParent()) {        SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);        if (itr == _simpleKinematicEntities.end()) {            _simpleKinematicEntities.insert(entity);        }    }} void PhysicalEntitySimulation::removeEntityFromInternalLists(EntityItemPointer entity) {    _entitiesToAddToPhysics.remove(entity);    EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());    if (motionState) {        removeOwnershipData(motionState);        _entitiesToRemoveFromPhysics.insert(entity);    }    if (entity->isDead() && entity->getElement()) {        _deadEntitiesToRemoveFromTree.insert(entity);    }    if (entity->isAvatarEntity()) {        _deadAvatarEntities.insert(entity);    }    EntitySimulation::removeEntityFromInternalLists(entity);} void PhysicalEntitySimulation::removeOwnershipData(EntityMotionState* motionState) {    assert(motionState);    if (motionState->getOwnershipState() == EntityMotionState::OwnershipState::LocallyOwned) {        for (uint32_t i = 0; i < _owned.size(); ++i) {            if (_owned[i] == motionState) {                _owned[i]->clearOwnershipState();                _owned.remove(i);            }        }    } else if (motionState->getOwnershipState() == EntityMotionState::OwnershipState::PendingBid) {        for (uint32_t i = 0; i < _bids.size(); ++i) {            if (_bids[i] == motionState) {                _bids[i]->clearOwnershipState();                _bids.remove(i);            }        }    }} void PhysicalEntitySimulation::clearOwnershipData() {    for (uint32_t i = 0; i < _owned.size(); ++i) {        _owned[i]->clearOwnershipState();    }    _owned.clear();    for (uint32_t i = 0; i < _bids.size(); ++i) {        _bids[i]->clearOwnershipState();    }    _bids.clear();} void PhysicalEntitySimulation::takeDeadAvatarEntities(SetOfEntities& deadEntities) {    _deadAvatarEntities.swap(deadEntities);    _deadAvatarEntities.clear();} void PhysicalEntitySimulation::processChangedEntity(const EntityItemPointer& entity) {    EntitySimulation::processChangedEntity(entity);     // queue incoming changes: from external sources (script, EntityServer, etc) to physics engine    EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());    uint8_t region = _space->getRegion(entity->getSpaceIndex());    bool shouldBePhysical = region < workload::Region::R3 && entity->shouldBePhysical();    bool canBeKinematic = region <= workload::Region::R3;    if (motionState) {        if (!shouldBePhysical) {            if (motionState->isLocallyOwned()) {                // zero velocities by first deactivating the RigidBody                btRigidBody* body = motionState->getRigidBody();                if (body) {                    body->forceActivationState(ISLAND_SLEEPING);                }                 // send packet to remove ownership                // NOTE: this packet will NOT be resent if lost, but the good news is:                // the entity-server will eventually clear velocity and ownership for timeout                motionState->sendUpdate(_entityPacketSender, _physicsEngine->getNumSubsteps());            }             // remove from the physical simulation            _incomingChanges.remove(motionState);            removeOwnershipData(motionState);            _entitiesToRemoveFromPhysics.insert(entity);            if (canBeKinematic && entity->isMovingRelativeToParent()) {                SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);                if (itr == _simpleKinematicEntities.end()) {                    _simpleKinematicEntities.insert(entity);                }            }        } else {            _incomingChanges.insert(motionState);        }        motionState->setRegion(region);    } else if (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?        _entitiesToAddToPhysics.insert(entity);        SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);        if (itr != _simpleKinematicEntities.end()) {            _simpleKinematicEntities.erase(itr);        }    } else if (canBeKinematic && entity->isMovingRelativeToParent()) {        SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);        if (itr == _simpleKinematicEntities.end()) {            _simpleKinematicEntities.insert(entity);        }    } else {        SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);        if (itr != _simpleKinematicEntities.end()) {            _simpleKinematicEntities.erase(itr);        }    }} void PhysicalEntitySimulation::processDeadEntities() {    // Note: this override is a complete rewite of the base class's method because we cannot assume all entities    // are domain entities, and the consequence of trying to delete a domain-entity in this case is very different.    if (_deadEntitiesToRemoveFromTree.empty()) {        return;    }    PROFILE_RANGE(simulation_physics, "Deletes");    std::vector<EntityItemPointer> entitiesToDeleteImmediately;    entitiesToDeleteImmediately.reserve(_deadEntitiesToRemoveFromTree.size());    QUuid sessionID = Physics::getSessionUUID();    QMutexLocker lock(&_mutex);    for (auto entity : _deadEntitiesToRemoveFromTree) {        EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());        if (motionState) {            _entitiesToRemoveFromPhysics.insert(entity);        }        if (entity->isDomainEntity() && !getEntityTree()->isServerlessMode()) {            // interface-client can't delete domainEntities outright, they must roundtrip through the entity-server            _entityPacketSender->queueEraseEntityMessage(entity->getID());        } else {            entitiesToDeleteImmediately.push_back(entity);            entity->collectChildrenForDelete(entitiesToDeleteImmediately, sessionID);        }    }    _deadEntitiesToRemoveFromTree.clear();     if (!entitiesToDeleteImmediately.empty()) {        getEntityTree()->deleteEntitiesByPointer(entitiesToDeleteImmediately);    }} void PhysicalEntitySimulation::clearEntities() {    // TODO: we should probably wait to lock the _physicsEngine so we don't mess up data structures    // while it is in the middle of a simulation step.  As it is, we're probably in shutdown mode    // anyway, so maybe the simulation was already properly shutdown?  Cross our fingers...     QMutexLocker lock(&_mutex);    // remove the objects (aka MotionStates) from physics    _physicsEngine->removeSetOfObjects(_physicalObjects);     clearOwnershipData();     // delete the MotionStates    for (auto stateItr : _physicalObjects) {        EntityMotionState* motionState = static_cast<EntityMotionState*>(&(*stateItr));        assert(motionState);        EntityItemPointer entity = motionState->getEntity();        // TODO: someday when we invert the entities/physics lib dependencies we can let EntityItem delete its own PhysicsInfo        // until then we must do it here        delete motionState;    }    _physicalObjects.clear();     // clear all other lists specific to this derived class    _entitiesToRemoveFromPhysics.clear();    _entitiesToAddToPhysics.clear();    _incomingChanges.clear();    _entitiesToDeleteLater.clear();     EntitySimulation::clearEntities();} void PhysicalEntitySimulation::queueEraseDomainEntity(const QUuid& id) const {    if (_entityPacketSender) {        _entityPacketSender->queueEraseEntityMessage(id);    }} // virtualvoid PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {    // DANGER! this can be called on any thread    // do no dirty deeds here --> assemble list for later    assert(entity);    assert(entity->isDead());    QMutexLocker lock(&_mutex);    _entitiesToDeleteLater.push_back(entity);} void PhysicalEntitySimulation::removeDeadEntities() {    // DANGER! only ever call this on the main thread    QMutexLocker lock(&_mutex);    for (auto& entity : _entitiesToDeleteLater) {        entity->clearActions(getThisPointer());        EntitySimulation::prepareEntityForDelete(entity);    }    _entitiesToDeleteLater.clear();}// end EntitySimulation overrides 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<void*>(motionState));        motionState->setRegion(_space->getRegion(entity->getSpaceIndex()));        _physicalObjects.insert(motionState);        _incomingChanges.insert(motionState);    };     uint32_t deliveryCount = ObjectMotionState::getShapeManager()->getWorkDeliveryCount();    if (deliveryCount != _lastWorkDeliveryCount) {        // new off-thread shapes have arrived --> find adds whose shapes have arrived        _lastWorkDeliveryCount = deliveryCount;        ShapeRequests::iterator requestItr = _shapeRequests.begin();        while (requestItr != _shapeRequests.end()) {            EntityItemPointer entity = requestItr->entity;            EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());            if (!motionState) {                // this is an ADD because motionState doesn't exist yet                btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShapeByKey(requestItr->shapeHash));                if (shape) {                    // shape is ready at last!                    // but the entity's physics desired physics status may have changed since last requested                    if (!entity->shouldBePhysical()) {                        requestItr = _shapeRequests.erase(requestItr);                        continue;                    }                    // rebuild the ShapeInfo to verify hash because entity's desired shape may have changed                    // TODO? is there a better way to do this?                    ShapeInfo shapeInfo;                    entity->computeShapeInfo(shapeInfo);                     if (shapeInfo.getType() == SHAPE_TYPE_NONE) {                        requestItr = _shapeRequests.erase(requestItr);                    } else 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<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));                        if (shape) {                            buildMotionState(shape, entity);                            requestItr = _shapeRequests.erase(requestItr);                        } else {                            requestItr->shapeHash = shapeInfo.getHash();                            ++requestItr;                        }                    } else {                        buildMotionState(shape, entity);                        requestItr = _shapeRequests.erase(requestItr);                    }                } else {                    // shape not ready                    ++requestItr;                }            } else {                // this is a CHANGE because motionState already exists                if (ObjectMotionState::getShapeManager()->hasShapeWithKey(requestItr->shapeHash)) {                    entity->markDirtyFlags(Simulation::DIRTY_SHAPE);                    _incomingChanges.insert(motionState);                    requestItr = _shapeRequests.erase(requestItr);                } else {                    // shape not ready                    ++requestItr;                }            }        }    }     SetOfEntities::iterator entityItr = _entitiesToAddToPhysics.begin();    while (entityItr != _entitiesToAddToPhysics.end()) {        EntityItemPointer entity = (*entityItr);        if (entity->isDead()) {            prepareEntityForDelete(entity);            entityItr = _entitiesToAddToPhysics.erase(entityItr);            continue;        }        if (entity->getPhysicsInfo()) {            entityItr = _entitiesToAddToPhysics.erase(entityItr);            continue;        }        if (!entity->shouldBePhysical()) {            // this entity should no longer be on _entitiesToAddToPhysics            if (entity->isMovingRelativeToParent()) {                SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);                if (itr == _simpleKinematicEntities.end()) {                    _simpleKinematicEntities.insert(entity);                }            }            entityItr = _entitiesToAddToPhysics.erase(entityItr);            continue;        }         uint8_t region = _space->getRegion(entity->getSpaceIndex());        if (region == workload::Region::UNKNOWN) {            // the workload hasn't categorized it yet --> skip for later            ++entityItr;            continue;        }        if (region > workload::Region::R2) {            // not in physical zone --> remove from list            entityItr = _entitiesToAddToPhysics.erase(entityItr);            continue;        }         if (entity->isReadyToComputeShape()) {            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<btCollisionShape*>(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                }            }            entityItr = _entitiesToAddToPhysics.erase(entityItr);        } else {            // skip for later            ++entityItr;        }    }} void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) {    QMutexLocker lock(&_mutex);    // entities being removed    for (auto entity : _entitiesToRemoveFromPhysics) {        EntityMotionState* motionState = static_cast<EntityMotionState*>(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 unhandledFlags = object->getIncomingDirtyFlags();         uint32_t handledFlags = EASY_DIRTY_PHYSICS_FLAGS;        bool isInPhysicsSimulation = object->isInPhysicsSimulation();        bool shouldBeInPhysicsSimulation = object->shouldBeInPhysicsSimulation();        if (!shouldBeInPhysicsSimulation && isInPhysicsSimulation) {            transaction.objectsToRemove.push_back(object);            continue;        }         bool needsNewShape = object->needsNewShape() && object->_entity->isReadyToComputeShape();        if (needsNewShape) {            ShapeType shapeType = object->getShapeType();            if (shapeType == SHAPE_TYPE_STATIC_MESH) {                ShapeRequest shapeRequest(object->_entity);                ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest);                if (requestItr == _shapeRequests.end()) {                    ShapeInfo shapeInfo;                    object->_entity->computeShapeInfo(shapeInfo);                    uint32_t requestCount = ObjectMotionState::getShapeManager()->getWorkRequestCount();                    btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));                    if (shape) {                        object->setShape(shape);                        handledFlags |= Simulation::DIRTY_SHAPE;                        needsNewShape = false;                    } else if (requestCount != ObjectMotionState::getShapeManager()->getWorkRequestCount()) {                        // shape doesn't exist but a new worker has been spawned to build it --> add to shapeRequests and wait                        shapeRequest.shapeHash = shapeInfo.getHash();                        _shapeRequests.insert(shapeRequest);                    } else {                        // failed to build shape --> will not be added/updated                        handledFlags |= Simulation::DIRTY_SHAPE;                    }                } else {                    // continue waiting for shape request                }            } else {                ShapeInfo shapeInfo;                object->_entity->computeShapeInfo(shapeInfo);                btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));                if (shape) {                    object->setShape(shape);                    handledFlags |= Simulation::DIRTY_SHAPE;                    needsNewShape = false;                } else {                    // failed to build shape --> will not be added                }            }        }        if (!isInPhysicsSimulation) {            if (needsNewShape) {                // skip it                continue;            } else {                transaction.objectsToAdd.push_back(object);                handledFlags = DIRTY_PHYSICS_FLAGS;                unhandledFlags = 0;            }        }         if (unhandledFlags & EASY_DIRTY_PHYSICS_FLAGS) {            object->handleEasyChanges(unhandledFlags);        }        if (unhandledFlags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP | (handledFlags & Simulation::DIRTY_SHAPE))) {            transaction.objectsToReinsert.push_back(object);            handledFlags |= HARD_DIRTY_PHYSICS_FLAGS;        } else if (unhandledFlags & Simulation::DIRTY_PHYSICS_ACTIVATION && object->getRigidBody()->isStaticObject()) {            transaction.activeStaticObjects.push_back(object);        }        object->clearIncomingDirtyFlags(handledFlags);    }    _incomingChanges.clear();} void PhysicalEntitySimulation::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) {    // things on objectsToRemove are ready for delete    for (auto object : transaction.objectsToRemove) {        EntityMotionState* entityState = static_cast<EntityMotionState*>(object);        removeOwnershipData(entityState);        _physicalObjects.remove(object);        delete object;    }    transaction.clear();

Lines 1–500 of 703.