Example #1
0
bool DeleteEntityOperator::preRecursion(const OctreeElementPointer& element) {
    EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);

    // In Pre-recursion, we're generally deciding whether or not we want to recurse this
    // path of the tree. For this operation, we want to recurse the branch of the tree if:
    //   * We have not yet found the all entities, and 
    //   * this branch contains our some of the entities we're looking for.
    
    bool keepSearching = false; // assume we don't need to search any more
    
    // If we haven't yet found all the entities, and this sub tree contains at least one of our
    // entities, then we need to keep searching.
    if ((_foundCount < _lookingCount) && subTreeContainsSomeEntitiesToDelete(element)) {

        // check against each of our search entities
        foreach(const EntityToDeleteDetails& details, _entitiesToDelete) {

            // If this is the element we're looking for, then ask it to remove the old entity
            // and we can stop searching.
            if (entityTreeElement == details.containingElement) {
                EntityItemPointer theEntity = details.entity;
                bool entityDeleted = entityTreeElement->removeEntityItem(theEntity, true); // remove it from the element
                assert(entityDeleted);
                (void)entityDeleted; // quite warning
                _tree->clearEntityMapEntry(details.entity->getEntityItemID());
                _foundCount++;
            }
        }
        
        // if we haven't found all of our search for entities, then keep looking
        keepSearching = (_foundCount < _lookingCount);
    }
Example #2
0
void EntityTreeElement::initializeExtraEncodeData(EncodeBitstreamParams& params) {

    OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
    assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
    // Check to see if this element yet has encode data... if it doesn't create it
    if (!extraEncodeData->contains(this)) {
        EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData = new EntityTreeElementExtraEncodeData();
        entityTreeElementExtraEncodeData->elementCompleted = (_entityItems.size() == 0);
        for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
            EntityTreeElementPointer child = getChildAtIndex(i);
            if (!child) {
                entityTreeElementExtraEncodeData->childCompleted[i] = true; // if no child exists, it is completed
            } else {
                if (child->hasEntities()) {
                    entityTreeElementExtraEncodeData->childCompleted[i] = false; // HAS ENTITIES NEEDS ENCODING
                } else {
                    entityTreeElementExtraEncodeData->childCompleted[i] = true; // child doesn't have enities, it is completed
                }
            }
        }
        forEachEntity([&](EntityItemPointer entity) {
            entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params));
        });

        // TODO: some of these inserts might be redundant!!!
        extraEncodeData->insert(this, entityTreeElementExtraEncodeData);
    }
}
Example #3
0
bool AddEntityOperator::preRecursion(const OctreeElementPointer& element) {
    EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);

    // In Pre-recursion, we're generally deciding whether or not we want to recurse this
    // path of the tree. For this operation, we want to recurse the branch of the tree if
    // any of the following are true:
    //   * We have not yet found the location for the new entity, and this branch contains the bounds of the new entity
    
    bool keepSearching = false; // assume we don't need to search any more
    
    // If we haven't yet found the new entity,  and this subTreeContains our new
    // entity, then we need to keep searching.
    if (!_foundNew && element->getAACube().contains(_newEntityBox)) {

        // If this element is the best fit for the new entity properties, then add/or update it
        if (entityTreeElement->bestFitBounds(_newEntityBox)) {
            _tree->addEntityMapEntry(_newEntity);
            entityTreeElement->addEntityItem(_newEntity);
            _foundNew = true;
            keepSearching = false;
        } else {
            keepSearching = true;
        }
    }
    
    return keepSearching; // if we haven't yet found it, keep looking
}
Example #4
0
void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::VisibleElement& next,
        const DiffTraversal::View& view, const DiffTraversal::View& lastView) {
    if (_nextIndex == -1) {
        // root case is special
        ++_nextIndex;
        EntityTreeElementPointer element = _weakElement.lock();
        next.element = element;
        next.intersection = ViewFrustum::INTERSECT;
        return;
    } else if (_nextIndex < NUMBER_OF_CHILDREN) {
        EntityTreeElementPointer element = _weakElement.lock();
        if (element) {
            while (_nextIndex < NUMBER_OF_CHILDREN) {
                EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
                ++_nextIndex;
                if (nextElement) {
                    AACube cube = nextElement->getAACube();
                    // check for LOD truncation
                    float distance = glm::distance(view.viewFrustum.getPosition(), cube.calcCenter()) + MIN_VISIBLE_DISTANCE;
                    float angularDiameter = cube.getScale() / distance;
                    if (angularDiameter > MIN_ELEMENT_ANGULAR_DIAMETER * view.lodScaleFactor) {
                        if (view.viewFrustum.calculateCubeKeyholeIntersection(cube) != ViewFrustum::OUTSIDE) {
                            next.element = nextElement;
                            next.intersection = ViewFrustum::OUTSIDE;
                            return;
                        }
                    }
                }
            }
        }
    }
    next.element.reset();
    next.intersection = ViewFrustum::OUTSIDE;
}
Example #5
0
void EntityTree::deleteEntity(const EntityItemID& entityID, bool force, bool ignoreWarnings) {
    EntityTreeElementPointer containingElement = getContainingElement(entityID);
    if (!containingElement) {
        if (!ignoreWarnings) {
            qCDebug(entities) << "UNEXPECTED!!!!  EntityTree::deleteEntity() entityID doesn't exist!!! entityID=" << entityID;
        }
        return;
    }

    EntityItemPointer existingEntity = containingElement->getEntityWithEntityItemID(entityID);
    if (!existingEntity) {
        if (!ignoreWarnings) {
            qCDebug(entities) << "UNEXPECTED!!!! don't call EntityTree::deleteEntity() on entity items that don't exist. "
                        "entityID=" << entityID;
        }
        return;
    }

    if (existingEntity->getLocked() && !force) {
        if (!ignoreWarnings) {
            qCDebug(entities) << "ERROR! EntityTree::deleteEntity() trying to delete locked entity. entityID=" << entityID;
        }
        return;
    }

    emit deletingEntity(entityID);

    // NOTE: callers must lock the tree before using this method
    DeleteEntityOperator theOperator(getThisPointer(), entityID);
    recurseTreeWithOperator(&theOperator);
    processRemovedEntities(theOperator);
    _isDirty = true;
}
Example #6
0
void DiffTraversal::Waypoint::getNextVisibleElementFirstTime(DiffTraversal::VisibleElement& next,
        const DiffTraversal::View& view) {
    // NOTE: no need to set next.intersection in the "FirstTime" context
    if (_nextIndex == -1) {
        // root case is special:
        // its intersection is always INTERSECT,
        // we never bother checking for LOD culling, and
        // we can skip it if the content hasn't changed
        ++_nextIndex;
        next.element = _weakElement.lock();
        return;
    } else if (_nextIndex < NUMBER_OF_CHILDREN) {
        EntityTreeElementPointer element = _weakElement.lock();
        if (element) {
            while (_nextIndex < NUMBER_OF_CHILDREN) {
                EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
                ++_nextIndex;
                if (nextElement && view.shouldTraverseElement(*nextElement)) {
                    next.element = nextElement;
                    return;
                }
            }
        }
    }
    next.element.reset();
}
bool UpdateEntityOperator::postRecursion(OctreeElementPointer element) {
    // Post-recursion is the unwinding process. For this operation, while we
    // unwind we want to mark the path as being dirty if we changed it below.
    // We might have two paths, one for the old entity and one for the new entity.
    bool keepSearching = !_foundOld || !_foundNew;

    bool subtreeContainsOld = subTreeContainsOldEntity(element);
    bool subtreeContainsNew = subTreeContainsNewEntity(element);

    // As we unwind, if we're in either of these two paths, we mark our element
    // as dirty.
    if ((_foundOld && subtreeContainsOld) ||
            (_foundNew && subtreeContainsNew)) {
        element->markWithChangedTime();
    }

    // It's not OK to prune if we have the potential of deleting the original containig element.
    // because if we prune the containing element then new might end up reallocating the same memory later 
    // and that will confuse our logic.
    // 
    // it's ok to prune if:
    // 1) we're not removing the old
    // 2) we are removing the old, but this subtree doesn't contain the old
    // 3) we are removing the old, this subtree contains the old, but this element isn't a direct parent of _containingElement
    if (!_removeOld || !subtreeContainsOld || !element->isParentOf(_containingElement)) {
        EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
        entityTreeElement->pruneChildren(); // take this opportunity to prune any empty leaves
    }
    
    return keepSearching; // if we haven't yet found it, keep looking
}
Example #8
0
bool EntityTreeElement::shouldRecurseChildTree(int childIndex, EncodeBitstreamParams& params) const {
    EntityTreeElementPointer childElement = getChildAtIndex(childIndex);
    if (childElement->alreadyFullyEncoded(params)) {
        return false;
    }

    return true; // if we don't know otherwise than recurse!
}
Example #9
0
bool EntityMotionState::entityTreeIsLocked() const {
    EntityTreeElementPointer element = _entity ? _entity->getElement() : nullptr;
    EntityTreePointer tree = element ? element->getTree() : nullptr;
    if (!tree) {
        return true;
    }
    return true;
}
Example #10
0
OctreeElementPointer EntityTree::createNewElement(unsigned char* octalCode) {
    EntityTreeElementPointer newElement = EntityTreeElementPointer(new EntityTreeElement(octalCode),
                                                                   // see comment int EntityTreeElement::createNewElement
                                                                   [=](EntityTreeElement* dyingElement) {
                                                                       EntityTreeElementPointer tmpSharedPointer(dyingElement);
                                                                       dyingElement->notifyDeleteHooks();
                                                                   });
    newElement->setTree(std::static_pointer_cast<EntityTree>(shared_from_this()));
    return std::static_pointer_cast<OctreeElement>(newElement);
}
void MovingEntitiesOperator::addEntityToMoveList(EntityItemPointer entity, const AACube& newCube) {
    EntityTreeElementPointer oldContainingElement = entity->getElement();
    AABox newCubeClamped = newCube.clamp((float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE);

    if (_wantDebug) {
        qCDebug(entities) << "MovingEntitiesOperator::addEntityToMoveList() -----------------------------";
        qCDebug(entities) << "    newCube:" << newCube;
        qCDebug(entities) << "    newCubeClamped:" << newCubeClamped;
        if (oldContainingElement) {
            qCDebug(entities) << "    oldContainingElement:" << oldContainingElement->getAACube();
            qCDebug(entities) << "    oldContainingElement->bestFitBounds(newCubeClamped):" 
                            << oldContainingElement->bestFitBounds(newCubeClamped);
        } else {
            qCDebug(entities) << "    WARNING NO OLD CONTAINING ELEMENT for entity" << entity->getEntityItemID();
        }
    }

    if (!oldContainingElement) {
        return; // bail without adding.
    }

    // If the original containing element is the best fit for the requested newCube locations then
    // we don't actually need to add the entity for moving and we can short circuit all this work
    if (!oldContainingElement->bestFitBounds(newCubeClamped)) {
        // check our tree, to determine if this entity is known
        EntityToMoveDetails details;
        details.oldContainingElement = oldContainingElement;
        details.oldContainingElementCube = oldContainingElement->getAACube();
        details.entity = entity;
        details.oldFound = false;
        details.newFound = false;
        details.newCube = newCube;
        details.newCubeClamped = newCubeClamped;
        _entitiesToMove << details;
        _lookingCount++;

        if (_wantDebug) {
            qCDebug(entities) << "MovingEntitiesOperator::addEntityToMoveList() -----------------------------";
            qCDebug(entities) << "    details.entity:" << details.entity->getEntityItemID();
            qCDebug(entities) << "    details.oldContainingElementCube:" << details.oldContainingElementCube;
            qCDebug(entities) << "    details.newCube:" << details.newCube;
            qCDebug(entities) << "    details.newCubeClamped:" << details.newCubeClamped;
            qCDebug(entities) << "    _lookingCount:" << _lookingCount;
            qCDebug(entities) << "--------------------------------------------------------------------------";
        }
    } else {
        if (_wantDebug) {
            qCDebug(entities) << "    oldContainingElement->bestFitBounds(newCubeClamped) IS BEST FIT... NOTHING TO DO";
        }
    }

    if (_wantDebug) {
        qCDebug(entities) << "--------------------------------------------------------------------------";
    }
}
Example #12
0
EntityItemPointer ObjectDynamic::getEntityByID(EntityItemID entityID) const {
    EntityItemPointer ownerEntity;
    withReadLock([&]{
        ownerEntity = _ownerEntity.lock();
    });
    EntityTreeElementPointer element = ownerEntity ? ownerEntity->getElement() : nullptr;
    EntityTreePointer tree = element ? element->getTree() : nullptr;
    if (!tree) {
        return nullptr;
    }
    return tree->findEntityByID(entityID);
}
Example #13
0
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
    EntityTreeElementPointer containingElement = getContainingElement(entityID);
    if (!containingElement) {
        return false;
    }

    EntityItemPointer existingEntity = containingElement->getEntityWithEntityItemID(entityID);
    if (!existingEntity) {
        return false;
    }

    return updateEntityWithElement(existingEntity, properties, containingElement, senderNode);
}
Example #14
0
UpdateEntityOperator::UpdateEntityOperator(EntityTreePointer tree,
                        EntityTreeElementPointer containingElement,
                        EntityItemPointer existingEntity,
                        const AACube newQueryAACube) :
    _tree(tree),
    _existingEntity(existingEntity),
    _containingElement(containingElement),
    _containingElementCube(containingElement->getAACube()),
    _entityItemID(existingEntity->getEntityItemID()),
    _foundOld(false),
    _foundNew(false),
    _removeOld(false),
    _changeTime(usecTimestampNow()),
    _oldEntityCube(),
    _newEntityCube(),
    _wantDebug(false)
{
    // caller must have verified existence of containingElement and oldEntity
    assert(_containingElement && _existingEntity);

    if (_wantDebug) {
        qCDebug(entities) << "UpdateEntityOperator::UpdateEntityOperator() -----------------------------";
    }

    // Here we have a choice to make, do we want to "tight fit" the actual minimum for the
    // entity into the the element, or do we want to use the entities "relaxed" bounds
    // which can handle all potential rotations?
    // the getMaximumAACube is the relaxed form.
    _oldEntityCube = _existingEntity->getQueryAACube();
    _oldEntityBox = _oldEntityCube.clamp((float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); // clamp to domain bounds

    _newEntityCube = newQueryAACube;
    _newEntityBox = _newEntityCube.clamp((float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); // clamp to domain bounds

    // set oldElementBestFit true if the entity was in the correct element before this operator was run.
    bool oldElementBestFit = _containingElement->bestFitBounds(_oldEntityBox);

    // For some reason we've seen a case where the original containing element isn't a best fit for the old properties
    // in this case we want to move it, even if the properties haven't changed.
    if (!oldElementBestFit) {
        _oldEntityBox = _existingEntity->getElement()->getAACube();
        _removeOld = true; // our properties are going to move us, so remember this for later processing

        if (_wantDebug) {
            qCDebug(entities) << "    **** UNUSUAL CASE ****  not best fit.... **";
        }
    }

    if (_wantDebug) {
        qCDebug(entities) << "    _entityItemID:" << _entityItemID;
        qCDebug(entities) << "    _containingElementCube:" << _containingElementCube;
        qCDebug(entities) << "    _oldEntityCube:" << _oldEntityCube;
        qCDebug(entities) << "    _oldEntityBox:" << _oldEntityBox;
        qCDebug(entities) << "    _newEntityCube:" << _newEntityCube;
        qCDebug(entities) << "    _newEntityBox:" << _newEntityBox;
        qCDebug(entities) << "--------------------------------------------------------------------------";
    }

}
Example #15
0
EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
    EntityItemPointer result = NULL;

    if (getIsClient()) {
        // if our Node isn't allowed to create entities in this domain, don't try.
        auto nodeList = DependencyManager::get<NodeList>();
        if (nodeList && !nodeList->getThisNodeCanRez()) {
            return NULL;
        }
    }

    bool recordCreationTime = false;
    if (properties.getCreated() == UNKNOWN_CREATED_TIME) {
        // the entity's creation time was not specified in properties, which means this is a NEW entity
        // and we must record its creation time
        recordCreationTime = true;
    }

    // You should not call this on existing entities that are already part of the tree! Call updateEntity()
    EntityTreeElementPointer containingElement = getContainingElement(entityID);
    if (containingElement) {
        qCDebug(entities) << "UNEXPECTED!!! ----- don't call addEntity() on existing entity items. entityID=" << entityID
                          << "containingElement=" << containingElement.get();
        return result;
    }

    // construct the instance of the entity
    EntityTypes::EntityType type = properties.getType();
    result = EntityTypes::constructEntityItem(type, entityID, properties);

    if (result) {
        if (recordCreationTime) {
            result->recordCreationTime();
        }
        // Recurse the tree and store the entity in the correct tree element
        AddEntityOperator theOperator(getThisPointer(), result);
        recurseTreeWithOperator(&theOperator);

        postAddEntity(result);
    }
    return result;
}
Example #16
0
void DiffTraversal::Waypoint::getNextVisibleElementRepeat(
        DiffTraversal::VisibleElement& next, const DiffTraversal::View& view, uint64_t lastTime) {
    if (_nextIndex == -1) {
        // root case is special
        ++_nextIndex;
        EntityTreeElementPointer element = _weakElement.lock();
        if (element->getLastChangedContent() > lastTime) {
            next.element = element;
            return;
        }
    }
    if (_nextIndex < NUMBER_OF_CHILDREN) {
        EntityTreeElementPointer element = _weakElement.lock();
        if (element) {
            while (_nextIndex < NUMBER_OF_CHILDREN) {
                EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
                ++_nextIndex;
                if (nextElement &&
                    nextElement->getLastChanged() > lastTime &&
                    view.shouldTraverseElement(*nextElement)) {

                    next.element = nextElement;
                    return;
                }
            }
        }
    }
    next.element.reset();
}
Example #17
0
void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t step) {
    DETAILED_PROFILE_RANGE(simulation_physics, "Bid");
    assert(entityTreeIsLocked());

    updateSendVelocities();

    EntityItemProperties properties;
    Transform localTransform;
    glm::vec3 linearVelocity;
    glm::vec3 angularVelocity;
    _entity->getLocalTransformAndVelocities(localTransform, linearVelocity, angularVelocity);
    properties.setPosition(localTransform.getTranslation());
    properties.setRotation(localTransform.getRotation());
    properties.setVelocity(linearVelocity);
    properties.setAcceleration(_entity->getAcceleration());
    properties.setAngularVelocity(angularVelocity);

    // we don't own the simulation for this entity yet, but we're sending a bid for it
    quint64 now = usecTimestampNow();
    uint8_t finalBidPriority = computeFinalBidPriority();
    _entity->prepareForSimulationOwnershipBid(properties, now, finalBidPriority);

    EntityTreeElementPointer element = _entity->getElement();
    EntityTreePointer tree = element ? element->getTree() : nullptr;

    EntityItemID id(_entity->getID());
    EntityEditPacketSender* entityPacketSender = static_cast<EntityEditPacketSender*>(packetSender);
    entityPacketSender->queueEditEntityMessage(PacketType::EntityPhysics, tree, id, properties);

    // NOTE: we don't descend to children for ownership bid.  Instead, if we win ownership of the parent
    // then in sendUpdate() we'll walk descendents and send updates for their QueryAACubes if necessary.

    _lastStep = step;
    _nextBidExpiry = now + USECS_BETWEEN_OWNERSHIP_BIDS;

    // after sending a bid/update we clear _bumpedPriority
    // which might get promoted again next frame (after local script or simulation interaction)
    // or we might win the bid
    _bumpedPriority = 0;
}
Example #18
0
void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::VisibleElement& next,
        const DiffTraversal::View& view, const DiffTraversal::View& lastView) {
    if (_nextIndex == -1) {
        // root case is special
        ++_nextIndex;
        EntityTreeElementPointer element = _weakElement.lock();
        next.element = element;
        return;
    } else if (_nextIndex < NUMBER_OF_CHILDREN) {
        EntityTreeElementPointer element = _weakElement.lock();
        if (element) {
            while (_nextIndex < NUMBER_OF_CHILDREN) {
                EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
                ++_nextIndex;
                if (nextElement && view.shouldTraverseElement(*nextElement)) {
                    next.element = nextElement;
                    return;
                }
            }
        }
    }
    next.element.reset();
}
Example #19
0
void DiffTraversal::Waypoint::getNextVisibleElementFirstTime(DiffTraversal::VisibleElement& next,
        const DiffTraversal::View& view) {
    // NOTE: no need to set next.intersection in the "FirstTime" context
    if (_nextIndex == -1) {
        // root case is special:
        // its intersection is always INTERSECT,
        // we never bother checking for LOD culling, and
        // we can skip it if the content hasn't changed
        ++_nextIndex;
        next.element = _weakElement.lock();
        return;
    } else if (_nextIndex < NUMBER_OF_CHILDREN) {
        EntityTreeElementPointer element = _weakElement.lock();
        if (element) {
            while (_nextIndex < NUMBER_OF_CHILDREN) {
                EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
                ++_nextIndex;
                if (nextElement) {
                    if (!view.usesViewFrustum) {
                        // No LOD truncation if we aren't using the view frustum
                        next.element = nextElement;
                        return;
                    } else if (view.viewFrustum.cubeIntersectsKeyhole(nextElement->getAACube())) {
                        // check for LOD truncation
                        float distance = glm::distance(view.viewFrustum.getPosition(), nextElement->getAACube().calcCenter()) + MIN_VISIBLE_DISTANCE;
                        float angularDiameter = nextElement->getAACube().getScale() / distance;
                        if (angularDiameter > MIN_ELEMENT_ANGULAR_DIAMETER * view.lodScaleFactor) {
                            next.element = nextElement;
                            return;
                        }
                    }
                }
            }
        }
    }
    next.element.reset();
}
Example #20
0
void DiffTraversal::Waypoint::getNextVisibleElementRepeat(
        DiffTraversal::VisibleElement& next, const DiffTraversal::View& view, uint64_t lastTime) {
    if (_nextIndex == -1) {
        // root case is special
        ++_nextIndex;
        EntityTreeElementPointer element = _weakElement.lock();
        if (element->getLastChangedContent() > lastTime) {
            next.element = element;
            next.intersection = ViewFrustum::INTERSECT;
            return;
        }
    }
    if (_nextIndex < NUMBER_OF_CHILDREN) {
        EntityTreeElementPointer element = _weakElement.lock();
        if (element) {
            while (_nextIndex < NUMBER_OF_CHILDREN) {
                EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
                ++_nextIndex;
                if (nextElement && nextElement->getLastChanged() > lastTime) {
                    if (!view.usesViewFrustum) {
                        // No LOD truncation if we aren't using the view frustum
                        next.element = nextElement;
                        next.intersection = ViewFrustum::INSIDE;
                        return;
                    } else {
                        // check for LOD truncation
                        float distance = glm::distance(view.viewFrustum.getPosition(), nextElement->getAACube().calcCenter()) + MIN_VISIBLE_DISTANCE;
                        float angularDiameter = nextElement->getAACube().getScale() / distance;
                        if (angularDiameter > MIN_ELEMENT_ANGULAR_DIAMETER * view.lodScaleFactor) {
                            ViewFrustum::intersection intersection = view.viewFrustum.calculateCubeKeyholeIntersection(nextElement->getAACube());
                            if (intersection != ViewFrustum::OUTSIDE) {
                                next.element = nextElement;
                                next.intersection = intersection;
                                return;
                            }
                        }
                    }
                }
            }
        }
    }
    next.element.reset();
    next.intersection = ViewFrustum::OUTSIDE;
}
bool MovingEntitiesOperator::preRecursion(const OctreeElementPointer& element) {
    EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
    
    // In Pre-recursion, we're generally deciding whether or not we want to recurse this
    // path of the tree. For this operation, we want to recurse the branch of the tree if
    // any of the following are true:
    //   * We have not yet found the old entity, and this branch contains our old entity
    //   * We have not yet found the new entity, and this branch contains our new entity
    //
    // Note: it's often the case that the branch in question contains both the old entity
    // and the new entity.
    
    bool keepSearching = (_foundOldCount < _lookingCount) || (_foundNewCount < _lookingCount);

    // If we haven't yet found all the entities, and this sub tree contains at least one of our
    // entities, then we need to keep searching.
    if (keepSearching && shouldRecurseSubTree(element)) {

        // check against each of our search entities
        int detailIndex = 0;
        foreach(const EntityToMoveDetails& details, _entitiesToMove) {
        
            if (_wantDebug) {
                qCDebug(entities) << "MovingEntitiesOperator::preRecursion() details["<< detailIndex <<"]-----------------------------";
                qCDebug(entities) << "    entityTreeElement:" << entityTreeElement->getAACube();
                qCDebug(entities) << "    entityTreeElement->bestFitBounds(details.newCube):" << entityTreeElement->bestFitBounds(details.newCube);
                qCDebug(entities) << "    details.entity:" << details.entity->getEntityItemID();
                qCDebug(entities) << "    details.oldContainingElementCube:" << details.oldContainingElementCube;
                qCDebug(entities) << "    entityTreeElement:" << entityTreeElement.get();
                qCDebug(entities) << "    details.newCube:" << details.newCube;
                qCDebug(entities) << "    details.newCubeClamped:" << details.newCubeClamped;
                qCDebug(entities) << "    _lookingCount:" << _lookingCount;
                qCDebug(entities) << "    _foundOldCount:" << _foundOldCount;
                qCDebug(entities) << "--------------------------------------------------------------------------";
            }
        

            // If this is one of the old elements we're looking for, then ask it to remove the old entity
            if (!details.oldFound && entityTreeElement == details.oldContainingElement) {
                // DO NOT remove the entity here.  It will be removed when added to the destination element.
                _foundOldCount++;
                //details.oldFound = true; // TODO: would be nice to add this optimization
                if (_wantDebug) {
                    qCDebug(entities) << "MovingEntitiesOperator::preRecursion() -----------------------------";
                    qCDebug(entities) << "    FOUND OLD - REMOVING";
                    qCDebug(entities) << "    entityTreeElement == details.oldContainingElement";
                    qCDebug(entities) << "--------------------------------------------------------------------------";
                }
            }

            // If this element is the best fit for the new bounds of this entity then add the entity to the element
            if (!details.newFound && entityTreeElement->bestFitBounds(details.newCube)) {
                // remove from the old before adding
                EntityTreeElementPointer oldElement = details.entity->getElement();
                if (oldElement != entityTreeElement) {
                    if (oldElement) {
                        oldElement->removeEntityItem(details.entity);
                    }
                    entityTreeElement->addEntityItem(details.entity);
                } else {
                    entityTreeElement->bumpChangedContent();
                }
                _foundNewCount++;
                //details.newFound = true; // TODO: would be nice to add this optimization
                if (_wantDebug) {
                    qCDebug(entities) << "MovingEntitiesOperator::preRecursion() -----------------------------";
                    qCDebug(entities) << "    FOUND NEW - ADDING";
                    qCDebug(entities) << "    entityTreeElement->bestFitBounds(details.newCube)";
                    qCDebug(entities) << "--------------------------------------------------------------------------";
                }
            }
            detailIndex++;
        }
        // if we haven't found all of our search for entities, then keep looking
        keepSearching = (_foundOldCount < _lookingCount) || (_foundNewCount < _lookingCount);
    }
Example #22
0
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
    assert(_entity);
    assert(entityTreeIsLocked());

    if (!_body->isActive()) {
        // make sure all derivatives are zero
        _entity->setVelocity(Vectors::ZERO);
        _entity->setAngularVelocity(Vectors::ZERO);
        _entity->setAcceleration(Vectors::ZERO);
        _numInactiveUpdates++;
    } else {
        glm::vec3 gravity = _entity->getGravity();

        // if this entity has been accelerated at close to gravity for a certain number of simulation-steps, let
        // the entity server's estimates include gravity.
        const uint8_t STEPS_TO_DECIDE_BALLISTIC = 4;
        if (_accelerationNearlyGravityCount >= STEPS_TO_DECIDE_BALLISTIC) {
            _entity->setAcceleration(gravity);
        } else {
            _entity->setAcceleration(Vectors::ZERO);
        }

        if (!_body->isStaticOrKinematicObject()) {
            const float DYNAMIC_LINEAR_VELOCITY_THRESHOLD = 0.05f;  // 5 cm/sec
            const float DYNAMIC_ANGULAR_VELOCITY_THRESHOLD = 0.087266f;  // ~5 deg/sec

            bool movingSlowlyLinear =
                glm::length2(_entity->getVelocity()) < (DYNAMIC_LINEAR_VELOCITY_THRESHOLD * DYNAMIC_LINEAR_VELOCITY_THRESHOLD);
            bool movingSlowlyAngular = glm::length2(_entity->getAngularVelocity()) <
                    (DYNAMIC_ANGULAR_VELOCITY_THRESHOLD * DYNAMIC_ANGULAR_VELOCITY_THRESHOLD);
            bool movingSlowly = movingSlowlyLinear && movingSlowlyAngular && _entity->getAcceleration() == Vectors::ZERO;

            if (movingSlowly) {
                // velocities might not be zero, but we'll fake them as such, which will hopefully help convince
                // other simulating observers to deactivate their own copies
                glm::vec3 zero(0.0f);
                _entity->setVelocity(zero);
                _entity->setAngularVelocity(zero);
            }
        }
        _numInactiveUpdates = 0;
    }

    // remember properties for local server prediction
    Transform localTransform;
    _entity->getLocalTransformAndVelocities(localTransform, _serverVelocity, _serverAngularVelocity);
    _serverPosition = localTransform.getTranslation();
    _serverRotation = localTransform.getRotation();
    _serverAcceleration = _entity->getAcceleration();
    _serverActionData = _entity->getActionData();

    EntityItemProperties properties;

    // explicitly set the properties that changed so that they will be packed
    properties.setPosition(_entity->getLocalPosition());
    properties.setRotation(_entity->getLocalOrientation());

    properties.setVelocity(_serverVelocity);
    properties.setAcceleration(_serverAcceleration);
    properties.setAngularVelocity(_serverAngularVelocity);
    if (_entity->actionDataNeedsTransmit()) {
        _entity->setActionDataNeedsTransmit(false);
        properties.setActionData(_serverActionData);
    }

    if (properties.parentRelatedPropertyChanged() && _entity->computePuffedQueryAACube()) {
        // due to parenting, the server may not know where something is in world-space, so include the bounding cube.
        properties.setQueryAACube(_entity->getQueryAACube());
    }

    // set the LastEdited of the properties but NOT the entity itself
    quint64 now = usecTimestampNow();
    properties.setLastEdited(now);

    #ifdef WANT_DEBUG
        quint64 lastSimulated = _entity->getLastSimulated();
        qCDebug(physics) << "EntityMotionState::sendUpdate()";
        qCDebug(physics) << "        EntityItemId:" << _entity->getEntityItemID()
                         << "---------------------------------------------";
        qCDebug(physics) << "       lastSimulated:" << debugTime(lastSimulated, now);
    #endif //def WANT_DEBUG

    if (_numInactiveUpdates > 0) {
        // we own the simulation but the entity has stopped so we tell the server we're clearing simulatorID
        // but we remember we do still own it...  and rely on the server to tell us we don't
        properties.clearSimulationOwner();
        _outgoingPriority = 0;
        _entity->setPendingOwnershipPriority(_outgoingPriority, now);
    } else if (Physics::getSessionUUID() != _entity->getSimulatorID()) {
        // we don't own the simulation for this entity yet, but we're sending a bid for it
        quint8 bidPriority = glm::max<uint8_t>(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY);
        properties.setSimulationOwner(Physics::getSessionUUID(), bidPriority);
        _nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS;
        // copy _outgoingPriority into pendingPriority...
        _entity->setPendingOwnershipPriority(_outgoingPriority, now);
        // ...then reset _outgoingPriority in preparation for the next frame
        _outgoingPriority = 0;
    } else if (_outgoingPriority != _entity->getSimulationPriority()) {
        // we own the simulation but our desired priority has changed
        if (_outgoingPriority == 0) {
            // we should release ownership
            properties.clearSimulationOwner();
        } else {
            // we just need to change the priority
            properties.setSimulationOwner(Physics::getSessionUUID(), _outgoingPriority);
        }
        _entity->setPendingOwnershipPriority(_outgoingPriority, now);
    }

    EntityItemID id(_entity->getID());
    EntityEditPacketSender* entityPacketSender = static_cast<EntityEditPacketSender*>(packetSender);
    #ifdef WANT_DEBUG
        qCDebug(physics) << "EntityMotionState::sendUpdate()... calling queueEditEntityMessage()...";
    #endif

    EntityTreeElementPointer element = _entity->getElement();
    EntityTreePointer tree = element ? element->getTree() : nullptr;

    properties.setClientOnly(_entity->getClientOnly());
    properties.setOwningAvatarID(_entity->getOwningAvatarID());

    entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, tree, id, properties);
    _entity->setLastBroadcast(now);

    // if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server
    // if they've changed.
    _entity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
        if (descendant->getNestableType() == NestableType::Entity) {
            EntityItemPointer entityDescendant = std::static_pointer_cast<EntityItem>(descendant);
            if (descendant->computePuffedQueryAACube()) {
                EntityItemProperties newQueryCubeProperties;
                newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube());
                newQueryCubeProperties.setLastEdited(properties.getLastEdited());

                newQueryCubeProperties.setClientOnly(entityDescendant->getClientOnly());
                newQueryCubeProperties.setOwningAvatarID(entityDescendant->getOwningAvatarID());

                entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, tree,
                                                           descendant->getID(), newQueryCubeProperties);
                entityDescendant->setLastBroadcast(now);
            }
        }
    });

    _lastStep = step;
}
Example #23
0
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
    DETAILED_PROFILE_RANGE(simulation_physics, "Send");
    assert(entityTreeIsLocked());
    assert(isLocallyOwned());

    updateSendVelocities();

    // remember _serverFoo data for local prediction of server state
    Transform localTransform;
    _entity->getLocalTransformAndVelocities(localTransform, _serverVelocity, _serverAngularVelocity);
    _serverPosition = localTransform.getTranslation();
    _serverRotation = localTransform.getRotation();
    _serverAcceleration = _entity->getAcceleration();
    _serverActionData = _entity->getDynamicData();

    EntityItemProperties properties;
    properties.setPosition(_entity->getLocalPosition());
    properties.setRotation(_entity->getLocalOrientation());
    properties.setVelocity(_serverVelocity);
    properties.setAcceleration(_serverAcceleration);
    properties.setAngularVelocity(_serverAngularVelocity);
    if (_entity->dynamicDataNeedsTransmit()) {
        _entity->setDynamicDataNeedsTransmit(false);
        properties.setActionData(_serverActionData);
    }

    if (_entity->updateQueryAACube()) {
        // due to parenting, the server may not know where something is in world-space, so include the bounding cube.
        properties.setQueryAACube(_entity->getQueryAACube());
    }

    // set the LastEdited of the properties but NOT the entity itself
    quint64 now = usecTimestampNow();
    properties.setLastEdited(now);
    _entity->setSimulationOwnershipExpiry(now + MAX_OUTGOING_SIMULATION_UPDATE_PERIOD);

    if (_numInactiveUpdates > 0 && _entity->getScriptSimulationPriority() == 0) {
        // the entity is stopped and inactive so we tell the server we're clearing simulatorID
        // but we remember we do still own it...  and rely on the server to tell us we don't
        properties.clearSimulationOwner();
        _entity->setPendingOwnershipPriority(0);
    } else {
        uint8_t newPriority = computeFinalBidPriority();
        _entity->clearScriptSimulationPriority();
        // if we get here then we own the simulation and the object is NOT going inactive
        // if newPriority is zero, then it must be outside of R1, which means we should really set it to YIELD
        // which we achive by just setting it to the max of the two
        newPriority = glm::max(newPriority, YIELD_SIMULATION_PRIORITY);
        if (newPriority != _entity->getSimulationPriority() &&
                !(newPriority == VOLUNTEER_SIMULATION_PRIORITY && _entity->getSimulationPriority() == RECRUIT_SIMULATION_PRIORITY)) {
            // our desired priority has changed
            if (newPriority == 0) {
                // we should release ownership
                properties.clearSimulationOwner();
            } else {
                // we just need to inform the entity-server
                properties.setSimulationOwner(Physics::getSessionUUID(), newPriority);
            }
            _entity->setPendingOwnershipPriority(newPriority);
        }
    }

    EntityItemID id(_entity->getID());
    EntityEditPacketSender* entityPacketSender = static_cast<EntityEditPacketSender*>(packetSender);

    EntityTreeElementPointer element = _entity->getElement();
    EntityTreePointer tree = element ? element->getTree() : nullptr;

    properties.setEntityHostType(_entity->getEntityHostType());
    properties.setOwningAvatarID(_entity->getOwningAvatarID());

    entityPacketSender->queueEditEntityMessage(PacketType::EntityPhysics, tree, id, properties);
    _entity->setLastBroadcast(now); // for debug/physics status icons

    // if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server
    // if they've changed.
    _entity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
        if (descendant->getNestableType() == NestableType::Entity) {
            EntityItemPointer entityDescendant = std::static_pointer_cast<EntityItem>(descendant);
            if (descendant->updateQueryAACube()) {
                EntityItemProperties newQueryCubeProperties;
                newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube());
                newQueryCubeProperties.setLastEdited(properties.getLastEdited());
                newQueryCubeProperties.setEntityHostType(entityDescendant->getEntityHostType());
                newQueryCubeProperties.setOwningAvatarID(entityDescendant->getOwningAvatarID());

                entityPacketSender->queueEditEntityMessage(PacketType::EntityPhysics, tree,
                                                           descendant->getID(), newQueryCubeProperties);
                entityDescendant->setLastBroadcast(now); // for debug/physics status icons
            }
        }
    });

    _lastStep = step;

    // after sending a bid/update we clear _bumpedPriority
    // which might get promoted again next frame (after local script or simulation interaction)
    // or we might win the bid
    _bumpedPriority = 0;
}
Example #24
0
OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData* packetData,
                                                                    EncodeBitstreamParams& params) const {

    OctreeElement::AppendState appendElementState = OctreeElement::COMPLETED; // assume the best...

    // first, check the params.extraEncodeData to see if there's any partial re-encode data for this element
    OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
    EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData = NULL;
    bool hadElementExtraData = false;
    if (extraEncodeData && extraEncodeData->contains(this)) {
        entityTreeElementExtraEncodeData =
            static_cast<EntityTreeElementExtraEncodeData*>(extraEncodeData->value(this));
        hadElementExtraData = true;
    } else {
        // if there wasn't one already, then create one
        entityTreeElementExtraEncodeData = new EntityTreeElementExtraEncodeData();
        entityTreeElementExtraEncodeData->elementCompleted = !hasContent();

        for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
            EntityTreeElementPointer child = getChildAtIndex(i);
            if (!child) {
                entityTreeElementExtraEncodeData->childCompleted[i] = true; // if no child exists, it is completed
            } else {
                if (child->hasEntities()) {
                    entityTreeElementExtraEncodeData->childCompleted[i] = false;
                } else {
                    // if the child doesn't have enities, it is completed
                    entityTreeElementExtraEncodeData->childCompleted[i] = true;
                }
            }
        }
        forEachEntity([&](EntityItemPointer entity) {
            entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params));
        });
    }

    //assert(extraEncodeData);
    //assert(extraEncodeData->contains(this));
    //entityTreeElementExtraEncodeData = static_cast<EntityTreeElementExtraEncodeData*>(extraEncodeData->value(this));

    LevelDetails elementLevel = packetData->startLevel();

    // write our entities out... first determine which of the entities are in view based on our params
    uint16_t numberOfEntities = 0;
    uint16_t actualNumberOfEntities = 0;
    int numberOfEntitiesOffset = 0;
    withReadLock([&] {
        QVector<uint16_t> indexesOfEntitiesToInclude;

        // It's possible that our element has been previous completed. In this case we'll simply not include any of our
        // entities for encoding. This is needed because we encode the element data at the "parent" level, and so we
        // need to handle the case where our sibling elements need encoding but we don't.
        if (!entityTreeElementExtraEncodeData->elementCompleted) {
            for (uint16_t i = 0; i < _entityItems.size(); i++) {
                EntityItemPointer entity = _entityItems[i];
                bool includeThisEntity = true;

                if (!params.forceSendScene && entity->getLastChangedOnServer() < params.lastViewFrustumSent) {
                    includeThisEntity = false;
                }

                if (hadElementExtraData) {
                    includeThisEntity = includeThisEntity &&
                        entityTreeElementExtraEncodeData->entities.contains(entity->getEntityItemID());
                }

                if (includeThisEntity && params.viewFrustum) {

                    // we want to use the maximum possible box for this, so that we don't have to worry about the nuance of
                    // simulation changing what's visible. consider the case where the entity contains an angular velocity
                    // the entity may not be in view and then in view a frame later, let the client side handle it's view
                    // frustum culling on rendering.
                    bool success;
                    AACube entityCube = entity->getQueryAACube(success);
                    if (!success || !params.viewFrustum->cubeIntersectsKeyhole(entityCube)) {
                        includeThisEntity = false; // out of view, don't include it
                    } else {
                        // Check the size of the entity, it's possible that a "too small to see" entity is included in a
                        // larger octree cell because of its position (for example if it crosses the boundary of a cell it
                        // pops to the next higher cell. So we want to check to see that the entity is large enough to be seen
                        // before we consider including it.
                        success = true;
                        // we can't cull a parent-entity by its dimensions because the child may be larger.  we need to
                        // avoid sending details about a child but not the parent.  the parent's queryAACube should have
                        // been adjusted to encompass the queryAACube of the child.
                        AABox entityBounds = entity->hasChildren() ? AABox(entityCube) : entity->getAABox(success);
                        if (!success) {
                            // if this entity is a child of an avatar, the entity-server wont be able to determine its
                            // AABox.  If this happens, fall back to the queryAACube.
                            entityBounds = AABox(entityCube);
                        }
                        auto renderAccuracy = params.viewFrustum->calculateRenderAccuracy(entityBounds,
                                                                                          params.octreeElementSizeScale,
                                                                                          params.boundaryLevelAdjust);
                        if (renderAccuracy <= 0.0f) {
                            includeThisEntity = false; // too small, don't include it

                            #ifdef WANT_LOD_DEBUGGING
                            qDebug() << "skipping entity - TOO SMALL - \n"
                                     << "......id:" << entity->getID() << "\n"
                                     << "....name:" << entity->getName() << "\n"
                                     << "..bounds:" << entityBounds << "\n"
                                     << "....cell:" << getAACube();
                            #endif
                        }
                    }
                }

                if (includeThisEntity) {
                    #ifdef WANT_LOD_DEBUGGING
                    qDebug() << "including entity - \n"
                        << "......id:" << entity->getID() << "\n"
                        << "....name:" << entity->getName() << "\n"
                        << "....cell:" << getAACube();
                    #endif
                    indexesOfEntitiesToInclude << i;
                    numberOfEntities++;
                } else {
                    // if the extra data included this entity, and we've decided to not include the entity, then
                    // we can treat it as if it was completed.
                    entityTreeElementExtraEncodeData->entities.remove(entity->getEntityItemID());
                }
            }
        }

        numberOfEntitiesOffset = packetData->getUncompressedByteOffset();
        bool successAppendEntityCount = packetData->appendValue(numberOfEntities);

        if (successAppendEntityCount) {
            foreach(uint16_t i, indexesOfEntitiesToInclude) {
                EntityItemPointer entity = _entityItems[i];
                LevelDetails entityLevel = packetData->startLevel();
                OctreeElement::AppendState appendEntityState = entity->appendEntityData(packetData,
                    params, entityTreeElementExtraEncodeData);

                // If none of this entity data was able to be appended, then discard it
                // and don't include it in our entity count
                if (appendEntityState == OctreeElement::NONE) {
                    packetData->discardLevel(entityLevel);
                } else {
                    // If either ALL or some of it got appended, then end the level (commit it)
                    // and include the entity in our final count of entities
                    packetData->endLevel(entityLevel);
                    actualNumberOfEntities++;
                }

                // If the entity item got completely appended, then we can remove it from the extra encode data
                if (appendEntityState == OctreeElement::COMPLETED) {
                    entityTreeElementExtraEncodeData->entities.remove(entity->getEntityItemID());
                }

                // If any part of the entity items didn't fit, then the element is considered partial
                // NOTE: if the entity item didn't fit or only partially fit, then the entity item should have
                // added itself to the extra encode data.
                if (appendEntityState != OctreeElement::COMPLETED) {
                    appendElementState = OctreeElement::PARTIAL;
                }
            }
        } else {
Example #25
0
void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params) const {
    const bool wantDebug = false;

    if (wantDebug) {
        qCDebug(entities) << "EntityTreeElement::elementEncodeComplete() element:" << _cube;
    }

    OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
    assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
    assert(extraEncodeData->contains(this));

    EntityTreeElementExtraEncodeData* thisExtraEncodeData
                = static_cast<EntityTreeElementExtraEncodeData*>(extraEncodeData->value(this));

    // Note: this will be called when OUR element has finished running through encodeTreeBitstreamRecursion()
    // which means, it's possible that our parent element hasn't finished encoding OUR data... so
    // in this case, our children may be complete, and we should clean up their encode data...
    // but not necessarily cleanup our own encode data...
    //
    // If we're really complete here's what must be true...
    //    1) our own data must be complete
    //    2) the data for all our immediate children must be complete.
    // However, the following might also be the case...
    //    1) it's ok for our child trees to not yet be fully encoded/complete...
    //       SO LONG AS... the our child's node is in the bag ready for encoding

    bool someChildTreeNotComplete = false;
    for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
        EntityTreeElementPointer childElement = getChildAtIndex(i);
        if (childElement) {

            // 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.
            if (extraEncodeData->contains(childElement.get())) {
                EntityTreeElementExtraEncodeData* childExtraEncodeData
                    = static_cast<EntityTreeElementExtraEncodeData*>(extraEncodeData->value(childElement.get()));

                if (wantDebug) {
                    qCDebug(entities) << "checking child: " << childElement->_cube;
                    qCDebug(entities) << "    childElement->isLeaf():" << childElement->isLeaf();
                    qCDebug(entities) << "    childExtraEncodeData->elementCompleted:" << childExtraEncodeData->elementCompleted;
                    qCDebug(entities) << "    childExtraEncodeData->subtreeCompleted:" << childExtraEncodeData->subtreeCompleted;
                }

                if (childElement->isLeaf() && childExtraEncodeData->elementCompleted) {
                    if (wantDebug) {
                        qCDebug(entities) << "    CHILD IS LEAF -- AND CHILD ELEMENT DATA COMPLETED!!!";
                    }
                    childExtraEncodeData->subtreeCompleted = true;
                }

                if (!childExtraEncodeData->elementCompleted || !childExtraEncodeData->subtreeCompleted) {
                    someChildTreeNotComplete = true;
                }
            }
        }
    }

    if (wantDebug) {
        qCDebug(entities) << "for this element: " << _cube;
        qCDebug(entities) << "    WAS elementCompleted:" << thisExtraEncodeData->elementCompleted;
        qCDebug(entities) << "    WAS subtreeCompleted:" << thisExtraEncodeData->subtreeCompleted;
    }

    thisExtraEncodeData->subtreeCompleted = !someChildTreeNotComplete;

    if (wantDebug) {
        qCDebug(entities) << "    NOW elementCompleted:" << thisExtraEncodeData->elementCompleted;
        qCDebug(entities) << "    NOW subtreeCompleted:" << thisExtraEncodeData->subtreeCompleted;

        if (thisExtraEncodeData->subtreeCompleted) {
            qCDebug(entities) << "    YEAH!!!!! >>>>>>>>>>>>>> NOW subtreeCompleted:" << thisExtraEncodeData->subtreeCompleted;
        }
    }
}
UpdateEntityOperator::UpdateEntityOperator(EntityTreePointer tree,
                        EntityTreeElementPointer containingElement,
                        EntityItemPointer existingEntity,
                        const EntityItemProperties& properties) :
    _tree(tree),
    _existingEntity(existingEntity),
    _containingElement(containingElement),
    _containingElementCube(containingElement->getAACube()),
    _properties(properties),
    _entityItemID(existingEntity->getEntityItemID()),
    _foundOld(false),
    _foundNew(false),
    _removeOld(false),
    _dontMove(false), // assume we'll be moving
    _changeTime(usecTimestampNow()),
    _oldEntityCube(),
    _newEntityCube(),
    _wantDebug(false)
{
    // caller must have verified existence of containingElement and oldEntity
    assert(_containingElement && _existingEntity);

    if (_wantDebug) {
        qCDebug(entities) << "UpdateEntityOperator::UpdateEntityOperator() -----------------------------";
    }

    // Here we have a choice to make, do we want to "tight fit" the actual minimum for the
    // entity into the the element, or do we want to use the entities "relaxed" bounds
    // which can handle all potential rotations?
    // the getMaximumAACube is the relaxed form.
    _oldEntityCube = _existingEntity->getMaximumAACube();
    _oldEntityBox = _oldEntityCube.clamp((float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); // clamp to domain bounds

    // If the old properties doesn't contain the properties required to calculate a bounding box,
    // get them from the existing entity. Registration point is required to correctly calculate
    // the bounding box.
    if (!_properties.registrationPointChanged()) {
        _properties.setRegistrationPoint(_existingEntity->getRegistrationPoint());
    }
    
    // If the new properties has position OR dimension changes, but not both, we need to
    // get the old property value and set it in our properties in order for our bounds
    // calculations to work.
    if (_properties.containsPositionChange() && !_properties.containsDimensionsChange()) {
        glm::vec3 oldDimensions= _existingEntity->getDimensions();
        _properties.setDimensions(oldDimensions);

        if (_wantDebug) {
            qCDebug(entities) << "    ** setting properties dimensions - had position change, no dimension change **";
        }

    }
    if (!_properties.containsPositionChange() && _properties.containsDimensionsChange()) {
        glm::vec3 oldPosition= _existingEntity->getPosition();
        _properties.setPosition(oldPosition);

        if (_wantDebug) {
            qCDebug(entities) << "    ** setting properties position - had dimensions change, no position change **";
        }
    }

    // If our new properties don't have bounds details (no change to position, etc) or if this containing element would 
    // be the best fit for our new properties, then just do the new portion of the store pass, since the change path will 
    // be the same for both parts of the update
    bool oldElementBestFit = _containingElement->bestFitBounds(_properties);
    
    // if we don't have bounds properties, then use our old clamped box to determine best fit
    if (!_properties.containsBoundsProperties()) {
        oldElementBestFit = _containingElement->bestFitBounds(_oldEntityBox);

        if (_wantDebug) {
            qCDebug(entities) << "    ** old Element best fit - no dimensions change, no position change **";
        }

    }
    
    // For some reason we've seen a case where the original containing element isn't a best fit for the old properties
    // in this case we want to move it, even if the properties haven't changed.
    if (!_properties.containsBoundsProperties() && !oldElementBestFit) {
        _newEntityCube = _oldEntityCube;
        _removeOld = true; // our properties are going to move us, so remember this for later processing

        if (_wantDebug) {
            qCDebug(entities) << "    **** UNUSUAL CASE ****  no changes, but not best fit... consider it a move.... **";
        }


    } else if (!_properties.containsBoundsProperties() || oldElementBestFit) {
        _foundOld = true;
        _newEntityCube = _oldEntityCube;
        _dontMove = true;

        if (_wantDebug) {
            qCDebug(entities) << "    **** TYPICAL NO MOVE CASE ****";
            qCDebug(entities) << "        _properties.containsBoundsProperties():" << _properties.containsBoundsProperties();
            qCDebug(entities) << "                             oldElementBestFit:" << oldElementBestFit;
        }

    } else {
        _newEntityCube = _properties.getMaximumAACube();
        _removeOld = true; // our properties are going to move us, so remember this for later processing

        if (_wantDebug) {
            qCDebug(entities) << "    **** TYPICAL MOVE CASE ****";
        }
    }

    _newEntityBox = _newEntityCube.clamp((float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); // clamp to domain bounds


    if (_wantDebug) {
        qCDebug(entities) << "    _entityItemID:" << _entityItemID;
        qCDebug(entities) << "    _containingElementCube:" << _containingElementCube;
        qCDebug(entities) << "    _oldEntityCube:" << _oldEntityCube;
        qCDebug(entities) << "    _oldEntityBox:" << _oldEntityBox;
        qCDebug(entities) << "    _newEntityCube:" << _newEntityCube;
        qCDebug(entities) << "    _newEntityBox:" << _newEntityBox;
        qCDebug(entities) << "--------------------------------------------------------------------------";
    }

}
bool UpdateEntityOperator::preRecursion(OctreeElementPointer element) {
    EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
    
    // In Pre-recursion, we're generally deciding whether or not we want to recurse this
    // path of the tree. For this operation, we want to recurse the branch of the tree if
    // and of the following are true:
    //   * We have not yet found the old entity, and this branch contains our old entity
    //   * We have not yet found the new entity, and this branch contains our new entity
    //
    // Note: it's often the case that the branch in question contains both the old entity
    // and the new entity.
    
    bool keepSearching = false; // assume we don't need to search any more

    bool subtreeContainsOld = subTreeContainsOldEntity(element);
    bool subtreeContainsNew = subTreeContainsNewEntity(element);

    if (_wantDebug) {
        qCDebug(entities) << "---- UpdateEntityOperator::preRecursion().... ----";
        qCDebug(entities) << "    element=" << element->getAACube();
        qCDebug(entities) << "    subtreeContainsOld=" << subtreeContainsOld;
        qCDebug(entities) << "    subtreeContainsNew=" << subtreeContainsNew;
        qCDebug(entities) << "    _foundOld=" << _foundOld;
        qCDebug(entities) << "    _foundNew=" << _foundNew;
    }

    // If we haven't yet found the old entity, and this subTreeContains our old
    // entity, then we need to keep searching.
    if (!_foundOld && subtreeContainsOld) {

        if (_wantDebug) {
            qCDebug(entities) << "    OLD TREE CASE....";
            qCDebug(entities) << "    entityTreeElement=" << entityTreeElement.get();
            qCDebug(entities) << "    _containingElement=" << _containingElement.get();
        }

        // If this is the element we're looking for, then ask it to remove the old entity
        // and we can stop searching.
        if (entityTreeElement == _containingElement) {

            if (_wantDebug) {
                qCDebug(entities) << "    *** it's the OLD ELEMENT! ***";
            }

            // If the containgElement IS NOT the best fit for the new entity properties
            // then we need to remove it, and the updateEntity below will store it in the
            // correct element.
            if (_removeOld) {

                if (_wantDebug) {
                    qCDebug(entities) << "    *** REMOVING from ELEMENT ***";
                }

                // the entity knows what element it's in, so we remove it from that one
                // NOTE: we know we haven't yet added it to its new element because _removeOld is true
                EntityTreeElementPointer oldElement = _existingEntity->getElement();
                oldElement->removeEntityItem(_existingEntity);
                _tree->setContainingElement(_entityItemID, NULL);

                if (oldElement != _containingElement) {
                    qCDebug(entities) << "WARNING entity moved during UpdateEntityOperator recursion";
                    _containingElement->removeEntityItem(_existingEntity);
                }

                if (_wantDebug) {
                    qCDebug(entities) << "    *** REMOVING from MAP ***";
                }
            }
            _foundOld = true;
        } else {
            // if this isn't the element we're looking for, then keep searching
            keepSearching = true;
        }
    }

    // If we haven't yet found the new entity,  and this subTreeContains our new
    // entity, then we need to keep searching.
    if (!_foundNew && subtreeContainsNew) {

        if (_wantDebug) {
            qCDebug(entities) << "    NEW TREE CASE....";
            qCDebug(entities) << "    entityTreeElement=" << entityTreeElement.get();
            qCDebug(entities) << "    _containingElement=" << _containingElement.get();
            qCDebug(entities) << "    entityTreeElement->bestFitBounds(_newEntityBox)=" << entityTreeElement->bestFitBounds(_newEntityBox);
        }

        // If this element is the best fit for the new entity properties, then add/or update it
        if (entityTreeElement->bestFitBounds(_newEntityBox)) {

            if (_wantDebug) {
                qCDebug(entities) << "    *** THIS ELEMENT IS BEST FIT ***";
            }

            EntityTreeElementPointer oldElement = _existingEntity->getElement();
            // if we are the existing containing element, then we can just do the update of the entity properties
            if (entityTreeElement == oldElement) {

                if (_wantDebug) {
                    qCDebug(entities) << "    *** This is the same OLD ELEMENT ***";
                }
            
                // set the entity properties and mark our element as changed.
                _existingEntity->setProperties(_properties);
                if (_wantDebug) {
                    qCDebug(entities) << "    *** set properties ***";
                }
            } else {
                // otherwise, this is an add case.
                if (oldElement) {
                    oldElement->removeEntityItem(_existingEntity);
                    if (oldElement != _containingElement) {
                        qCDebug(entities) << "WARNING entity moved during UpdateEntityOperator recursion";
                    }
                }
                entityTreeElement->addEntityItem(_existingEntity);
                _tree->setContainingElement(_entityItemID, entityTreeElement);

                _existingEntity->setProperties(_properties); // still need to update the properties!
                if (_wantDebug) {
                    qCDebug(entities) << "    *** ADDING ENTITY to ELEMENT and MAP and SETTING PROPERTIES ***";
                }
            }
            _foundNew = true; // we found the new element
            _removeOld = false; // and it has already been removed from the old
        } else {
            keepSearching = true;
        }
    }

    if (_wantDebug) {
        qCDebug(entities) << "    FINAL --- keepSearching=" << keepSearching;
        qCDebug(entities) << "--------------------------------------------------";
    }

    
    return keepSearching; // if we haven't yet found it, keep looking
}