diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index c166c57f3a..b7afd67947 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -1700,7 +1700,7 @@ bool VoxelSystem::isViewChanging() { bool result = false; // assume the best // If our viewFrustum has changed since our _lastKnownViewFrustum - if (!_lastKnownViewFrustum.matches(_viewFrustum)) { + if (!_lastKnownViewFrustum.isVerySimilar(_viewFrustum)) { result = true; _lastKnownViewFrustum = *_viewFrustum; // save last known } @@ -1716,7 +1716,7 @@ bool VoxelSystem::hasViewChanged() { } // If our viewFrustum has changed since our _lastKnownViewFrustum - if (!_lastStableViewFrustum.matches(_viewFrustum)) { + if (!_lastStableViewFrustum.isVerySimilar(_viewFrustum)) { result = true; _lastStableViewFrustum = *_viewFrustum; // save last stable } @@ -1867,7 +1867,7 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) { } } - if (!forceFullFrustum && _culledOnce && args.lastViewFrustum.matches(args.thisViewFrustum)) { + if (!forceFullFrustum && _culledOnce && args.lastViewFrustum.isVerySimilar(args.thisViewFrustum)) { //printf("view frustum hasn't changed BAIL!!!\n"); return; } diff --git a/libraries/voxel-server-library/src/VoxelNodeData.cpp b/libraries/voxel-server-library/src/VoxelNodeData.cpp index 1995dcc867..29efa5d870 100644 --- a/libraries/voxel-server-library/src/VoxelNodeData.cpp +++ b/libraries/voxel-server-library/src/VoxelNodeData.cpp @@ -84,7 +84,7 @@ bool VoxelNodeData::updateCurrentViewFrustum() { newestViewFrustum.setEyeOffsetPosition(getCameraEyeOffsetPosition()); // if there has been a change, then recalculate - if (!newestViewFrustum.matches(_currentViewFrustum)) { + if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) { _currentViewFrustum = newestViewFrustum; _currentViewFrustum.calculate(); currentViewFrustumChanged = true; @@ -109,7 +109,7 @@ void VoxelNodeData::setViewSent(bool viewSent) { void VoxelNodeData::updateLastKnownViewFrustum() { - bool frustumChanges = !_lastKnownViewFrustum.matches(_currentViewFrustum); + bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum); if (frustumChanges) { // save our currentViewFrustum into our lastKnownViewFrustum diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index 05380c9e4a..7c01a1ad03 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -10,6 +10,8 @@ #include +#include +#include #include #include @@ -296,17 +298,17 @@ ViewFrustum::location ViewFrustum::boxInFrustum(const AABox& box) const { return regularResult; } -bool testMatches(glm::quat lhs, glm::quat rhs) { - return (fabs(lhs.x - rhs.x) <= EPSILON && fabs(lhs.y - rhs.y) <= EPSILON && fabs(lhs.z - rhs.z) <= EPSILON - && fabs(lhs.w - rhs.w) <= EPSILON); +bool testMatches(glm::quat lhs, glm::quat rhs, float epsilon = EPSILON) { + return (fabs(lhs.x - rhs.x) <= epsilon && fabs(lhs.y - rhs.y) <= epsilon && fabs(lhs.z - rhs.z) <= epsilon + && fabs(lhs.w - rhs.w) <= epsilon); } -bool testMatches(glm::vec3 lhs, glm::vec3 rhs) { - return (fabs(lhs.x - rhs.x) <= EPSILON && fabs(lhs.y - rhs.y) <= EPSILON && fabs(lhs.z - rhs.z) <= EPSILON); +bool testMatches(glm::vec3 lhs, glm::vec3 rhs, float epsilon = EPSILON) { + return (fabs(lhs.x - rhs.x) <= epsilon && fabs(lhs.y - rhs.y) <= epsilon && fabs(lhs.z - rhs.z) <= epsilon); } -bool testMatches(float lhs, float rhs) { - return (fabs(lhs - rhs) <= EPSILON); +bool testMatches(float lhs, float rhs, float epsilon = EPSILON) { + return (fabs(lhs - rhs) <= epsilon); } bool ViewFrustum::matches(const ViewFrustum& compareTo, bool debug) const { @@ -369,6 +371,93 @@ bool ViewFrustum::matches(const ViewFrustum& compareTo, bool debug) const { return result; } +bool isNaN(float f) { + return f != f; +} + +bool ViewFrustum::isVerySimilar(const ViewFrustum& compareTo, bool debug) const { + + // Compute distance between the two positions + const float POSITION_SIMILAR_ENOUGH = 5.0f; // 5 meters + float positionDistance = glm::distance(_position, compareTo._position); + + const float EYEOFFSET_POSITION_SIMILAR_ENOUGH = 0.15f; // 0.15 meters + float eyeOffsetpositionDistance = glm::distance(_eyeOffsetPosition, compareTo._eyeOffsetPosition); + + // Compute the angular distance between the two orientations + const float ORIENTATION_SIMILAR_ENOUGH = 10.0f; // 10 degrees in any direction + glm::quat dQOrientation = _orientation * glm::inverse(compareTo._orientation); + float angleOrientation = compareTo._orientation == _orientation ? 0.0f : glm::angle(dQOrientation); + if (isNaN(angleOrientation)) { + angleOrientation = 0.0f; + } + + glm::quat dQEyeOffsetOrientation = _eyeOffsetOrientation * glm::inverse(compareTo._eyeOffsetOrientation); + float angleEyeOffsetOrientation = compareTo._eyeOffsetOrientation == _eyeOffsetOrientation + ? 0.0f : glm::angle(dQEyeOffsetOrientation); + if (isNaN(angleEyeOffsetOrientation)) { + angleOrientation = 0.0f; + } + + bool result = + testMatches(0, positionDistance, POSITION_SIMILAR_ENOUGH) && + testMatches(0, angleOrientation, ORIENTATION_SIMILAR_ENOUGH) && + testMatches(compareTo._fieldOfView, _fieldOfView) && + testMatches(compareTo._aspectRatio, _aspectRatio) && + testMatches(compareTo._nearClip, _nearClip) && + testMatches(compareTo._farClip, _farClip) && + testMatches(compareTo._focalLength, _focalLength) && + testMatches(0, eyeOffsetpositionDistance, EYEOFFSET_POSITION_SIMILAR_ENOUGH) && + testMatches(0, angleEyeOffsetOrientation, ORIENTATION_SIMILAR_ENOUGH); + + + if (!result && debug) { + qDebug("ViewFrustum::isVerySimilar()... result=%s\n", debug::valueOf(result)); + qDebug("%s -- compareTo._position=%f,%f,%f _position=%f,%f,%f\n", + (testMatches(compareTo._position,_position, POSITION_SIMILAR_ENOUGH) ? "IS SIMILAR ENOUGH " : "IS NOT SIMILAR ENOUGH"), + compareTo._position.x, compareTo._position.y, compareTo._position.z, + _position.x, _position.y, _position.z ); + + qDebug("%s -- positionDistance=%f\n", + (testMatches(0,positionDistance, POSITION_SIMILAR_ENOUGH) ? "IS SIMILAR ENOUGH " : "IS NOT SIMILAR ENOUGH"), + positionDistance); + + qDebug("%s -- angleOrientation=%f\n", + (testMatches(0, angleOrientation, ORIENTATION_SIMILAR_ENOUGH) ? "IS SIMILAR ENOUGH " : "IS NOT SIMILAR ENOUGH"), + angleOrientation); + + qDebug("%s -- compareTo._fieldOfView=%f _fieldOfView=%f\n", + (testMatches(compareTo._fieldOfView, _fieldOfView) ? "MATCHES " : "NO MATCH"), + compareTo._fieldOfView, _fieldOfView); + qDebug("%s -- compareTo._aspectRatio=%f _aspectRatio=%f\n", + (testMatches(compareTo._aspectRatio, _aspectRatio) ? "MATCHES " : "NO MATCH"), + compareTo._aspectRatio, _aspectRatio); + qDebug("%s -- compareTo._nearClip=%f _nearClip=%f\n", + (testMatches(compareTo._nearClip, _nearClip) ? "MATCHES " : "NO MATCH"), + compareTo._nearClip, _nearClip); + qDebug("%s -- compareTo._farClip=%f _farClip=%f\n", + (testMatches(compareTo._farClip, _farClip) ? "MATCHES " : "NO MATCH"), + compareTo._farClip, _farClip); + qDebug("%s -- compareTo._focalLength=%f _focalLength=%f\n", + (testMatches(compareTo._focalLength, _focalLength) ? "MATCHES " : "NO MATCH"), + compareTo._focalLength, _focalLength); + + qDebug("%s -- compareTo._eyeOffsetPosition=%f,%f,%f _eyeOffsetPosition=%f,%f,%f\n", + (testMatches(compareTo._eyeOffsetPosition, _eyeOffsetPosition, POSITION_SIMILAR_ENOUGH) ? "IS SIMILAR ENOUGH " : "IS NOT SIMILAR ENOUGH"), + compareTo._eyeOffsetPosition.x, compareTo._eyeOffsetPosition.y, compareTo._eyeOffsetPosition.z, + _eyeOffsetPosition.x, _eyeOffsetPosition.y, _eyeOffsetPosition.z); + + qDebug("%s -- eyeOffsetpositionDistance=%f\n", + (testMatches(0,eyeOffsetpositionDistance, EYEOFFSET_POSITION_SIMILAR_ENOUGH) ? "IS SIMILAR ENOUGH " : "IS NOT SIMILAR ENOUGH"), + eyeOffsetpositionDistance); + + qDebug("%s -- angleEyeOffsetOrientation=%f\n", + (testMatches(0, angleEyeOffsetOrientation, ORIENTATION_SIMILAR_ENOUGH) ? "IS SIMILAR ENOUGH " : "IS NOT SIMILAR ENOUGH"), + angleEyeOffsetOrientation); + } + return result; +} + void ViewFrustum::computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const { origin = _nearTopLeft + x*(_nearTopRight - _nearTopLeft) + y*(_nearBottomLeft - _nearTopLeft); direction = glm::normalize(origin - (_position + _orientation * _eyeOffsetPosition)); diff --git a/libraries/voxels/src/ViewFrustum.h b/libraries/voxels/src/ViewFrustum.h index 9a3c69ca7c..c5f2be159d 100644 --- a/libraries/voxels/src/ViewFrustum.h +++ b/libraries/voxels/src/ViewFrustum.h @@ -85,6 +85,9 @@ public: bool matches(const ViewFrustum& compareTo, bool debug = false) const; bool matches(const ViewFrustum* compareTo, bool debug = false) const { return matches(*compareTo, debug); } + bool isVerySimilar(const ViewFrustum& compareTo, bool debug = false) const; + bool isVerySimilar(const ViewFrustum* compareTo, bool debug = false) const { return isVerySimilar(*compareTo, debug); } + void computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const; void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& near, float& far, diff --git a/libraries/voxels/src/VoxelConstants.h b/libraries/voxels/src/VoxelConstants.h index 64a3f3cc44..9d992f8b55 100644 --- a/libraries/voxels/src/VoxelConstants.h +++ b/libraries/voxels/src/VoxelConstants.h @@ -54,4 +54,9 @@ const uint64_t CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS = 1000 * 5; // 1 packe const float VIEW_FRUSTUM_FOV_OVERSEND = 60.0f; +// These are guards to prevent our voxel tree recursive routines from spinning out of control +const int UNREASONABLY_DEEP_RECURSION = 20; // use this for something that you want to be shallow, but not spin out +const int DANGEROUSLY_DEEP_RECURSION = 200; // use this for something that needs to go deeper + + #endif \ No newline at end of file diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index b4fbce630f..40cdbe3a3b 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -1021,14 +1021,18 @@ VoxelNode* VoxelNode::addChildAtIndex(int childIndex) { } // handles staging or deletion of all deep children -void VoxelNode::safeDeepDeleteChildAtIndex(int childIndex) { +void VoxelNode::safeDeepDeleteChildAtIndex(int childIndex, int recursionCount) { + if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { + qDebug() << "VoxelNode::safeDeepDeleteChildAtIndex() reached DANGEROUSLY_DEEP_RECURSION, bailing!\n"; + return; + } VoxelNode* childToDelete = getChildAtIndex(childIndex); if (childToDelete) { // 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++) { - childToDelete->safeDeepDeleteChildAtIndex(i); + childToDelete->safeDeepDeleteChildAtIndex(i,recursionCount+1); } } deleteChildAtIndex(childIndex); diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index f52198efaf..f76be6227f 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -48,7 +48,7 @@ public: void deleteChildAtIndex(int childIndex); VoxelNode* removeChildAtIndex(int childIndex); VoxelNode* addChildAtIndex(int childIndex); - void safeDeepDeleteChildAtIndex(int childIndex); // handles deletion of all descendents + void safeDeepDeleteChildAtIndex(int childIndex, int recursionCount = 0); // handles deletion of all descendents void setColorFromAverageOfChildren(); void setRandomColor(int minimumBrightness); diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 6c1fe42b76..4a76c74596 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -78,13 +78,18 @@ void VoxelTree::recurseTreeWithOperation(RecurseVoxelTreeOperation operation, vo } // Recurses voxel node with an operation function -void VoxelTree::recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData) { +void VoxelTree::recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData, + int recursionCount) { + if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { + qDebug() << "VoxelTree::recurseNodeWithOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!\n"; + return; + } if (operation(node, extraData)) { for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { VoxelNode* child = node->getChildAtIndex(i); if (child) { - recurseNodeWithOperation(child, operation, extraData); + recurseNodeWithOperation(child, operation, extraData, recursionCount+1); } } } @@ -100,7 +105,13 @@ void VoxelTree::recurseTreeWithOperationDistanceSorted(RecurseVoxelTreeOperation // Recurses voxel node with an operation function void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation, - const glm::vec3& point, void* extraData) { + const glm::vec3& point, void* extraData, int recursionCount) { + + if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { + qDebug() << "VoxelTree::recurseNodeWithOperationDistanceSorted() reached DANGEROUSLY_DEEP_RECURSION, bailing!\n"; + return; + } + if (operation(node, extraData)) { // determine the distance sorted order of our children VoxelNode* sortedChildren[NUMBER_OF_CHILDREN]; @@ -641,7 +652,6 @@ void VoxelTree::reaverageVoxelColors(VoxelNode* startNode) { } else { recursionCount++; } - const int UNREASONABLY_DEEP_RECURSION = 20; if (recursionCount > UNREASONABLY_DEEP_RECURSION) { qDebug("VoxelTree::reaverageVoxelColors()... bailing out of UNREASONABLY_DEEP_RECURSION\n"); recursionCount--; diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index a38c751133..465c1a54aa 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -190,10 +190,11 @@ public: bool getShouldReaverage() const { return _shouldReaverage; } - void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData); + void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, + void* extraData, int recursionCount = 0); void recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation, - const glm::vec3& point, void* extraData); + const glm::vec3& point, void* extraData, int recursionCount = 0); void nudgeSubTree(VoxelNode* nodeToNudge, const glm::vec3& nudgeAmount, VoxelEditPacketSender& voxelEditSender);