Merge pull request #10476 from sethalves/fix-imported-children-querybox

fix up queryAACubes before sending imported entities to server
This commit is contained in:
Brad Hefta-Gaub 2017-05-25 08:29:23 -07:00 committed by GitHub
commit 5af95c8fdf
8 changed files with 138 additions and 91 deletions

View file

@ -27,10 +27,6 @@ AddEntityOperator::AddEntityOperator(EntityTreePointer tree, EntityItemPointer n
bool success;
auto queryCube = _newEntity->getQueryAACube(success);
if (!success) {
_newEntity->markAncestorMissing(true);
}
_newEntityBox = queryCube.clamp((float)(-HALF_TREE_SCALE), (float)HALF_TREE_SCALE);
}

View file

@ -1374,7 +1374,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
somethingChanged = true;
}
// Now check the sub classes
// Now check the sub classes
somethingChanged |= setSubClassProperties(properties);
// Finally notify if change detected
@ -1606,11 +1606,19 @@ void EntityItem::updateRegistrationPoint(const glm::vec3& value) {
void EntityItem::updatePosition(const glm::vec3& value) {
if (getLocalPosition() != value) {
setLocalPosition(value);
EntityTreePointer tree = getTree();
markDirtyFlags(Simulation::DIRTY_POSITION);
if (tree) {
tree->entityChanged(getThisPointer());
}
forEachDescendant([&](SpatiallyNestablePointer object) {
if (object->getNestableType() == NestableType::Entity) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
entity->markDirtyFlags(Simulation::DIRTY_POSITION);
if (tree) {
tree->entityChanged(entity);
}
}
});
}
@ -1622,6 +1630,11 @@ void EntityItem::updateParentID(const QUuid& value) {
// children are forced to be kinematic
// may need to not collide with own avatar
markDirtyFlags(Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP);
EntityTreePointer tree = getTree();
if (tree) {
tree->addToNeedsParentFixupList(getThisPointer());
}
}
}

View file

@ -187,7 +187,7 @@ public:
const Transform getTransformToCenter(bool& success) const;
inline void requiresRecalcBoxes();
void requiresRecalcBoxes();
// Hyperlink related getters and setters
QString getHref() const;
@ -476,6 +476,8 @@ public:
virtual bool getMeshes(MeshProxyList& result) { return true; }
virtual void locationChanged(bool tellPhysics = true) override;
protected:
void setSimulated(bool simulated) { _simulated = simulated; }
@ -483,7 +485,6 @@ protected:
const QByteArray getDynamicDataInternal() const;
void setDynamicDataInternal(QByteArray dynamicData);
virtual void locationChanged(bool tellPhysics = true) override;
virtual void dimensionsChanged() override;
EntityTypes::EntityType _type;

View file

@ -122,16 +122,15 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
_simulation->addEntity(entity);
}
if (!entity->isParentIDValid()) {
QWriteLocker locker(&_missingParentLock);
_missingParent.append(entity);
if (!entity->getParentID().isNull()) {
addToNeedsParentFixupList(entity);
}
_isDirty = true;
emit addingEntity(entity->getEntityItemID());
// find and hook up any entities with this entity as a (previously) missing parent
fixupMissingParents();
fixupNeedsParentFixups();
}
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
@ -291,13 +290,11 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
bool success;
AACube queryCube = childEntity->getQueryAACube(success);
if (!success) {
QWriteLocker locker(&_missingParentLock);
_missingParent.append(childEntity);
addToNeedsParentFixupList(childEntity);
continue;
}
if (!childEntity->isParentIDValid()) {
QWriteLocker locker(&_missingParentLock);
_missingParent.append(childEntity);
if (!childEntity->getParentID().isNull()) {
addToNeedsParentFixupList(childEntity);
}
UpdateEntityOperator theChildOperator(getThisPointer(), containingElement, childEntity, queryCube);
@ -383,11 +380,8 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti
// Recurse the tree and store the entity in the correct tree element
AddEntityOperator theOperator(getThisPointer(), result);
recurseTreeWithOperator(&theOperator);
if (result->getAncestorMissing()) {
// we added the entity, but didn't know about all its ancestors, so it went into the wrong place.
// add it to a list of entities needing to be fixed once their parents are known.
QWriteLocker locker(&_missingParentLock);
_missingParent.append(result);
if (!result->getParentID().isNull()) {
addToNeedsParentFixupList(result);
}
postAddEntity(result);
@ -1254,60 +1248,68 @@ void EntityTree::entityChanged(EntityItemPointer entity) {
}
}
void EntityTree::fixupMissingParents() {
void EntityTree::fixupNeedsParentFixups() {
MovingEntitiesOperator moveOperator(getThisPointer());
QList<EntityItemPointer> missingParents;
{
QWriteLocker locker(&_missingParentLock);
QMutableVectorIterator<EntityItemWeakPointer> iter(_missingParent);
while (iter.hasNext()) {
EntityItemWeakPointer entityWP = iter.next();
EntityItemPointer entity = entityWP.lock();
if (entity) {
if (entity->isParentIDValid()) {
iter.remove();
} else {
missingParents.append(entity);
}
} else {
// entity was deleted before we found its parent.
iter.remove();
QWriteLocker locker(&_needsParentFixupLock);
QMutableVectorIterator<EntityItemWeakPointer> iter(_needsParentFixup);
while (iter.hasNext()) {
EntityItemWeakPointer entityWP = iter.next();
EntityItemPointer entity = entityWP.lock();
if (!entity) {
// entity was deleted before we found its parent
iter.remove();
continue;
}
entity->requiresRecalcBoxes();
bool queryAACubeSuccess { false };
bool maxAACubeSuccess { false };
AACube newCube = entity->getQueryAACube(queryAACubeSuccess);
if (queryAACubeSuccess) {
// make sure queryAACube encompasses maxAACube
AACube maxAACube = entity->getMaximumAACube(maxAACubeSuccess);
if (maxAACubeSuccess && !newCube.contains(maxAACube)) {
newCube = maxAACube;
}
}
}
for (EntityItemPointer entity : missingParents) {
if (entity) {
bool queryAACubeSuccess;
AACube newCube = entity->getQueryAACube(queryAACubeSuccess);
if (queryAACubeSuccess) {
// make sure queryAACube encompasses maxAACube
bool maxAACubeSuccess;
AACube maxAACube = entity->getMaximumAACube(maxAACubeSuccess);
if (maxAACubeSuccess && !newCube.contains(maxAACube)) {
newCube = maxAACube;
bool doMove = false;
if (entity->isParentIDValid() && maxAACubeSuccess) { // maxAACubeSuccess of true means all ancestors are known
iter.remove(); // this entity is all hooked up; we can remove it from the list
// this entity's parent was previously not known, and now is. Update its location in the EntityTree...
doMove = true;
// the bounds on the render-item may need to be updated, the rigid body in the physics engine may
// need to be moved.
entity->markDirtyFlags(Simulation::DIRTY_MOTION_TYPE |
Simulation::DIRTY_COLLISION_GROUP |
Simulation::DIRTY_TRANSFORM);
entityChanged(entity);
entity->forEachDescendant([&](SpatiallyNestablePointer object) {
if (object->getNestableType() == NestableType::Entity) {
EntityItemPointer descendantEntity = std::static_pointer_cast<EntityItem>(object);
descendantEntity->markDirtyFlags(Simulation::DIRTY_MOTION_TYPE |
Simulation::DIRTY_COLLISION_GROUP |
Simulation::DIRTY_TRANSFORM);
entityChanged(descendantEntity);
}
});
entity->locationChanged(true);
} else if (getIsServer() && _avatarIDs.contains(entity->getParentID())) {
// this is a child of an avatar, which the entity server will never have
// a SpatiallyNestable object for. Add it to a list for cleanup when the avatar leaves.
if (!_childrenOfAvatars.contains(entity->getParentID())) {
_childrenOfAvatars[entity->getParentID()] = QSet<EntityItemID>();
}
_childrenOfAvatars[entity->getParentID()] += entity->getEntityItemID();
doMove = true;
iter.remove(); // and pull it out of the list
}
bool doMove = false;
if (entity->isParentIDValid()) {
// this entity's parent was previously not known, and now is. Update its location in the EntityTree...
doMove = true;
} else if (getIsServer() && _avatarIDs.contains(entity->getParentID())) {
// this is a child of an avatar, which the entity server will never have
// a SpatiallyNestable object for. Add it to a list for cleanup when the avatar leaves.
if (!_childrenOfAvatars.contains(entity->getParentID())) {
_childrenOfAvatars[entity->getParentID()] = QSet<EntityItemID>();
}
_childrenOfAvatars[entity->getParentID()] += entity->getEntityItemID();
doMove = true;
}
if (queryAACubeSuccess && doMove) {
moveOperator.addEntityToMoveList(entity, newCube);
entity->markAncestorMissing(false);
}
if (queryAACubeSuccess && doMove) {
moveOperator.addEntityToMoveList(entity, newCube);
}
}
@ -1324,8 +1326,13 @@ void EntityTree::deleteDescendantsOfAvatar(QUuid avatarID) {
}
}
void EntityTree::addToNeedsParentFixupList(EntityItemPointer entity) {
QWriteLocker locker(&_needsParentFixupLock);
_needsParentFixup.append(entity);
}
void EntityTree::update() {
fixupMissingParents();
fixupNeedsParentFixups();
if (_simulation) {
withWriteLock([&] {
_simulation->updateEntities();
@ -1626,31 +1633,61 @@ QByteArray EntityTree::remapActionDataIDs(QByteArray actionData, QHash<EntityIte
QVector<EntityItemID> EntityTree::sendEntities(EntityEditPacketSender* packetSender, EntityTreePointer localTree,
float x, float y, float z) {
SendEntitiesOperationArgs args;
args.packetSender = packetSender;
args.ourTree = this;
args.otherTree = localTree;
args.root = glm::vec3(x, y, z);
// If this is called repeatedly (e.g., multiple pastes with the same data), the new elements will clash unless we use new identifiers.
// We need to keep a map so that we can map parent identifiers correctly.
// If this is called repeatedly (e.g., multiple pastes with the same data), the new elements will clash unless we
// use new identifiers. We need to keep a map so that we can map parent identifiers correctly.
QHash<EntityItemID, EntityItemID> map;
args.map = &map;
withReadLock([&] {
recurseTreeWithOperation(sendEntitiesOperation, &args);
});
packetSender->releaseQueuedMessages();
// the values from map are used as the list of successfully "sent" entities. If some didn't actually make it,
// The values from map are used as the list of successfully "sent" entities. If some didn't actually make it,
// pull them out. Bogus entries could happen if part of the imported data makes some reference to an entity
// that isn't in the data being imported.
// that isn't in the data being imported. For those that made it, fix up their queryAACubes and send an
// add-entity packet to the server.
// fix the queryAACubes of any children that were read in before their parents, get them into the correct element
MovingEntitiesOperator moveOperator(localTree);
QHash<EntityItemID, EntityItemID>::iterator i = map.begin();
while (i != map.end()) {
EntityItemID newID = i.value();
if (localTree->findEntityByEntityItemID(newID)) {
EntityItemPointer entity = localTree->findEntityByEntityItemID(newID);
if (entity) {
if (!entity->getParentID().isNull()) {
addToNeedsParentFixupList(entity);
}
entity->forceQueryAACubeUpdate();
moveOperator.addEntityToMoveList(entity, entity->getQueryAACube());
i++;
} else {
i = map.erase(i);
}
}
if (moveOperator.hasMovingEntities()) {
PerformanceTimer perfTimer("recurseTreeWithOperator");
localTree->recurseTreeWithOperator(&moveOperator);
}
// send add-entity packets to the server
i = map.begin();
while (i != map.end()) {
EntityItemID newID = i.value();
EntityItemPointer entity = localTree->findEntityByEntityItemID(newID);
if (entity) {
// queue the packet to send to the server
entity->computePuffedQueryAACube();
EntityItemProperties properties = entity->getProperties();
properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity
packetSender->queueEditEntityMessage(PacketType::EntityAdd, localTree, newID, properties);
i++;
} else {
i = map.erase(i);
}
}
packetSender->releaseQueuedMessages();
return map.values().toVector();
}
@ -1704,14 +1741,9 @@ bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extra
// set creation time to "now" for imported entities
properties.setCreated(usecTimestampNow());
properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
EntityTreePointer tree = entityTreeElement->getTree();
// queue the packet to send to the server
args->packetSender->queueEditEntityMessage(PacketType::EntityAdd, tree, newID, properties);
// also update the local tree instantly (note: this is not our tree, but an alternate tree)
if (args->otherTree) {
args->otherTree->withWriteLock([&] {

View file

@ -53,7 +53,6 @@ public:
glm::vec3 root;
EntityTree* ourTree;
EntityTreePointer otherTree;
EntityEditPacketSender* packetSender;
QHash<EntityItemID, EntityItemID>* map;
};
@ -272,6 +271,8 @@ public:
void forgetAvatarID(QUuid avatarID) { _avatarIDs -= avatarID; }
void deleteDescendantsOfAvatar(QUuid avatarID);
void addToNeedsParentFixupList(EntityItemPointer entity);
void notifyNewCollisionSoundURL(const QString& newCollisionSoundURL, const EntityItemID& entityID);
static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME;
@ -354,9 +355,9 @@ protected:
quint64 _maxEditDelta = 0;
quint64 _treeResetTime = 0;
void fixupMissingParents(); // try to hook members of _missingParent to parent instances
QVector<EntityItemWeakPointer> _missingParent; // entites with a parentID but no (yet) known parent instance
mutable QReadWriteLock _missingParentLock;
void fixupNeedsParentFixups(); // try to hook members of _needsParentFixup to parent instances
QVector<EntityItemWeakPointer> _needsParentFixup; // entites with a parentID but no (yet) known parent instance
mutable QReadWriteLock _needsParentFixupLock;
// we maintain a list of avatarIDs to notice when an entity is a child of one.
QSet<QUuid> _avatarIDs; // IDs of avatars connected to entity server

View file

@ -208,7 +208,7 @@ void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params) con
// why would this ever fail???
// If we've encoding this element before... but we're coming back a second time in an attempt to
// encoud our parent... this might happen.
// encode our parent... this might happen.
if (extraEncodeData->contains(childElement.get())) {
EntityTreeElementExtraEncodeDataPointer childExtraEncodeData
= std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[childElement.get()]);
@ -981,6 +981,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
// 3) remember the old cube for the entity so we can mark it as dirty
if (entityItem) {
QString entityScriptBefore = entityItem->getScript();
QUuid parentIDBefore = entityItem->getParentID();
QString entityServerScriptsBefore = entityItem->getServerScripts();
quint64 entityScriptTimestampBefore = entityItem->getScriptTimestamp();
bool bestFitBefore = bestFitEntityBounds(entityItem);
@ -1018,6 +1019,11 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
_myTree->emitEntityServerScriptChanging(entityItemID, reload); // the entity server script has changed
}
QUuid parentIDAfter = entityItem->getParentID();
if (parentIDBefore != parentIDAfter) {
_myTree->addToNeedsParentFixupList(entityItem);
}
} else {
entityItem = EntityTypes::constructEntityItem(dataAt, bytesLeftToRead, args);
if (entityItem) {

View file

@ -946,12 +946,13 @@ AACube SpatiallyNestable::getMaximumAACube(bool& success) const {
return AACube(getPosition(success) - glm::vec3(defaultAACubeSize / 2.0f), defaultAACubeSize);
}
void SpatiallyNestable::checkAndAdjustQueryAACube() {
bool SpatiallyNestable::checkAndAdjustQueryAACube() {
bool success;
AACube maxAACube = getMaximumAACube(success);
if (success && (!_queryAACubeSet || !_queryAACube.contains(maxAACube))) {
setQueryAACube(maxAACube);
}
return success;
}
void SpatiallyNestable::setQueryAACube(const AACube& queryAACube) {

View file

@ -102,11 +102,12 @@ public:
virtual glm::vec3 getParentAngularVelocity(bool& success) const;
virtual AACube getMaximumAACube(bool& success) const;
virtual void checkAndAdjustQueryAACube();
virtual bool checkAndAdjustQueryAACube();
virtual bool computePuffedQueryAACube();
virtual void setQueryAACube(const AACube& queryAACube);
virtual bool queryAABoxNeedsUpdate() const;
void forceQueryAACubeUpdate() { _queryAACubeSet = false; }
virtual AACube getQueryAACube(bool& success) const;
virtual AACube getQueryAACube() const;
@ -157,9 +158,6 @@ public:
SpatiallyNestablePointer getThisPointer() const;
void markAncestorMissing(bool value) { _missingAncestor = value; }
bool getAncestorMissing() { return _missingAncestor; }
void forEachChild(std::function<void(SpatiallyNestablePointer)> actor);
void forEachDescendant(std::function<void(SpatiallyNestablePointer)> actor);
@ -206,7 +204,6 @@ protected:
mutable AACube _queryAACube;
mutable bool _queryAACubeSet { false };
bool _missingAncestor { false };
quint64 _scaleChanged { 0 };
quint64 _translationChanged { 0 };
quint64 _rotationChanged { 0 };