OctreeElementPointer OctreeElement::getOrCreateChildElementContaining(const AABox& box) { OctreeElementPointer child = NULL; int childIndex = getMyChildContaining(box); // If getMyChildContaining() returns CHILD_UNKNOWN then it means that our level // is the correct level for this cube if (childIndex == CHILD_UNKNOWN) { return shared_from_this(); } // Now, check if we have a child at that location child = getChildAtIndex(childIndex); if (!child) { child = addChildAtIndex(childIndex); } // if we've made a really small child, then go ahead and use that one. if (child->getScale() <= SMALLEST_REASONABLE_OCTREE_ELEMENT_SCALE) { return child; } // Now that we have the child to recurse down, let it answer the original question... return child->getOrCreateChildElementContaining(box); }
// TODO: consider removing this, or switching to using getOrCreateChildElementContaining(const AACube& box)... OctreeElementPointer OctreeElement::getOrCreateChildElementAt(float x, float y, float z, float s) { OctreeElementPointer child = NULL; // If the requested size is less than or equal to our scale, but greater than half our scale, then // we are the Element they are looking for. float ourScale = getScale(); float halfOurScale = ourScale / 2.0f; if(s > ourScale) { qCDebug(octree, "UNEXPECTED -- OctreeElement::getOrCreateChildElementAt() s=[%f] > ourScale=[%f] ", (double)s, (double)ourScale); } if (s > halfOurScale) { return shared_from_this(); } int childIndex = getMyChildContainingPoint(glm::vec3(x, y, z)); // Now, check if we have a child at that location child = getChildAtIndex(childIndex); if (!child) { child = addChildAtIndex(childIndex); } // Now that we have the child to recurse down, let it answer the original question... return child->getOrCreateChildElementAt(x, y, z, s); }
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 }
// does this entity tree element contain the old entity bool MovingEntitiesOperator::shouldRecurseSubTree(const OctreeElementPointer& element) { bool containsEntity = false; // If we don't have an old entity, then we don't contain the entity, otherwise // check the bounds if (_entitiesToMove.size() > 0) { const AACube& elementCube = element->getAACube(); int detailIndex = 0; foreach(const EntityToMoveDetails& details, _entitiesToMove) { if (_wantDebug) { qCDebug(entities) << "MovingEntitiesOperator::shouldRecurseSubTree() details["<< detailIndex <<"]-----------------------------"; qCDebug(entities) << " element:" << element->getAACube(); 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) << " elementCube.contains(details.newCube)" << elementCube.contains(details.newCube); qCDebug(entities) << " elementCube.contains(details.newCubeClamped)" << elementCube.contains(details.newCubeClamped); qCDebug(entities) << "--------------------------------------------------------------------------"; } if (elementCube.contains(details.oldContainingElementCube) || elementCube.contains(details.newCubeClamped)) { containsEntity = true; break; // if it contains at least one, we're good to go } detailIndex++; } } return containsEntity; }
bool AddEntityOperator::postRecursion(const 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 = !_foundNew; // As we unwind, if we're in either of these two paths, we mark our element // as dirty. if ((_foundNew && element->getAACube().contains(_newEntityBox))) { element->markWithChangedTime(); } return keepSearching; // if we haven't yet found it, keep looking }
void OctreeElement::deleteChildAtIndex(int childIndex) { OctreeElementPointer childAt = getChildAtIndex(childIndex); if (childAt) { childAt.reset(); setChildAtIndex(childIndex, NULL); _isDirty = true; markWithChangedTime(); // after deleting the child, check to see if we're a leaf if (isLeaf()) { _voxelNodeLeafCount++; } } }
bool OctreeRenderer::renderOperation(OctreeElementPointer element, void* extraData) { RenderArgs* args = static_cast<RenderArgs*>(extraData); if (element->isInView(*args->_viewFrustum)) { if (element->hasContent()) { if (element->calculateShouldRender(args->_viewFrustum, args->_sizeScale, args->_boundaryLevelAdjust)) { args->_renderer->renderElement(element, args); } else { return false; // if we shouldn't render, then we also should stop recursing. } } return true; // continue recursing } // if not in view stop recursing return false; }
bool UpdateEntityOperator::subTreeContainsNewEntity(OctreeElementPointer element) { bool elementContainsNewBox = element->getAACube().contains(_newEntityBox); if (_wantDebug) { bool elementContainsNewCube = element->getAACube().contains(_newEntityCube); qCDebug(entities) << "UpdateEntityOperator::subTreeContainsNewEntity()...."; qCDebug(entities) << " element->getAACube()=" << element->getAACube(); qCDebug(entities) << " _newEntityCube=" << _newEntityCube; qCDebug(entities) << " _newEntityBox=" << _newEntityBox; qCDebug(entities) << " elementContainsNewCube=" << elementContainsNewCube; qCDebug(entities) << " elementContainsNewBox=" << elementContainsNewBox; } return elementContainsNewBox; }
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 }
void OctreeElement::deleteAllChildren() { // first delete all the OctreeElement objects... for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { OctreeElementPointer childAt = getChildAtIndex(i); if (childAt) { childAt.reset(); } } if (_childrenExternal) { // if the children_t union represents _children.external we need to delete it here for (int i = 0; i < NUMBER_OF_CHILDREN; i ++) { _externalChildren[i].reset(); } } }
// does this entity tree element contain the old entity bool UpdateEntityOperator::subTreeContainsOldEntity(OctreeElementPointer element) { // We've found cases where the old entity might be placed in an element that is not actually the best fit // so when we're searching the tree for the old element, we use the known cube for the known containing element bool elementContainsOldBox = element->getAACube().contains(_containingElementCube); if (_wantDebug) { bool elementContainsOldCube = element->getAACube().contains(_oldEntityCube); qCDebug(entities) << "UpdateEntityOperator::subTreeContainsOldEntity()...."; qCDebug(entities) << " element->getAACube()=" << element->getAACube(); qCDebug(entities) << " _oldEntityCube=" << _oldEntityCube; qCDebug(entities) << " _oldEntityBox=" << _oldEntityBox; qCDebug(entities) << " elementContainsOldCube=" << elementContainsOldCube; qCDebug(entities) << " elementContainsOldBox=" << elementContainsOldBox; } return elementContainsOldBox; }
void OctreeSceneStats::didntFit(const OctreeElementPointer element) { _didntFit++; if (element->isLeaf()) { _leavesDidntFit++; } else { _internalDidntFit++; } }
void OctreeSceneStats::colorSent(const OctreeElementPointer element) { _colorSent++; if (element->isLeaf()) { _leavesColorSent++; } else { _internalColorSent++; } }
void OctreeSceneStats::skippedOccluded(const OctreeElementPointer element) { _skippedOccluded++; if (element->isLeaf()) { _leavesSkippedOccluded++; } else { _internalSkippedOccluded++; } }
void OctreeSceneStats::traversed(const OctreeElementPointer element) { _traversed++; if (element->isLeaf()) { _leaves++; } else { _internal++; } }
OctreeElementPointer AddEntityOperator::possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) { // If we're getting called, it's because there was no child element at this index while recursing. // We only care if this happens while still searching for the new entity location. // Check to see if if (!_foundNew) { float childElementScale = element->getAACube().getScale() / 2.0f; // all of our children will be half our scale // if the scale of our desired cube is smaller than our children, then consider making a child if (_newEntityBox.getLargestDimension() <= childElementScale) { int indexOfChildContainingNewEntity = element->getMyChildContaining(_newEntityBox); if (childIndex == indexOfChildContainingNewEntity) { return element->addChildAtIndex(childIndex); } } } return NULL; }
OctreeElementPointer UpdateEntityOperator::possiblyCreateChildAt(OctreeElementPointer element, int childIndex) { // If we're getting called, it's because there was no child element at this index while recursing. // We only care if this happens while still searching for the new entity location. // Check to see if if (!_foundNew) { float childElementScale = element->getScale() / 2.0f; // all of our children will be half our scale // Note: because the entity's bounds might have been clamped to the domain. We want to check if the // bounds of the clamped box would fit in our child elements. It may be the case that the actual // bounds of the element would hang outside of the child elements cells. bool entityWouldFitInChild = _newEntityBox.getLargestDimension() <= childElementScale; // if the scale of our desired cube is smaller than our children, then consider making a child if (entityWouldFitInChild) { int indexOfChildContainingNewEntity = element->getMyChildContaining(_newEntityBox); if (childIndex == indexOfChildContainingNewEntity) { return element->addChildAtIndex(childIndex);; } } } return NULL; }
// handles staging or deletion of all deep children bool OctreeElement::safeDeepDeleteChildAtIndex(int childIndex, int recursionCount) { bool deleteApproved = false; if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex( "OctreeElement::safeDeepDeleteChildAtIndex\\(\\) reached DANGEROUSLY_DEEP_RECURSION, bailing!"); qCDebug(octree) << "OctreeElement::safeDeepDeleteChildAtIndex() reached DANGEROUSLY_DEEP_RECURSION, bailing!"; return deleteApproved; } OctreeElementPointer childToDelete = getChildAtIndex(childIndex); if (childToDelete) { if (childToDelete->deleteApproved()) { // If the child is not a leaf, then call ourselves recursively on all the children if (!childToDelete->isLeaf()) { // delete all it's children for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { if (childToDelete->getChildAtIndex(i)) { deleteApproved = childToDelete->safeDeepDeleteChildAtIndex(i,recursionCount+1); if (!deleteApproved) { break; // no point in continuing... } } } } else { deleteApproved = true; // because we got here after checking that delete was approved } if (deleteApproved) { deleteChildAtIndex(childIndex); _isDirty = true; markWithChangedTime(); } } } return deleteApproved; }
// does this entity tree element contain the old entity bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(const OctreeElementPointer& element) { bool containsEntity = false; // If we don't have an old entity, then we don't contain the entity, otherwise // check the bounds if (_entitiesToDelete.size() > 0) { const AACube& elementCube = element->getAACube(); foreach(const EntityToDeleteDetails& details, _entitiesToDelete) { if (elementCube.contains(details.cube)) { containsEntity = true; break; // if it contains at least one, we're good to go } } } return containsEntity; }
bool DirtyOctreeElementOperator::postRecursion(const OctreeElementPointer& element) { element->markWithChangedTime(); return true; }
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 }
bool DirtyOctreeElementOperator::preRecursion(const OctreeElementPointer& element) { if (element == _element) { return false; } return element->getAACube().contains(_point); }
void OctreeElementBag::insert(OctreeElementPointer element) { _bagElements[element.get()] = element; }