add a heuristic for deciding when it's safe to enable bullet

This commit is contained in:
Seth Alves 2016-04-12 11:46:00 -07:00
parent 83494c4a96
commit e4c91e5064
3 changed files with 78 additions and 29 deletions

View file

@ -3197,7 +3197,19 @@ void Application::cameraMenuChanged() {
}
}
void Application::resetPhysicsReadyInformation() {
// we've changed domains or cleared out caches or something. we no longer know enough about the
// collision information of nearby entities to make running bullet be safe.
_fullSceneReceivedCounter = 0;
_fullSceneCounterAtLastPhysicsCheck = 0;
_nearbyEntitiesCountAtLastPhysicsCheck = 0;
_nearbyEntitiesStabilityCount = 0;
_physicsEnabled = false;
}
void Application::reloadResourceCaches() {
resetPhysicsReadyInformation();
// Clear entities out of view frustum
_viewFrustum.setPosition(glm::vec3(0.0f, 0.0f, TREE_SCALE));
_viewFrustum.setOrientation(glm::quat());
@ -3254,21 +3266,34 @@ void Application::update(float deltaTime) {
updateLOD();
if (!_physicsEnabled && _processOctreeStatsCounter > 0) {
if (!_physicsEnabled) {
// we haven't yet enabled physics. we wait until we think we have all the collision information
// for nearby entities before starting bullet up.
quint64 now = usecTimestampNow();
bool timeout = false;
const int PHYSICS_CHECK_TIMEOUT = 2 * USECS_PER_SECOND;
if (_lastPhysicsCheckTime > 0 && now - _lastPhysicsCheckTime > PHYSICS_CHECK_TIMEOUT) {
timeout = true;
}
// process octree stats packets are sent in between full sends of a scene.
// We keep physics disabled until we've received a full scene and everything near the avatar in that
// scene is ready to compute its collision shape.
if (timeout || _fullSceneReceivedCounter > _fullSceneCounterAtLastPhysicsCheck) {
// we've received a new full-scene octree stats packet, or it's been long enough to try again anyway
_lastPhysicsCheckTime = now;
_fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter;
if (nearbyEntitiesAreReadyForPhysics()) {
_physicsEnabled = true;
getMyAvatar()->updateMotionBehaviorFromMenu();
} else {
auto characterController = getMyAvatar()->getCharacterController();
if (characterController) {
// if we have a character controller, disable it here so the avatar doesn't get stuck due to
// a non-loading collision hull.
characterController->setEnabled(false);
// process octree stats packets are sent in between full sends of a scene (this isn't currently true).
// We keep physics disabled until we've received a full scene and everything near the avatar in that
// scene is ready to compute its collision shape.
if (nearbyEntitiesAreReadyForPhysics()) {
_physicsEnabled = true;
getMyAvatar()->updateMotionBehaviorFromMenu();
} else {
auto characterController = getMyAvatar()->getCharacterController();
if (characterController) {
// if we have a character controller, disable it here so the avatar doesn't get stuck due to
// a non-loading collision hull.
characterController->setEnabled(false);
}
}
}
}
@ -4153,7 +4178,7 @@ void Application::clearDomainOctreeDetails() {
qCDebug(interfaceapp) << "Clearing domain octree details...";
// reset the environment so that we don't erroneously end up with multiple
_physicsEnabled = false;
resetPhysicsReadyInformation();
// reset our node to stats and node to jurisdiction maps... since these must be changing...
_entityServerJurisdictions.withWriteLock([&] {
@ -4177,7 +4202,7 @@ void Application::domainChanged(const QString& domainHostname) {
updateWindowTitle();
clearDomainOctreeDetails();
// disable physics until we have enough information about our new location to not cause craziness.
_physicsEnabled = false;
resetPhysicsReadyInformation();
}
@ -4310,15 +4335,31 @@ bool Application::nearbyEntitiesAreReadyForPhysics() {
entityTree->findEntities(box, entities);
});
foreach (EntityItemPointer entity, entities) {
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();
return false;
}
// For reasons I haven't found, we don't necessarily have the full scene when we receive a stats packet. Apply
// a heuristic to try to decide when we actually know about all of the nearby entities.
int nearbyCount = entities.size();
if (nearbyCount == _nearbyEntitiesCountAtLastPhysicsCheck) {
_nearbyEntitiesStabilityCount++;
} else {
_nearbyEntitiesStabilityCount = 0;
}
return true;
_nearbyEntitiesCountAtLastPhysicsCheck = nearbyCount;
const uint32_t MINIMUM_NEARBY_ENTITIES_STABILITY_COUNT = 3;
if (_nearbyEntitiesStabilityCount >= MINIMUM_NEARBY_ENTITIES_STABILITY_COUNT) {
// We've seen the same number of nearby entities for several stats packets in a row. assume we've got all
// the local entities.
foreach (EntityItemPointer entity, entities) {
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();
return false;
}
}
return true;
}
return false;
}
int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode) {
@ -4335,6 +4376,10 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer
OctreeSceneStats& octreeStats = _octreeServerSceneStats[nodeUUID];
statsMessageLength = octreeStats.unpackFromPacket(message);
if (octreeStats.isFullScene()) {
_fullSceneReceivedCounter++;
}
// see if this is the first we've heard of this node...
NodeToJurisdictionMap* jurisdiction = nullptr;
QString serverType;
@ -4366,8 +4411,6 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer
});
});
_processOctreeStatsCounter++;
return statsMessageLength;
}

View file

@ -271,6 +271,8 @@ public slots:
void toggleOverlays();
void setOverlaysVisible(bool visible);
void resetPhysicsReadyInformation();
void reloadResourceCaches();
void updateHeartbeat() const;
@ -514,7 +516,11 @@ private:
std::map<void*, std::function<void()>> _preRenderLambdas;
std::mutex _preRenderLambdasLock;
std::atomic<uint32_t> _processOctreeStatsCounter { 0 };
std::atomic<uint32_t> _fullSceneReceivedCounter { 0 }; // how many times have we received a full-scene octree stats packet
uint32_t _fullSceneCounterAtLastPhysicsCheck { 0 }; // _fullSceneReceivedCounter last time we checked physics ready
uint32_t _nearbyEntitiesCountAtLastPhysicsCheck { 0 }; // how many in-range entities last time we checked physics ready
uint32_t _nearbyEntitiesStabilityCount { 0 }; // how many times has _nearbyEntitiesCountAtLastPhysicsCheck been the same
quint64 _lastPhysicsCheckTime { 0 }; // when did we last check to see if physics was ready
bool _keyboardDeviceHasFocus { true };

View file

@ -699,7 +699,7 @@ void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searc
// if the sphere doesn't intersect with our world frame AABox, we don't need to consider the more complex case
glm::vec3 penetration;
if (success && entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) {
if (!success || entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) {
glm::vec3 dimensions = entity->getDimensions();
@ -764,7 +764,7 @@ void EntityTreeElement::getEntities(const AACube& cube, QVector<EntityItemPointe
//
// If the entities AABox touches the search cube then consider it to be found
if (success && entityBox.touches(cube)) {
if (!success || entityBox.touches(cube)) {
foundEntities.push_back(entity);
}
});
@ -790,7 +790,7 @@ void EntityTreeElement::getEntities(const AABox& box, QVector<EntityItemPointer>
//
// If the entities AABox touches the search cube then consider it to be found
if (success && entityBox.touches(box)) {
if (!success || entityBox.touches(box)) {
foundEntities.push_back(entity);
}
});