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) << "--------------------------------------------------------------------------"; } }
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; }
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) << "--------------------------------------------------------------------------"; } }
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(); }
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; }
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 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); }