Merge pull request #6612 from sethalves/no-physics-until-ready

No physics until ready
This commit is contained in:
Howard Stearns 2015-12-10 16:09:49 -08:00
commit 585911fe98
12 changed files with 122 additions and 56 deletions

View file

@ -352,6 +352,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<UserInputMapper>();
DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
DependencyManager::set<InterfaceParentFinder>();
DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp);
return true;
}
@ -371,7 +372,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_frameCount(0),
_fps(60.0f),
_physicsEngine(new PhysicsEngine(Vectors::ZERO)),
_entities(true, this, this),
_entityClipboardRenderer(false, this, this),
_entityClipboard(new EntityTree()),
_lastQueriedTime(usecTimestampNow()),
@ -864,7 +864,7 @@ void Application::cleanupBeforeQuit() {
}
_keyboardFocusHighlight = nullptr;
_entities.clear(); // this will allow entity scripts to properly shutdown
getEntities()->clear(); // this will allow entity scripts to properly shutdown
auto nodeList = DependencyManager::get<NodeList>();
@ -875,7 +875,7 @@ void Application::cleanupBeforeQuit() {
// tell the packet receiver we're shutting down, so it can drop packets
nodeList->getPacketReceiver().setShouldDropPackets(true);
_entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
ScriptEngine::stopAllScripts(this); // stop all currently running global scripts
// first stop all timers directly or by invokeMethod
@ -921,7 +921,7 @@ void Application::emptyLocalCache() {
}
Application::~Application() {
EntityTreePointer tree = _entities.getTree();
EntityTreePointer tree = getEntities()->getTree();
tree->setSimulation(NULL);
_octreeProcessor.terminate();
@ -1976,7 +1976,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
event->buttons(), event->modifiers());
_entities.mouseMoveEvent(&mappedEvent, deviceID);
getEntities()->mouseMoveEvent(&mappedEvent, deviceID);
_controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
@ -2002,7 +2002,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
event->buttons(), event->modifiers());
if (!_aboutToQuit) {
_entities.mousePressEvent(&mappedEvent, deviceID);
getEntities()->mousePressEvent(&mappedEvent, deviceID);
}
_controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts
@ -2047,7 +2047,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
event->buttons(), event->modifiers());
if (!_aboutToQuit) {
_entities.mouseReleaseEvent(&mappedEvent, deviceID);
getEntities()->mouseReleaseEvent(&mappedEvent, deviceID);
}
_controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
@ -2370,7 +2370,7 @@ void Application::calibrateEyeTracker5Points() {
bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) {
QVector<EntityItemPointer> entities;
auto entityTree = _entities.getTree();
auto entityTree = getEntities()->getTree();
auto exportTree = std::make_shared<EntityTree>();
exportTree->createRootElement();
@ -2414,7 +2414,7 @@ bool Application::exportEntities(const QString& filename, const QVector<EntityIt
bool Application::exportEntities(const QString& filename, float x, float y, float z, float scale) {
QVector<EntityItemPointer> entities;
_entities.getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities);
getEntities()->getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities);
if (entities.size() > 0) {
glm::vec3 root(x, y, z);
@ -2482,7 +2482,7 @@ bool Application::importEntities(const QString& urlOrFilename) {
}
QVector<EntityItemID> Application::pasteEntities(float x, float y, float z) {
return _entityClipboard->sendEntities(&_entityEditSender, _entities.getTree(), x, y, z);
return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), x, y, z);
}
void Application::initDisplay() {
@ -2521,13 +2521,13 @@ void Application::init() {
// fire off an immediate domain-server check in now that settings are loaded
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
_entities.init();
_entities.setViewFrustum(getViewFrustum());
getEntities()->init();
getEntities()->setViewFrustum(getViewFrustum());
ObjectMotionState::setShapeManager(&_shapeManager);
_physicsEngine->init();
EntityTreePointer tree = _entities.getTree();
EntityTreePointer tree = getEntities()->getTree();
_entitySimulation.init(tree, _physicsEngine, &_entityEditSender);
tree->setSimulation(&_entitySimulation);
@ -2535,11 +2535,11 @@ void Application::init() {
// connect the _entityCollisionSystem to our EntityTreeRenderer since that's what handles running entity scripts
connect(&_entitySimulation, &EntitySimulation::entityCollisionWithEntity,
&_entities, &EntityTreeRenderer::entityCollisionWithEntity);
getEntities(), &EntityTreeRenderer::entityCollisionWithEntity);
// connect the _entities (EntityTreeRenderer) to our script engine's EntityScriptingInterface for firing
// of events related clicking, hovering over, and entering entities
_entities.connectSignalsToSlots(entityScriptingInterface.data());
getEntities()->connectSignalsToSlots(entityScriptingInterface.data());
_entityClipboardRenderer.init();
_entityClipboardRenderer.setViewFrustum(getViewFrustum());
@ -2888,19 +2888,19 @@ void Application::update(float deltaTime) {
_avatarUpdate->synchronousProcess();
if (true || _physicsEnabled) {
if (_physicsEnabled) {
PerformanceTimer perfTimer("physics");
static VectorOfMotionStates motionStates;
_entitySimulation.getObjectsToDelete(motionStates);
_physicsEngine->deleteObjects(motionStates);
_entities.getTree()->withWriteLock([&] {
getEntities()->getTree()->withWriteLock([&] {
_entitySimulation.getObjectsToAdd(motionStates);
_physicsEngine->addObjects(motionStates);
});
_entities.getTree()->withWriteLock([&] {
getEntities()->getTree()->withWriteLock([&] {
_entitySimulation.getObjectsToChange(motionStates);
VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
_entitySimulation.setObjectsToChange(stillNeedChange);
@ -2918,12 +2918,12 @@ void Application::update(float deltaTime) {
myAvatar->prepareForPhysicsSimulation();
_entities.getTree()->withWriteLock([&] {
getEntities()->getTree()->withWriteLock([&] {
_physicsEngine->stepSimulation();
});
if (_physicsEngine->hasOutgoingChanges()) {
_entities.getTree()->withWriteLock([&] {
getEntities()->getTree()->withWriteLock([&] {
_entitySimulation.handleOutgoingChanges(_physicsEngine->getOutgoingChanges(), _physicsEngine->getSessionID());
avatarManager->handleOutgoingChanges(_physicsEngine->getOutgoingChanges());
});
@ -2938,9 +2938,9 @@ void Application::update(float deltaTime) {
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
// deadlock.)
_entitySimulation.handleCollisionEvents(collisionEvents);
// NOTE: the _entities.update() call below will wait for lock
// NOTE: the getEntities()->update() call below will wait for lock
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
_entities.update(); // update the models...
getEntities()->update(); // update the models...
}
myAvatar->harvestResultsFromPhysicsSimulation();
@ -3764,7 +3764,7 @@ void Application::clearDomainOctreeDetails() {
});
// reset the model renderer
_entities.clear();
getEntities()->clear();
}
void Application::domainChanged(const QString& domainHostname) {
@ -3886,7 +3886,7 @@ bool Application::nearbyEntitiesAreReadyForPhysics() {
// Someone logs in close to the table. They receive information about the items on the table before they
// receive information about the table. The items are very close to the avatar's capsule, so they become
// activated in bullet. This causes them to fall to the floor, because the table's shape isn't yet in bullet.
EntityTreePointer entityTree = _entities.getTree();
EntityTreePointer entityTree = getEntities()->getTree();
if (!entityTree) {
return false;
}
@ -3899,6 +3899,9 @@ bool Application::nearbyEntitiesAreReadyForPhysics() {
foreach (EntityItemPointer entity, entities) {
if (!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;
}
}
@ -3950,11 +3953,21 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer
});
});
if (!_physicsEnabled && nearbyEntitiesAreReadyForPhysics()) {
// These stats packets are sent in between full sends of a scene.
// We keep physics disabled until we've recieved a full scene and everything near the avatar in that
// scene is ready to compute its collision shape.
_physicsEnabled = true;
if (!_physicsEnabled) {
if (nearbyEntitiesAreReadyForPhysics()) {
// These stats packets are sent in between full sends of a scene.
// We keep physics disabled until we've recieved a full scene and everything near the avatar in that
// scene is ready to compute its collision shape.
_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);
}
}
}
return statsMessageLength;
@ -4017,7 +4030,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
// we can use the same ones from the application.
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
entityScriptingInterface->setPacketSender(&_entityEditSender);
entityScriptingInterface->setEntityTree(_entities.getTree());
entityScriptingInterface->setEntityTree(getEntities()->getTree());
// AvatarManager has some custom types
AvatarManager::registerMetaTypes(scriptEngine);

View file

@ -136,7 +136,7 @@ public:
const ViewFrustum* getDisplayViewFrustum() const;
ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; }
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
EntityTreeRenderer* getEntities() { return &_entities; }
EntityTreeRenderer* getEntities() { return DependencyManager::get<EntityTreeRenderer>().data(); }
QUndoStack* getUndoStack() { return &_undoStack; }
MainWindow* getWindow() { return _window; }
EntityTreePointer getEntityClipboard() { return _entityClipboard; }
@ -444,7 +444,6 @@ private:
PhysicalEntitySimulation _entitySimulation;
PhysicsEnginePointer _physicsEngine;
EntityTreeRenderer _entities;
EntityTreeRenderer _entityClipboardRenderer;
EntityTreePointer _entityClipboard;

View file

@ -95,7 +95,6 @@ int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval() const {
result = MSECS_PER_SECOND / THROTTLED_FRAMERATE;
}
qDebug() << "New interval " << result;
return result;
}
@ -138,7 +137,6 @@ void Basic2DWindowOpenGLDisplayPlugin::updateFramerate() {
}
int newInterval = getDesiredInterval();
qDebug() << newInterval;
_timer.start(newInterval);
}

View file

@ -30,7 +30,7 @@ class ZoneEntityItem;
// Generic client side Octree renderer class.
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService {
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService, public Dependency {
Q_OBJECT
public:
EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,

View file

@ -12,12 +12,14 @@
#include <glm/gtx/quaternion.hpp>
#include <QJsonDocument>
#include <QtCore/QThread>
#include <AbstractViewStateInterface.h>
#include <DeferredLightingEffect.h>
#include <Model.h>
#include <PerfStat.h>
#include <render/Scene.h>
#include <DependencyManager.h>
#include "EntityTreeRenderer.h"
#include "EntitiesRendererLogging.h"
@ -44,6 +46,32 @@ RenderableModelEntityItem::~RenderableModelEntityItem() {
}
}
void RenderableModelEntityItem::setModelURL(const QString& url) {
auto& currentURL = getParsedModelURL();
ModelEntityItem::setModelURL(url);
if (currentURL != getParsedModelURL() || !_model) {
EntityTreePointer tree = getTree();
if (tree) {
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
}
}
}
void RenderableModelEntityItem::loader() {
_needsModelReload = true;
EntityTreeRenderer* renderer = DependencyManager::get<EntityTreeRenderer>().data();
assert(renderer);
if (!_model || _needsModelReload) {
PerformanceTimer perfTimer("getModel");
getModel(renderer);
}
if (_model) {
_model->setURL(getParsedModelURL());
_model->setCollisionModelURL(QUrl(getCompoundShapeURL()));
}
}
void RenderableModelEntityItem::setDimensions(const glm::vec3& value) {
_dimensionsInitialized = true;
ModelEntityItem::setDimensions(value);
@ -223,7 +251,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
// check if the URL has changed
auto& currentURL = getParsedModelURL();
if (currentURL != _model->getURL()) {
qDebug().noquote() << "Updating model URL: " << currentURL.toDisplayString();
qCDebug(entitiesrenderer).noquote() << "Updating model URL: " << currentURL.toDisplayString();
_model->setURL(currentURL);
}
@ -318,7 +346,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
Model* result = NULL;
if (!renderer) {
return result;
}
@ -340,7 +368,8 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
// if we have a previously allocated model, but its URL doesn't match
// then we need to let our renderer update our model for us.
if (_model && QUrl(getModelURL()) != _model->getURL()) {
if (_model && (QUrl(getModelURL()) != _model->getURL() ||
QUrl(getCompoundShapeURL()) != _model->getCollisionURL())) {
result = _model = _myRenderer->updateModel(_model, getModelURL(), getCompoundShapeURL());
_needsInitialSimulation = true;
} else if (!_model) { // if we don't yet have a model, then we want our renderer to allocate one
@ -403,23 +432,34 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
}
void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
auto currentCompoundShapeURL = getCompoundShapeURL();
ModelEntityItem::setCompoundShapeURL(url);
if (_model) {
_model->setCollisionModelURL(QUrl(url));
if (getCompoundShapeURL() != currentCompoundShapeURL || !_model) {
EntityTreePointer tree = getTree();
if (tree) {
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
}
}
}
bool RenderableModelEntityItem::isReadyToComputeShape() {
ShapeType type = getShapeType();
if (type == SHAPE_TYPE_COMPOUND) {
if (type == SHAPE_TYPE_COMPOUND) {
if (!_model) {
EntityTreePointer tree = getTree();
if (tree) {
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
}
return false; // hmm...
}
if (_needsInitialSimulation) {
// the _model's offset will be wrong until _needsInitialSimulation is false
return false;
PerformanceTimer perfTimer("_model->simulate");
_model->simulate(0.0f);
_needsInitialSimulation = false;
}
assert(!_model->getCollisionURL().isEmpty());

View file

@ -29,7 +29,8 @@ public:
virtual ~RenderableModelEntityItem();
virtual void setDimensions(const glm::vec3& value) override;
virtual void setModelURL(const QString& url);
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
@ -71,6 +72,8 @@ public:
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
virtual void loader() override;
private:
void remapTextures();

View file

@ -382,6 +382,8 @@ public:
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override { return glm::quat(); }
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override { return glm::vec3(0.0f); }
virtual void loader() {} // called indirectly when urls for geometry are updated
protected:
const QByteArray getActionDataInternal() const;

View file

@ -1266,3 +1266,12 @@ void EntityTree::trackIncomingEntityLastEdited(quint64 lastEditedTime, int bytes
}
}
}
void EntityTree::callLoader(EntityItemID entityID) {
// this is used to bounce from the networking thread to the main thread
EntityItemPointer entity = findEntityByEntityItemID(entityID);
if (entity) {
entity->loader();
}
}

View file

@ -235,6 +235,9 @@ public:
return _deletedEntityItemIDs.contains(id);
}
public slots:
void callLoader(EntityItemID entityID);
signals:
void deletingEntity(const EntityItemID& entityID);
void addingEntity(const EntityItemID& entityID);

View file

@ -74,11 +74,10 @@ public:
_color[GREEN_INDEX] = value.green;
_color[BLUE_INDEX] = value.blue;
}
// model related properties
void setModelURL(const QString& url) { _modelURL = url; _parsedModelURL = QUrl(url); }
virtual void setCompoundShapeURL(const QString& url);
// model related properties
virtual void setModelURL(const QString& url) { _modelURL = url; _parsedModelURL = QUrl(url); }
virtual void setCompoundShapeURL(const QString& url);
// Animation related items...
const AnimationPropertyGroup& getAnimationProperties() const { return _animationProperties; }

View file

@ -57,7 +57,7 @@ public:
static bool getDrawZoneBoundaries() { return _drawZoneBoundaries; }
static void setDrawZoneBoundaries(bool value) { _drawZoneBoundaries = value; }
virtual bool isReadyToComputeShape() { return false; }
virtual bool isReadyToComputeShape() { return true; }
void updateShapeType(ShapeType type) { _shapeType = type; }
virtual ShapeType getShapeType() const;

View file

@ -319,10 +319,10 @@ void Resource::attemptRequest() {
void Resource::finishedLoading(bool success) {
if (success) {
qDebug().noquote() << "Finished loading:" << _url.toDisplayString();
qCDebug(networking).noquote() << "Finished loading:" << _url.toDisplayString();
_loaded = true;
} else {
qDebug().noquote() << "Failed to load:" << _url.toDisplayString();
qCDebug(networking).noquote() << "Failed to load:" << _url.toDisplayString();
_failedToLoad = true;
}
_loadPriorities.clear();
@ -339,13 +339,13 @@ void Resource::makeRequest() {
_request = ResourceManager::createResourceRequest(this, _activeUrl);
if (!_request) {
qDebug().noquote() << "Failed to get request for" << _url.toDisplayString();
qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString();
ResourceCache::requestCompleted(this);
finishedLoading(false);
return;
}
qDebug().noquote() << "Starting request for:" << _url.toDisplayString();
qCDebug(networking).noquote() << "Starting request for:" << _url.toDisplayString();
connect(_request, &ResourceRequest::progress, this, &Resource::handleDownloadProgress);
connect(_request, &ResourceRequest::finished, this, &Resource::handleReplyFinished);
@ -369,7 +369,7 @@ void Resource::handleReplyFinished() {
if (result == ResourceRequest::Success) {
_data = _request->getData();
auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString());
qDebug().noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo);
qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo);
finishedLoading(true);
emit loaded(_data);
@ -377,7 +377,7 @@ void Resource::handleReplyFinished() {
} else {
switch (result) {
case ResourceRequest::Result::Timeout: {
qDebug() << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal;
qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal;
// Fall through to other cases
}
case ResourceRequest::Result::ServerUnavailable: {
@ -386,7 +386,7 @@ void Resource::handleReplyFinished() {
const int BASE_DELAY_MS = 1000;
if (_attempts++ < MAX_ATTEMPTS) {
auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts);
qDebug().nospace() << "Retrying to load the asset in " << waitTime
qCDebug(networking).nospace() << "Retrying to load the asset in " << waitTime
<< "ms, attempt " << _attempts << " of " << MAX_ATTEMPTS;
QTimer::singleShot(waitTime, this, &Resource::attemptRequest);
break;
@ -394,7 +394,7 @@ void Resource::handleReplyFinished() {
// fall through to final failure
}
default: {
qDebug() << "Error loading " << _url;
qCDebug(networking) << "Error loading " << _url;
auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError
: QNetworkReply::UnknownNetworkError;
emit failed(error);