From f415f4081a6ab595f58b2670daa2726b9015a4a4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 15 Jul 2013 01:32:14 -0700 Subject: [PATCH] Fixed a bug in LOD boundaries - changed the way we calculate whether or not to render a parent "average" voxel so that if any of it's most distant child would not be visible, then it's used instead of it's children - added precalculated value for topFarLeft corner of AABox (optimization) - changed VoxelSystem::newTreeToArrays() and VoxelTree::encodeTreeBitstreamRecursion() to use the same help function for determining this LOD boundary behavior - deleted old dead code in voxel-server and VoxelTree for picking which node to start sending with, since it wasn't being used - added VoxelNode::furthestDistanceToCamera() which tells you not the distance to the center of the voxel, but the distance from the camera to the furthest corner relative to the camera. - added ViewFrustum::getFurthestPointFromCamera() which given an axis-aligned box will tell you which vertex of the box is furthest from the camera --- interface/src/Audio.cpp | 2 +- interface/src/VoxelSystem.cpp | 9 +-- libraries/voxels/src/AABox.cpp | 2 + libraries/voxels/src/AABox.h | 19 +++-- libraries/voxels/src/ViewFrustum.cpp | 43 ++++++++++- libraries/voxels/src/ViewFrustum.h | 1 + libraries/voxels/src/VoxelNode.cpp | 33 ++++++++ libraries/voxels/src/VoxelNode.h | 3 + libraries/voxels/src/VoxelTree.cpp | 108 +-------------------------- libraries/voxels/src/VoxelTree.h | 7 -- voxel-server/src/main.cpp | 40 +--------- 11 files changed, 98 insertions(+), 169 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 75c1198d58..73f971b7da 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -318,8 +318,8 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) : _lastAcceleration(0), _totalPacketsReceived(0), _firstPacketReceivedTime(), - _echoSamplesLeft(NULL), _packetsReceivedThisPlayback(0), + _echoSamplesLeft(NULL), _isSendingEchoPing(false), _pingAnalysisPending(false), _pingFramesToRecord(0), diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index ca7220afa8..2801d16748 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -319,14 +319,7 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) { int voxelsUpdated = 0; bool shouldRender = false; // assume we don't need to render it // if it's colored, we might need to render it! - if (node->isColored()) { - float distanceToNode = node->distanceToCamera(*Application::getInstance()->getViewFrustum()); - float boundary = boundaryDistanceForRenderLevel(node->getLevel()); - float childBoundary = boundaryDistanceForRenderLevel(node->getLevel() + 1); - bool inBoundary = (distanceToNode <= boundary); - bool inChildBoundary = (distanceToNode <= childBoundary); - shouldRender = (node->isLeaf() && inChildBoundary) || (inBoundary && !inChildBoundary); - } + shouldRender = node->calculateShouldRender(Application::getInstance()->getViewFrustum()); node->setShouldRender(shouldRender && !node->isStagedForDeletion()); // let children figure out their renderness if (!node->isLeaf()) { diff --git a/libraries/voxels/src/AABox.cpp b/libraries/voxels/src/AABox.cpp index 36e65561c3..dc9deb21db 100644 --- a/libraries/voxels/src/AABox.cpp +++ b/libraries/voxels/src/AABox.cpp @@ -18,6 +18,7 @@ void AABox::scale(float scale) { _corner = _corner * scale; _size = _size * scale; _center = _center * scale; + _topFarLeft = _topFarLeft * scale; } @@ -60,6 +61,7 @@ void AABox::setBox(const glm::vec3& corner, const glm::vec3& size) { _corner.z -= _size.z; } _center = _corner + (_size * 0.5f); + _topFarLeft = _corner + _size; } glm::vec3 AABox::getVertexP(const glm::vec3& normal) const { diff --git a/libraries/voxels/src/AABox.h b/libraries/voxels/src/AABox.h index 735799cd05..74d54ba777 100644 --- a/libraries/voxels/src/AABox.h +++ b/libraries/voxels/src/AABox.h @@ -41,10 +41,13 @@ class AABox public: - AABox(const glm::vec3& corner, float size) : _corner(corner), _size(size, size, size) { }; - AABox(const glm::vec3& corner, float x, float y, float z) : _corner(corner), _size(x, y, z) { }; - AABox(const glm::vec3& corner, const glm::vec3& size) : _corner(corner), _size(size) { }; - AABox() : _corner(0,0,0), _size(0,0,0) { } + AABox(const glm::vec3& corner, float size) : + _corner(corner), _size(size, size, size) { _topFarLeft = _corner + _size; }; + AABox(const glm::vec3& corner, float x, float y, float z) : + _corner(corner), _size(x, y, z) { _topFarLeft = _corner + _size; }; + AABox(const glm::vec3& corner, const glm::vec3& size) : + _corner(corner), _size(size) { _topFarLeft = _corner + _size; }; + AABox() : _corner(0,0,0), _size(0,0,0), _topFarLeft(0,0,0) { } ~AABox() { } void setBox(const glm::vec3& corner, float x, float y, float z) { setBox(corner,glm::vec3(x,y,z)); }; @@ -56,9 +59,10 @@ public: void scale(float scale); - const glm::vec3& getCorner() const { return _corner; }; - const glm::vec3& getSize() const { return _size; }; - const glm::vec3& getCenter() const { return _center; }; + const glm::vec3& getCorner() const { return _corner; }; + const glm::vec3& getSize() const { return _size; }; + const glm::vec3& getCenter() const { return _center; }; + const glm::vec3& getTopFarLeft() const { return _topFarLeft; }; glm::vec3 getVertex(BoxVertex vertex) const; @@ -81,6 +85,7 @@ private: glm::vec3 _corner; glm::vec3 _center; glm::vec3 _size; + glm::vec3 _topFarLeft; }; diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index 166a2f2126..415a064e5d 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -530,8 +530,10 @@ const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_PROJECTED_POLYGON_VERT }; VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { - glm::vec3 bottomNearRight = box.getCorner(); - glm::vec3 topFarLeft = box.getCorner() + box.getSize(); + const glm::vec3& bottomNearRight = box.getCorner(); + const glm::vec3& topFarLeft = box.getTopFarLeft(); + + int lookUp = ((_position.x < bottomNearRight.x) ) // 1 = right | compute 6-bit + ((_position.x > topFarLeft.x ) << 1) // 2 = left | code to + ((_position.y < bottomNearRight.y) << 2) // 4 = bottom | classify camera @@ -593,3 +595,40 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { projectedPolygon.setProjectionType(lookUp); // remember the projection type return projectedPolygon; } + + +// Similar strategy to getProjectedPolygon() we use the knowledge of camera position relative to the +// axis-aligned voxels to determine which of the voxels vertices must be the furthest. No need for +// squares and square-roots. Just compares. +glm::vec3 ViewFrustum::getFurthestPointFromCamera(const AABox& box) const { + const glm::vec3& center = box.getCenter(); + const glm::vec3& bottomNearRight = box.getCorner(); + const glm::vec3& topFarLeft = box.getTopFarLeft(); + + glm::vec3 furthestPoint; + if (_position.x < center.x) { + // we are to the right of the center, so the left edge is furthest + furthestPoint.x = topFarLeft.x; + } else { + // we are to the left of the center, so the right edge is furthest (at center ok too) + furthestPoint.x = bottomNearRight.x; + } + + if (_position.y < center.y) { + // we are below of the center, so the top edge is furthest + furthestPoint.y = topFarLeft.y; + } else { + // we are above the center, so the lower edge is furthest (at center ok too) + furthestPoint.y = bottomNearRight.y; + } + + if (_position.z < center.z) { + // we are to the near side of the center, so the far side edge is furthest + furthestPoint.z = topFarLeft.z; + } else { + // we are to the far side of the center, so the near side edge is furthest (at center ok too) + furthestPoint.z = bottomNearRight.z; + } + + return furthestPoint; +} diff --git a/libraries/voxels/src/ViewFrustum.h b/libraries/voxels/src/ViewFrustum.h index 7308d5bb64..e364816c59 100644 --- a/libraries/voxels/src/ViewFrustum.h +++ b/libraries/voxels/src/ViewFrustum.h @@ -90,6 +90,7 @@ public: glm::vec2 projectPoint(glm::vec3 point, bool& pointInView) const; VoxelProjectedPolygon getProjectedPolygon(const AABox& box) const; + glm::vec3 getFurthestPointFromCamera(const AABox& box) const; private: diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index a20715635f..6a702e75ba 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -335,6 +335,39 @@ ViewFrustum::location VoxelNode::inFrustum(const ViewFrustum& viewFrustum) const return viewFrustum.boxInFrustum(box); } +// There are two types of nodes for which we want to "render" +// 1) Leaves that are in the LOD +// 2) Non-leaves are more complicated though... usually you don't want to render them, but if their children +// wouldn't be rendered, then you do want to render them. But sometimes they have some children that ARE +// in the LOD, and others that are not. In this case we want to render the parent, and none of the children. +// +// Since, if we know the camera position and orientation, we can know which of the corners is the "furthest" +// corner. We can use we can use this corner as our "voxel position" to do our distance calculations off of. +// By doing this, we don't need to test each child voxel's position vs the LOD boundary +bool VoxelNode::calculateShouldRender(const ViewFrustum* viewFrustum, int boundaryLevelAdjust) const { + bool shouldRender = false; + if (isColored()) { + float furthestDistance = furthestDistanceToCamera(*viewFrustum); + float boundary = boundaryDistanceForRenderLevel(getLevel() + boundaryLevelAdjust); + float childBoundary = boundaryDistanceForRenderLevel(getLevel() + 1 + boundaryLevelAdjust); + bool inBoundary = (furthestDistance <= boundary); + bool inChildBoundary = (furthestDistance <= childBoundary); + shouldRender = (isLeaf() && inChildBoundary) || (inBoundary && !inChildBoundary); + } + return shouldRender; +} + +// Calculates the distance to the furthest point of the voxel to the camera +float VoxelNode::furthestDistanceToCamera(const ViewFrustum& viewFrustum) const { + AABox box = getAABox(); + box.scale(TREE_SCALE); + glm::vec3 furthestPoint = viewFrustum.getFurthestPointFromCamera(box); + glm::vec3 temp = viewFrustum.getPosition() - furthestPoint; + float distanceSquared = glm::dot(temp, temp); + float distanceToVoxelCenter = sqrtf(distanceSquared); + return distanceToVoxelCenter; +} + float VoxelNode::distanceToCamera(const ViewFrustum& viewFrustum) const { glm::vec3 center = _box.getCenter() * (float)TREE_SCALE; glm::vec3 temp = viewFrustum.getPosition() - center; diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index a92c562a7e..461a723a8a 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -70,6 +70,9 @@ public: bool isInView(const ViewFrustum& viewFrustum) const; ViewFrustum::location inFrustum(const ViewFrustum& viewFrustum) const; float distanceToCamera(const ViewFrustum& viewFrustum) const; + float furthestDistanceToCamera(const ViewFrustum& viewFrustum) const; + + bool calculateShouldRender(const ViewFrustum* viewFrustum, int boundaryLevelAdjust = 0) const; // points are assumed to be in Voxel Coordinates (not TREE_SCALE'd) float distanceSquareToPoint(const glm::vec3& point) const; // when you don't need the actual distance, use this. diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index d2ea383b8e..b84b374309 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -891,17 +891,6 @@ void VoxelTree::createSphere(float radius, float xc, float yc, float zc, float v } } -int VoxelTree::searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag, - bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) { - - // call the recursive version, this will add all found colored node roots to the bag - int currentSearchLevel = 0; - - int levelReached = searchForColoredNodesRecursion(maxSearchLevel, currentSearchLevel, rootNode, - viewFrustum, bag, deltaViewFrustum, lastViewFrustum); - return levelReached; -} - // combines the ray cast arguments into a single object class RayArgs { public: @@ -1014,88 +1003,6 @@ bool VoxelTree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& return args.found; } -int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel, - VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag, - bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) { - - // Keep track of how deep we've searched. - currentSearchLevel++; - - // If we've passed our max Search Level, then stop searching. return last level searched - if (currentSearchLevel > maxSearchLevel) { - return currentSearchLevel-1; - } - - // If we're at a node that is out of view, then we can return, because no nodes below us will be in view! - if (!node->isInView(viewFrustum)) { - return currentSearchLevel; - } - - // Ok, this is a little tricky, each child may have been deeper than the others, so we need to track - // how deep each child went. And we actually return the maximum of each child. We use these variables below - // when we recurse the children. - int thisLevel = currentSearchLevel; - int maxChildLevel = thisLevel; - - VoxelNode* inViewChildren[NUMBER_OF_CHILDREN]; - float distancesToChildren[NUMBER_OF_CHILDREN]; - int positionOfChildren[NUMBER_OF_CHILDREN]; - int inViewCount = 0; - int inViewNotLeafCount = 0; - int inViewWithColorCount = 0; - - // for each child node, check to see if they exist, are colored, and in view, and if so - // add them to our distance ordered array of children - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - VoxelNode* childNode = node->getChildAtIndex(i); - bool childIsColored = (childNode && childNode->isColored()); - bool childIsInView = (childNode && childNode->isInView(viewFrustum)); - bool childIsLeaf = (childNode && childNode->isLeaf()); - - if (childIsInView) { - - // track children in view as existing and not a leaf - if (!childIsLeaf) { - inViewNotLeafCount++; - } - - // track children with actual color - if (childIsColored) { - inViewWithColorCount++; - } - - float distance = childNode->distanceToCamera(viewFrustum); - - if (distance < boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1)) { - inViewCount = insertIntoSortedArrays((void*)childNode, distance, i, - (void**)&inViewChildren, (float*)&distancesToChildren, - (int*)&positionOfChildren, inViewCount, NUMBER_OF_CHILDREN); - } - } - } - - // If we have children with color, then we do want to add this node (and it's descendants) to the bag to be written - // we don't need to dig deeper. - // - // XXXBHG - this might be a good time to look at colors and add them to a dictionary? But we're not planning - // on scanning the whole tree, so we won't actually see all the colors, so maybe no point in that. - if (inViewWithColorCount) { - bag.insert(node); - } else { - // at this point, we need to iterate the children who are in view, even if not colored - // and we need to determine if there's a deeper tree below them that we care about. We will iterate - // these based on which tree is closer. - for (int i = 0; i < inViewCount; i++) { - VoxelNode* childNode = inViewChildren[i]; - thisLevel = currentSearchLevel; // reset this, since the children will munge it up - int childLevelReached = searchForColoredNodesRecursion(maxSearchLevel, thisLevel, childNode, viewFrustum, bag, - deltaViewFrustum, lastViewFrustum); - maxChildLevel = std::max(maxChildLevel, childLevelReached); - } - } - return maxChildLevel; -} - int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag, EncodeBitstreamParams& params) const { @@ -1344,19 +1251,10 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp } // wants occlusion culling & isLeaf() - // There are two types of nodes for which we want to send colors: - // 1) Leaves - obviously - // 2) Non-leaves who's children would be visible but are beyond our LOD. - bool isLeafOrLOD = childNode->isLeaf(); - if (params.viewFrustum && childNode->isColored() && !childNode->isLeaf()) { - int childLevel = childNode->getLevel(); - float childBoundary = boundaryDistanceForRenderLevel(childLevel + params.boundaryLevelAdjust); - float grandChildBoundary = boundaryDistanceForRenderLevel(childLevel + 1 + params.boundaryLevelAdjust); - isLeafOrLOD = ((distance <= childBoundary) && !(distance <= grandChildBoundary)); - } + bool shouldRender = childNode->calculateShouldRender(params.viewFrustum, params.boundaryLevelAdjust); // track children with actual color, only if the child wasn't previously in view! - if (childNode && isLeafOrLOD && childNode->isColored() && !childIsOccluded) { + if (shouldRender && !childIsOccluded) { bool childWasInView = false; if (childNode && params.deltaViewFrustum && params.lastViewFrustum) { @@ -1368,7 +1266,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp } else { childWasInView = location == ViewFrustum::INSIDE; } - } + } // If our child wasn't in view (or we're ignoring wasInView) then we add it to our sending items if (!childWasInView) { diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 61f2e13056..9c8e6e7d16 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -123,9 +123,6 @@ public: int encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag, EncodeBitstreamParams& params) const; - int searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag, - bool deltaViewFrustum = false, const ViewFrustum* lastViewFrustum = NULL); - bool isDirty() const { return _isDirty; }; void clearDirtyBit() { _isDirty = false; }; void setDirtyBit() { _isDirty = true; }; @@ -171,10 +168,6 @@ private: int encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag, EncodeBitstreamParams& params, int& currentEncodeLevel) const; - int searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel, - VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag, - bool deltaViewFrustum, const ViewFrustum* lastViewFrustum); - static bool countVoxelsOperation(VoxelNode* node, void* extraData); VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const; diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index cf59f226db..254b1d2d96 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -60,7 +60,6 @@ bool wantLocalDomain = false; bool wantColorRandomizer = false; bool debugVoxelSending = false; bool shouldShowAnimationDebug = false; -bool wantSearchForColoredNodes = false; EnvironmentData environmentData[3]; @@ -121,8 +120,6 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, pthread_mutex_lock(&::treeLock); - int maxLevelReached = 0; - uint64_t start = usecTimestampNow(); int truePacketsSent = 0; int trueBytesSent = 0; @@ -200,38 +197,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, nodeData->map.erase(); } - // For now, we're going to disable the "search for colored nodes" because that strategy doesn't work when we support - // deletion of nodes. Instead if we just start at the root we get the correct behavior we want. We are keeping this - // code for now because we want to be able to go back to it and find a solution to support both. The search method - // helps improve overall bitrate performance. - if (::wantSearchForColoredNodes) { - // If the bag was empty, then send everything in view, not just the delta - maxLevelReached = serverTree.searchForColoredNodes(INT_MAX, serverTree.rootNode, nodeData->getCurrentViewFrustum(), - nodeData->nodeBag, wantDelta, lastViewFrustum); - - // if nothing was found in view, send the root node. - if (nodeData->nodeBag.isEmpty()){ - nodeData->nodeBag.insert(serverTree.rootNode); - } - nodeData->setViewSent(false); - } else { - nodeData->nodeBag.insert(serverTree.rootNode); - } - } - uint64_t end = usecTimestampNow(); - int elapsedmsec = (end - start)/1000; - if (elapsedmsec > 100) { - if (elapsedmsec > 1000) { - int elapsedsec = (end - start)/1000000; - printf("WARNING! searchForColoredNodes() took %d seconds to identify %d nodes at level %d\n", - elapsedsec, nodeData->nodeBag.count(), maxLevelReached); - } else { - printf("WARNING! searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d\n", - elapsedmsec, nodeData->nodeBag.count(), maxLevelReached); - } - } else if (::debugVoxelSending) { - printf("searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d\n", - elapsedmsec, nodeData->nodeBag.count(), maxLevelReached); + nodeData->nodeBag.insert(serverTree.rootNode); } // If we have something in our nodeBag, then turn them into packets and send them out... @@ -438,10 +404,6 @@ int main(int argc, const char * argv[]) { ::wantColorRandomizer = cmdOptionExists(argc, argv, WANT_COLOR_RANDOMIZER); printf("wantColorRandomizer=%s\n", debug::valueOf(::wantColorRandomizer)); - const char* WANT_SEARCH_FOR_NODES = "--wantSearchForColoredNodes"; - ::wantSearchForColoredNodes = cmdOptionExists(argc, argv, WANT_SEARCH_FOR_NODES); - printf("wantSearchForColoredNodes=%s\n", debug::valueOf(::wantSearchForColoredNodes)); - // By default we will voxel persist, if you want to disable this, then pass in this parameter const char* NO_VOXEL_PERSIST = "--NoVoxelPersist"; if (cmdOptionExists(argc, argv, NO_VOXEL_PERSIST)) {