From 758fadd6a95a21ccf2f5c4f667bcc77fb51c89a4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 19 Jun 2013 23:34:45 -0700 Subject: [PATCH 01/55] optimized CoverageRegion to track _currentCoveredBounds and not test polygons in the tested ocludee is not in those covered bounds --- libraries/voxels/src/CoverageMap.cpp | 69 ++++++++++++------- libraries/voxels/src/CoverageMap.h | 4 ++ .../voxels/src/VoxelProjectedPolygon.cpp | 36 +++++++++- libraries/voxels/src/VoxelProjectedPolygon.h | 7 +- 4 files changed, 89 insertions(+), 27 deletions(-) diff --git a/libraries/voxels/src/CoverageMap.cpp b/libraries/voxels/src/CoverageMap.cpp index eae1262a59..0f8df158b3 100644 --- a/libraries/voxels/src/CoverageMap.cpp +++ b/libraries/voxels/src/CoverageMap.cpp @@ -13,6 +13,7 @@ int CoverageMap::_mapCount = 0; int CoverageMap::_checkMapRootCalls = 0; +int CoverageMap::_notAllInView = 0; bool CoverageMap::wantDebugging = false; const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-2.f,-2.f), glm::vec2(4.f,4.f)); @@ -84,16 +85,22 @@ void CoverageMap::erase() { printLog("MINIMUM_POLYGON_AREA_TO_STORE=%f\n",MINIMUM_POLYGON_AREA_TO_STORE); printLog("_mapCount=%d\n",_mapCount); printLog("_checkMapRootCalls=%d\n",_checkMapRootCalls); + printLog("_notAllInView=%d\n",_notAllInView); printLog("_maxPolygonsUsed=%d\n",CoverageRegion::_maxPolygonsUsed); printLog("_totalPolygons=%d\n",CoverageRegion::_totalPolygons); printLog("_occlusionTests=%d\n",CoverageRegion::_occlusionTests); + printLog("_regionSkips=%d\n",CoverageRegion::_regionSkips); + printLog("_tooSmallSkips=%d\n",CoverageRegion::_tooSmallSkips); printLog("_outOfOrderPolygon=%d\n",CoverageRegion::_outOfOrderPolygon); CoverageRegion::_maxPolygonsUsed = 0; CoverageRegion::_totalPolygons = 0; CoverageRegion::_occlusionTests = 0; + CoverageRegion::_regionSkips = 0; + CoverageRegion::_tooSmallSkips = 0; CoverageRegion::_outOfOrderPolygon = 0; _mapCount = 0; _checkMapRootCalls = 0; + _notAllInView = 0; } } @@ -131,6 +138,7 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b // short circuit: we don't handle polygons that aren't all in view, so, if the polygon in question is // not in view, then we just discard it with a DOESNT_FIT, this saves us time checking values later. if (!polygon->getAllInView()) { + _notAllInView++; return DOESNT_FIT; } @@ -195,6 +203,7 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b storeIn->storeInArray(polygon); return STORED; } else { + CoverageRegion::_tooSmallSkips++; return NOT_STORED; } } else { @@ -297,6 +306,8 @@ const char* CoverageRegion::getRegionName() const { int CoverageRegion::_maxPolygonsUsed = 0; int CoverageRegion::_totalPolygons = 0; int CoverageRegion::_occlusionTests = 0; +int CoverageRegion::_regionSkips = 0; +int CoverageRegion::_tooSmallSkips = 0; int CoverageRegion::_outOfOrderPolygon = 0; // just handles storage in the array, doesn't test for occlusion or @@ -305,6 +316,8 @@ void CoverageRegion::storeInArray(VoxelProjectedPolygon* polygon) { _totalPolygons++; + _currentCoveredBounds.explandToInclude(polygon->getBoundingBox()); + if (_polygonArraySize < _polygonCount + 1) { growPolygonArray(); } @@ -316,7 +329,7 @@ void CoverageRegion::storeInArray(VoxelProjectedPolygon* polygon) { _polygonCount = insertIntoSortedArrays((void*)polygon, polygon->getDistance(), IGNORED, (void**)_polygons, _polygonDistances, IGNORED, _polygonCount, _polygonArraySize); - + // Debugging and Optimization Tuning code. if (_polygonCount > _maxPolygonsUsed) { _maxPolygonsUsed = _polygonCount; @@ -335,37 +348,43 @@ CoverageMapStorageResult CoverageRegion::checkRegion(VoxelProjectedPolygon* poly if (_isRoot || _myBoundingBox.contains(polygonBox)) { result = NOT_STORED; // if we got here, then we DO fit... - - // check to make sure this polygon isn't occluded by something at this level - for (int i = 0; i < _polygonCount; i++) { - VoxelProjectedPolygon* polygonAtThisLevel = _polygons[i]; - // Check to make sure that the polygon in question is "behind" the polygon in the list - // otherwise, we don't need to test it's occlusion (although, it means we've potentially - // added an item previously that may be occluded??? Is that possible? Maybe not, because two - // voxels can't have the exact same outline. So one occludes the other, they can't both occlude - // each other. + + // only actually check the polygons if this polygon is in the covered bounds for this region + if (!_currentCoveredBounds.contains(polygonBox)) { + _regionSkips += _polygonCount; + } else { + // check to make sure this polygon isn't occluded by something at this level + for (int i = 0; i < _polygonCount; i++) { + VoxelProjectedPolygon* polygonAtThisLevel = _polygons[i]; + // Check to make sure that the polygon in question is "behind" the polygon in the list + // otherwise, we don't need to test it's occlusion (although, it means we've potentially + // added an item previously that may be occluded??? Is that possible? Maybe not, because two + // voxels can't have the exact same outline. So one occludes the other, they can't both occlude + // each other. - _occlusionTests++; - if (polygonAtThisLevel->occludes(*polygon)) { - // if the polygonAtThisLevel is actually behind the one we're inserting, then we don't - // want to report our inserted one as occluded, but we do want to add our inserted one. - if (polygonAtThisLevel->getDistance() >= polygon->getDistance()) { - _outOfOrderPolygon++; - if (storeIt) { - if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) { - //printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area()); - storeInArray(polygon); - return STORED; + _occlusionTests++; + if (polygonAtThisLevel->occludes(*polygon)) { + // if the polygonAtThisLevel is actually behind the one we're inserting, then we don't + // want to report our inserted one as occluded, but we do want to add our inserted one. + if (polygonAtThisLevel->getDistance() >= polygon->getDistance()) { + _outOfOrderPolygon++; + if (storeIt) { + if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) { + //printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area()); + storeInArray(polygon); + return STORED; + } else { + _tooSmallSkips++; + return NOT_STORED; + } } else { return NOT_STORED; } - } else { - return NOT_STORED; } + // this polygon is occluded by a closer polygon, so don't store it, and let the caller know + return OCCLUDED; } - // this polygon is occluded by a closer polygon, so don't store it, and let the caller know - return OCCLUDED; } } } diff --git a/libraries/voxels/src/CoverageMap.h b/libraries/voxels/src/CoverageMap.h index 8b01eabeee..96668d8663 100644 --- a/libraries/voxels/src/CoverageMap.h +++ b/libraries/voxels/src/CoverageMap.h @@ -31,6 +31,8 @@ public: static int _maxPolygonsUsed; static int _totalPolygons; static int _occlusionTests; + static int _regionSkips; + static int _tooSmallSkips; static int _outOfOrderPolygon; @@ -41,6 +43,7 @@ private: bool _isRoot; // is this map the root, if so, it never returns DOESNT_FIT BoundingBox _myBoundingBox; + BoundingBox _currentCoveredBounds; // area in this region currently covered by some polygon bool _managePolygons; // will the coverage map delete the polygons on destruct RegionName _regionName; int _polygonCount; // how many polygons at this level @@ -89,6 +92,7 @@ private: static int _mapCount; static int _checkMapRootCalls; + static int _notAllInView; }; diff --git a/libraries/voxels/src/VoxelProjectedPolygon.cpp b/libraries/voxels/src/VoxelProjectedPolygon.cpp index c609b27df4..b11a0df382 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.cpp +++ b/libraries/voxels/src/VoxelProjectedPolygon.cpp @@ -35,7 +35,7 @@ BoundingBox BoundingBox::rightHalf() const { } bool BoundingBox::contains(const BoundingBox& box) const { - return ( + return ( _set && (box.corner.x >= corner.x) && (box.corner.y >= corner.y) && (box.corner.x + box.size.x <= corner.x + size.x) && @@ -43,6 +43,40 @@ bool BoundingBox::contains(const BoundingBox& box) const { ); }; +void BoundingBox::explandToInclude(const BoundingBox& box) { + if (!_set) { + corner = box.corner; + size = box.size; + _set = true; + } else { + // example: + // original bounding [c:1,1 s:1,1] => [1,1]->[2,2] + // expand to include [c:0.5,0.5 s:2,2] => [0.5,0.5]->[2.5,2.5] + // new bounding [0.5,0.5]->[2.5,2.5] or [c:0.5,0.5 s:2,2] + + + if (box.corner.x < corner.x) { + corner.x = box.corner.x; + size.x += (corner.x - box.corner.x); + } + // new state... [c:0.5,1 s:1.5,1] + if (box.corner.y < corner.y) { + corner.y = box.corner.y; + size.y += (corner.y - box.corner.y); + } + // new state... [c:0.5,0.5 s:1.5,1.5] + if ((box.corner.x + box.size.x) > (corner.x + size.x)) { + size.x += ((box.corner.x + box.size.x) - (corner.x + size.x)); + } + // new state... [c:0.5,0.5 s:2,1.5] + if ((box.corner.y + box.size.y) > (corner.y + size.y)) { + size.y += ((box.corner.y + box.size.y) - (corner.y + size.y)); + } + // new state... [c:0.5,0.5 s:2,2] + } +} + + void BoundingBox::printDebugDetails(const char* label) const { if (label) { printLog(label); diff --git a/libraries/voxels/src/VoxelProjectedPolygon.h b/libraries/voxels/src/VoxelProjectedPolygon.h index 8123ca6a85..4b25a53ee8 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.h +++ b/libraries/voxels/src/VoxelProjectedPolygon.h @@ -16,10 +16,13 @@ typedef glm::vec2 ShadowVertices[MAX_SHADOW_VERTEX_COUNT]; class BoundingBox { public: - BoundingBox(glm::vec2 corner, glm::vec2 size) : corner(corner), size(size) {}; + BoundingBox(glm::vec2 corner, glm::vec2 size) : corner(corner), size(size), _set(true) {}; + BoundingBox() : _set(false) {}; glm::vec2 corner; glm::vec2 size; bool contains(const BoundingBox& box) const; + void explandToInclude(const BoundingBox& box); + float area() const { return size.x * size.y; }; BoundingBox topHalf() const; @@ -28,6 +31,8 @@ public: BoundingBox rightHalf() const; void printDebugDetails(const char* label=NULL) const; +private: + bool _set; }; class VoxelProjectedPolygon { From 4590bdc69d11ab84d62bfdc8419406216d8ed60c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Jun 2013 16:42:55 -0700 Subject: [PATCH 02/55] added support for clipping polygons to screen bounds, not yet enabled since it doesn't help performance, will use in future --- interface/src/VoxelSystem.cpp | 3 +- libraries/voxels/src/CoverageMap.cpp | 10 +- libraries/voxels/src/CoverageMap.h | 1 + libraries/voxels/src/GeometryUtil.cpp | 194 ++++++++++++++++++ libraries/voxels/src/GeometryUtil.h | 35 ++++ libraries/voxels/src/ViewFrustum.cpp | 46 ++++- .../voxels/src/VoxelProjectedPolygon.cpp | 39 ++-- libraries/voxels/src/VoxelProjectedPolygon.h | 11 +- 8 files changed, 295 insertions(+), 44 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 69edf376ee..bdd109aa97 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -1283,7 +1283,8 @@ void VoxelSystem::falseColorizeOccluded() { _tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, position, (void*)&args); - printLog("falseColorizeOccluded()\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n", + printLog("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n", + position.x, position.y, args.totalVoxels, args.coloredVoxels, args.occludedVoxels, args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped, args.stagedForDeletion, diff --git a/libraries/voxels/src/CoverageMap.cpp b/libraries/voxels/src/CoverageMap.cpp index 0f8df158b3..56b8b62541 100644 --- a/libraries/voxels/src/CoverageMap.cpp +++ b/libraries/voxels/src/CoverageMap.cpp @@ -16,7 +16,7 @@ int CoverageMap::_checkMapRootCalls = 0; int CoverageMap::_notAllInView = 0; bool CoverageMap::wantDebugging = false; -const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-2.f,-2.f), glm::vec2(4.f,4.f)); +const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-1.f,-1.f), glm::vec2(2.f,2.f)); // Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space. // @@ -92,12 +92,15 @@ void CoverageMap::erase() { printLog("_regionSkips=%d\n",CoverageRegion::_regionSkips); printLog("_tooSmallSkips=%d\n",CoverageRegion::_tooSmallSkips); printLog("_outOfOrderPolygon=%d\n",CoverageRegion::_outOfOrderPolygon); + printLog("_clippedPolygons=%d\n",CoverageRegion::_clippedPolygons); + CoverageRegion::_maxPolygonsUsed = 0; CoverageRegion::_totalPolygons = 0; CoverageRegion::_occlusionTests = 0; CoverageRegion::_regionSkips = 0; CoverageRegion::_tooSmallSkips = 0; CoverageRegion::_outOfOrderPolygon = 0; + CoverageRegion::_clippedPolygons = 0; _mapCount = 0; _checkMapRootCalls = 0; _notAllInView = 0; @@ -237,7 +240,7 @@ void CoverageRegion::init() { void CoverageRegion::erase() { -/* +/**/ if (_polygonCount) { printLog("CoverageRegion::erase()...\n"); printLog("_polygonCount=%d\n",_polygonCount); @@ -247,7 +250,7 @@ void CoverageRegion::erase() { // _polygons[i]->getBoundingBox().printDebugDetails(); //} } -*/ +/**/ // If we're in charge of managing the polygons, then clean them up first if (_managePolygons) { for (int i = 0; i < _polygonCount; i++) { @@ -309,6 +312,7 @@ int CoverageRegion::_occlusionTests = 0; int CoverageRegion::_regionSkips = 0; int CoverageRegion::_tooSmallSkips = 0; int CoverageRegion::_outOfOrderPolygon = 0; +int CoverageRegion::_clippedPolygons = 0; // just handles storage in the array, doesn't test for occlusion or // determining if this is the correct map to store in! diff --git a/libraries/voxels/src/CoverageMap.h b/libraries/voxels/src/CoverageMap.h index 96668d8663..736f3a125e 100644 --- a/libraries/voxels/src/CoverageMap.h +++ b/libraries/voxels/src/CoverageMap.h @@ -34,6 +34,7 @@ public: static int _regionSkips; static int _tooSmallSkips; static int _outOfOrderPolygon; + static int _clippedPolygons; const char* getRegionName() const; diff --git a/libraries/voxels/src/GeometryUtil.cpp b/libraries/voxels/src/GeometryUtil.cpp index 1fc4e57013..af2a6dfc95 100644 --- a/libraries/voxels/src/GeometryUtil.cpp +++ b/libraries/voxels/src/GeometryUtil.cpp @@ -5,6 +5,9 @@ // Created by Andrzej Kapolka on 5/21/13. // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +#include + +#include #include #include "GeometryUtil.h" @@ -117,6 +120,7 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& } // Do line segments (r1p1.x, r1p1.y)--(r1p2.x, r1p2.y) and (r2p1.x, r2p1.y)--(r2p2.x, r2p2.y) intersect? +// from: http://ptspts.blogspot.com/2010/06/how-to-determine-if-two-line-segments.html bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2) { int d1 = computeDirection(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p1.x, r1p1.y); int d2 = computeDirection(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p2.x, r1p2.y); @@ -140,3 +144,193 @@ int computeDirection(float xi, float yi, float xj, float yj, float xk, float yk) float b = (xj - xi) * (yk - yi); return a < b ? -1 : a > b ? 1 : 0; } + + +// +// Polygon Clipping routines inspired by, pseudo code found here: http://www.cs.rit.edu/~icss571/clipTrans/PolyClipBack.html +// +// Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space. +// +// (0,0) (windowWidth, 0) +// -1,1 1,1 +// +-----------------------+ +// | | | +// | | | +// | -1,0 | | +// |-----------+-----------| +// | 0,0 | +// | | | +// | | | +// | | | +// +-----------------------+ +// -1,-1 1,-1 +// (0,windowHeight) (windowWidth,windowHeight) +// + +const float PolygonClip::TOP_OF_CLIPPING_WINDOW = 1.0f; +const float PolygonClip::BOTTOM_OF_CLIPPING_WINDOW = -1.0f; +const float PolygonClip::LEFT_OF_CLIPPING_WINDOW = -1.0f; +const float PolygonClip::RIGHT_OF_CLIPPING_WINDOW = 1.0f; + +const glm::vec2 PolygonClip::TOP_LEFT_CLIPPING_WINDOW ( LEFT_OF_CLIPPING_WINDOW , TOP_OF_CLIPPING_WINDOW ); +const glm::vec2 PolygonClip::TOP_RIGHT_CLIPPING_WINDOW ( RIGHT_OF_CLIPPING_WINDOW, TOP_OF_CLIPPING_WINDOW ); +const glm::vec2 PolygonClip::BOTTOM_LEFT_CLIPPING_WINDOW ( LEFT_OF_CLIPPING_WINDOW , BOTTOM_OF_CLIPPING_WINDOW ); +const glm::vec2 PolygonClip::BOTTOM_RIGHT_CLIPPING_WINDOW ( RIGHT_OF_CLIPPING_WINDOW, BOTTOM_OF_CLIPPING_WINDOW ); + +void PolygonClip::clipToScreen(const glm::vec2* inputVertexArray, int inLength, glm::vec2*& outputVertexArray, int& outLength) { + int tempLengthA = inLength; + int tempLengthB; + int maxLength = inLength * 2; + glm::vec2* tempVertexArrayA = new glm::vec2[maxLength]; + glm::vec2* tempVertexArrayB = new glm::vec2[maxLength]; + + // set up our temporary arrays + memcpy(tempVertexArrayA, inputVertexArray, sizeof(glm::vec2) * inLength); + + // Left edge + LineSegment2 edge; + edge[0] = TOP_LEFT_CLIPPING_WINDOW; + edge[1] = BOTTOM_LEFT_CLIPPING_WINDOW; + // clip the array from tempVertexArrayA and copy end result to tempVertexArrayB + sutherlandHodgmanPolygonClip(tempVertexArrayA, tempVertexArrayB, tempLengthA, tempLengthB, edge); + // clean the array from tempVertexArrayA and copy cleaned result to tempVertexArrayA + copyCleanArray(tempLengthA, tempVertexArrayA, tempLengthB, tempVertexArrayB); + + // Bottom Edge + edge[0] = BOTTOM_LEFT_CLIPPING_WINDOW; + edge[1] = BOTTOM_RIGHT_CLIPPING_WINDOW; + // clip the array from tempVertexArrayA and copy end result to tempVertexArrayB + sutherlandHodgmanPolygonClip(tempVertexArrayA, tempVertexArrayB, tempLengthA, tempLengthB, edge); + // clean the array from tempVertexArrayA and copy cleaned result to tempVertexArrayA + copyCleanArray(tempLengthA, tempVertexArrayA, tempLengthB, tempVertexArrayB); + + // Right Edge + edge[0] = BOTTOM_RIGHT_CLIPPING_WINDOW; + edge[1] = TOP_RIGHT_CLIPPING_WINDOW; + // clip the array from tempVertexArrayA and copy end result to tempVertexArrayB + sutherlandHodgmanPolygonClip(tempVertexArrayA, tempVertexArrayB, tempLengthA, tempLengthB, edge); + // clean the array from tempVertexArrayA and copy cleaned result to tempVertexArrayA + copyCleanArray(tempLengthA, tempVertexArrayA, tempLengthB, tempVertexArrayB); + + // Top Edge + edge[0] = TOP_RIGHT_CLIPPING_WINDOW; + edge[1] = TOP_LEFT_CLIPPING_WINDOW; + // clip the array from tempVertexArrayA and copy end result to tempVertexArrayB + sutherlandHodgmanPolygonClip(tempVertexArrayA, tempVertexArrayB, tempLengthA, tempLengthB, edge); + // clean the array from tempVertexArrayA and copy cleaned result to tempVertexArrayA + copyCleanArray(tempLengthA, tempVertexArrayA, tempLengthB, tempVertexArrayB); + + // copy final output to outputVertexArray + outputVertexArray = tempVertexArrayA; + outLength = tempLengthA; + + // cleanup our unused temporary buffer... + delete[] tempVertexArrayB; + + // Note: we don't delete tempVertexArrayA, because that's the caller's responsibility +} + +void PolygonClip::sutherlandHodgmanPolygonClip(glm::vec2* inVertexArray, glm::vec2* outVertexArray, + int inLength, int& outLength, const LineSegment2& clipBoundary) { + glm::vec2 start, end; // Start, end point of current polygon edge + glm::vec2 intersection; // Intersection point with a clip boundary + + outLength = 0; + start = inVertexArray[inLength - 1]; // Start with the last vertex in inVertexArray + for (int j = 0; j < inLength; j++) { + end = inVertexArray[j]; // Now start and end correspond to the vertices + + // Cases 1 and 4 - the endpoint is inside the boundary + if (pointInsideBoundary(end,clipBoundary)) { + // Case 1 - Both inside + if (pointInsideBoundary(start, clipBoundary)) { + appendPoint(end, outLength, outVertexArray); + } else { // Case 4 - end is inside, but start is outside + segmentIntersectsBoundary(start, end, clipBoundary, intersection); + appendPoint(intersection, outLength, outVertexArray); + appendPoint(end, outLength, outVertexArray); + } + } else { // Cases 2 and 3 - end is outside + if (pointInsideBoundary(start, clipBoundary)) { + // Cases 2 - start is inside, end is outside + segmentIntersectsBoundary(start, end, clipBoundary, intersection); + appendPoint(intersection, outLength, outVertexArray); + } else { + // Case 3 - both are outside, No action + } + } + start = end; // Advance to next pair of vertices + } +} + +bool PolygonClip::pointInsideBoundary(const glm::vec2& testVertex, const LineSegment2& clipBoundary) { + // bottom edge + if (clipBoundary[1].x > clipBoundary[0].x) { + if (testVertex.y >= clipBoundary[0].y) { + return true; + } + } + // top edge + if (clipBoundary[1].x < clipBoundary[0].x) { + if (testVertex.y <= clipBoundary[0].y) { + return true; + } + } + // right edge + if (clipBoundary[1].y > clipBoundary[0].y) { + if (testVertex.x <= clipBoundary[1].x) { + return true; + } + } + // left edge + if (clipBoundary[1].y < clipBoundary[0].y) { + if (testVertex.x >= clipBoundary[1].x) { + return true; + } + } + return false; +} + +void PolygonClip::segmentIntersectsBoundary(const glm::vec2& first, const glm::vec2& second, + const LineSegment2& clipBoundary, glm::vec2& intersection) { + // horizontal + if (clipBoundary[0].y==clipBoundary[1].y) { + intersection.y = clipBoundary[0].y; + intersection.x = first.x + (clipBoundary[0].y - first.y) * (second.x - first.x) / (second.y - first.y); + } else { // Vertical + intersection.x = clipBoundary[0].x; + intersection.y = first.y + (clipBoundary[0].x - first.x) * (second.y - first.y) / (second.x - first.x); + } +} + +void PolygonClip::appendPoint(glm::vec2 newVertex, int& outLength, glm::vec2* outVertexArray) { + outVertexArray[outLength].x = newVertex.x; + outVertexArray[outLength].y = newVertex.y; + outLength++; +} + +// The copyCleanArray() function sets the resulting polygon of the previous step up to be the input polygon for next step of the +// clipping algorithm. As the Sutherland-Hodgman algorithm is a polygon clipping algorithm, it does not handle line +// clipping very well. The modification so that lines may be clipped as well as polygons is included in this function. +// when completed vertexArrayA will be ready for output and/or next step of clipping +void PolygonClip::copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& lengthB, glm::vec2* vertexArrayB) { + // Fix lines: they will come back with a length of 3, from an original of length of 2 + if ((lengthA == 2) && (lengthB == 3)) { + // The first vertex should be copied as is. + vertexArrayA[0] = vertexArrayB[0]; + // If the first two vertices of the "B" array are same, then collapse them down to be the 2nd vertex + if (vertexArrayB[0].x == vertexArrayB[1].x) { + vertexArrayA[1] = vertexArrayB[2]; + } else { + // Otherwise the first vertex should be the same as third vertex + vertexArrayA[1] = vertexArrayB[1]; + } + lengthA=2; + } else { + // for all other polygons, then just copy the vertexArrayB to vertextArrayA for next step + lengthA = lengthB; + for (int i = 0; i < lengthB; i++) { + vertexArrayA[i] = vertexArrayB[i]; + } + } +} diff --git a/libraries/voxels/src/GeometryUtil.h b/libraries/voxels/src/GeometryUtil.h index b16ad4e2c0..bb053d20c6 100644 --- a/libraries/voxels/src/GeometryUtil.h +++ b/libraries/voxels/src/GeometryUtil.h @@ -43,4 +43,39 @@ bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm bool isOnSegment(float xi, float yi, float xj, float yj, float xk, float yk); int computeDirection(float xi, float yi, float xj, float yj, float xk, float yk); + +typedef glm::vec2 LineSegment2[2]; + +// Polygon Clipping routines inspired by, pseudo code found here: http://www.cs.rit.edu/~icss571/clipTrans/PolyClipBack.html +class PolygonClip { + +public: + static void clipToScreen(const glm::vec2* inputVertexArray, int length, glm::vec2*& outputVertexArray, int& outLength); + + static const float TOP_OF_CLIPPING_WINDOW; + static const float BOTTOM_OF_CLIPPING_WINDOW; + static const float LEFT_OF_CLIPPING_WINDOW; + static const float RIGHT_OF_CLIPPING_WINDOW; + + static const glm::vec2 TOP_LEFT_CLIPPING_WINDOW; + static const glm::vec2 TOP_RIGHT_CLIPPING_WINDOW; + static const glm::vec2 BOTTOM_LEFT_CLIPPING_WINDOW; + static const glm::vec2 BOTTOM_RIGHT_CLIPPING_WINDOW; + +private: + + static void sutherlandHodgmanPolygonClip(glm::vec2* inVertexArray, glm::vec2* outVertexArray, + int inLength, int& outLength, const LineSegment2& clipBoundary); + + static bool pointInsideBoundary(const glm::vec2& testVertex, const LineSegment2& clipBoundary); + + static void segmentIntersectsBoundary(const glm::vec2& first, const glm::vec2& second, + const LineSegment2& clipBoundary, glm::vec2& intersection); + + static void appendPoint(glm::vec2 newVertex, int& outLength, glm::vec2* outVertexArray); + + static void copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& lengthB, glm::vec2* vertexArrayB); +}; + + #endif /* defined(__interface__GeometryUtil__) */ diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index 4ded457d44..36606e6702 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -12,11 +12,15 @@ #include -#include "ViewFrustum.h" -#include "VoxelConstants.h" #include "SharedUtil.h" #include "Log.h" +#include "CoverageMap.h" +#include "GeometryUtil.h" +#include "ViewFrustum.h" +#include "VoxelConstants.h" + + using namespace std; ViewFrustum::ViewFrustum() : @@ -451,7 +455,7 @@ glm::vec2 ViewFrustum::projectPoint(glm::vec3 point, bool& pointInView) const { const int MAX_POSSIBLE_COMBINATIONS = 43; -const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_SHADOW_VERTEX_COUNT+1] = { +const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_PROJECTED_POLYGON_VERTEX_COUNT+1] = { // Number of vertices in shadow polygon for the visible faces, then a list of the index of each vertice from the AABox {0}, // inside {4, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR}, // right @@ -510,7 +514,7 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { int vertexCount = hullVertexLookup[lookUp][0]; //look up number of vertices - VoxelProjectedPolygon shadow(vertexCount); + VoxelProjectedPolygon projectedPolygon(vertexCount); bool pointInView = true; bool allPointsInView = false; // assume the best, but wait till we know we have a vertex @@ -523,13 +527,37 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { glm::vec2 projectedPoint = projectPoint(point, pointInView); allPointsInView = allPointsInView && pointInView; anyPointsInView = anyPointsInView || pointInView; - shadow.setVertex(i, projectedPoint); + projectedPolygon.setVertex(i, projectedPoint); } + + /*** + // Now that we've got the polygon, if it extends beyond the clipping window, then let's clip it + // NOTE: This clipping does not improve our overall performance. It basically causes more polygons to + // end up in the same quad/half and so the polygon lists get longer, and that's more calls to polygon.occludes() + if ( (projectedPolygon.getMaxX() > PolygonClip::RIGHT_OF_CLIPPING_WINDOW ) || + (projectedPolygon.getMaxY() > PolygonClip::TOP_OF_CLIPPING_WINDOW ) || + (projectedPolygon.getMaxX() < PolygonClip::LEFT_OF_CLIPPING_WINDOW ) || + (projectedPolygon.getMaxY() < PolygonClip::BOTTOM_OF_CLIPPING_WINDOW) ) { + + CoverageRegion::_clippedPolygons++; + + glm::vec2* clippedVertices; + int clippedVertexCount; + PolygonClip::clipToScreen(projectedPolygon.getVertices(), vertexCount, clippedVertices, clippedVertexCount); + + // Now reset the vertices of our projectedPolygon object + projectedPolygon.setVertexCount(clippedVertexCount); + for(int i = 0; i < clippedVertexCount; i++) { + projectedPolygon.setVertex(i, clippedVertices[i]); + } + delete[] clippedVertices; + } + ***/ } // set the distance from our camera position, to the closest vertex float distance = glm::distance(getPosition(), box.getCenter()); - shadow.setDistance(distance); - shadow.setAnyInView(anyPointsInView); - shadow.setAllInView(allPointsInView); - return shadow; + projectedPolygon.setDistance(distance); + projectedPolygon.setAnyInView(anyPointsInView); + projectedPolygon.setAllInView(allPointsInView); + return projectedPolygon; } diff --git a/libraries/voxels/src/VoxelProjectedPolygon.cpp b/libraries/voxels/src/VoxelProjectedPolygon.cpp index b11a0df382..db02117918 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.cpp +++ b/libraries/voxels/src/VoxelProjectedPolygon.cpp @@ -5,9 +5,11 @@ // Added by Brad Hefta-Gaub on 06/11/13. // +#include #include "VoxelProjectedPolygon.h" #include "GeometryUtil.h" #include "Log.h" +#include "SharedUtil.h" BoundingBox BoundingBox::topHalf() const { @@ -35,7 +37,7 @@ BoundingBox BoundingBox::rightHalf() const { } bool BoundingBox::contains(const BoundingBox& box) const { - return ( _set && + return ( _set && (box.corner.x >= corner.x) && (box.corner.y >= corner.y) && (box.corner.x + box.size.x <= corner.x + size.x) && @@ -49,30 +51,14 @@ void BoundingBox::explandToInclude(const BoundingBox& box) { size = box.size; _set = true; } else { - // example: - // original bounding [c:1,1 s:1,1] => [1,1]->[2,2] - // expand to include [c:0.5,0.5 s:2,2] => [0.5,0.5]->[2.5,2.5] - // new bounding [0.5,0.5]->[2.5,2.5] or [c:0.5,0.5 s:2,2] - - - if (box.corner.x < corner.x) { - corner.x = box.corner.x; - size.x += (corner.x - box.corner.x); - } - // new state... [c:0.5,1 s:1.5,1] - if (box.corner.y < corner.y) { - corner.y = box.corner.y; - size.y += (corner.y - box.corner.y); - } - // new state... [c:0.5,0.5 s:1.5,1.5] - if ((box.corner.x + box.size.x) > (corner.x + size.x)) { - size.x += ((box.corner.x + box.size.x) - (corner.x + size.x)); - } - // new state... [c:0.5,0.5 s:2,1.5] - if ((box.corner.y + box.size.y) > (corner.y + size.y)) { - size.y += ((box.corner.y + box.size.y) - (corner.y + size.y)); - } - // new state... [c:0.5,0.5 s:2,2] + float minX = std::min(box.corner.x, corner.x); + float minY = std::min(box.corner.y, corner.y); + float maxX = std::max(box.corner.x + box.size.x, corner.x + size.x); + float maxY = std::max(box.corner.y + box.size.y, corner.y + size.y); + corner.x = minX; + corner.y = minY; + size.x = maxX - minX; + size.y = maxY - minY; } } @@ -83,7 +69,8 @@ void BoundingBox::printDebugDetails(const char* label) const { } else { printLog("BoundingBox"); } - printLog("\n corner=%f,%f size=%f,%f\n", corner.x, corner.y, size.x, size.y); + printLog("\n _set=%s\n corner=%f,%f size=%f,%f\n bounds=[(%f,%f) to (%f,%f)]\n", + debug::valueOf(_set), corner.x, corner.y, size.x, size.y, corner.x, corner.y, corner.x+size.x, corner.y+size.y); } diff --git a/libraries/voxels/src/VoxelProjectedPolygon.h b/libraries/voxels/src/VoxelProjectedPolygon.h index 4b25a53ee8..2a4b1092a6 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.h +++ b/libraries/voxels/src/VoxelProjectedPolygon.h @@ -10,9 +10,10 @@ #include -const int MAX_SHADOW_VERTEX_COUNT = 6; - -typedef glm::vec2 ShadowVertices[MAX_SHADOW_VERTEX_COUNT]; +// there's a max of 6 vertices of a project polygon, and a max of twice that when clipped to the screen +const int MAX_PROJECTED_POLYGON_VERTEX_COUNT = 6; +const int MAX_CLIPPED_PROJECTED_POLYGON_VERTEX_COUNT = MAX_PROJECTED_POLYGON_VERTEX_COUNT * 2; +typedef glm::vec2 ProjectedVertices[MAX_CLIPPED_PROJECTED_POLYGON_VERTEX_COUNT]; class BoundingBox { public: @@ -45,7 +46,7 @@ public: { }; ~VoxelProjectedPolygon() { }; - const ShadowVertices& getVerices() const { return _vertices; }; + const ProjectedVertices& getVertices() const { return _vertices; }; const glm::vec2& getVertex(int i) const { return _vertices[i]; }; void setVertex(int vertex, const glm::vec2& point); int getVertexCount() const { return _vertexCount; }; @@ -75,7 +76,7 @@ public: private: int _vertexCount; - ShadowVertices _vertices; + ProjectedVertices _vertices; float _maxX; float _maxY; float _minX; From f918d757cad4ca0a5ba9f8b800c0cdd9a440e440 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 24 Jun 2013 10:12:14 -0700 Subject: [PATCH 03/55] added comment --- libraries/voxels/src/VoxelProjectedPolygon.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/voxels/src/VoxelProjectedPolygon.cpp b/libraries/voxels/src/VoxelProjectedPolygon.cpp index db02117918..dbebb298ad 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.cpp +++ b/libraries/voxels/src/VoxelProjectedPolygon.cpp @@ -121,6 +121,9 @@ bool VoxelProjectedPolygon::occludes(const VoxelProjectedPolygon& occludee, bool } } + // If we are a concave polygon, then it's not good enough to know that all of the occludee's points are + // inside us, we also need to make sure than none of the occludee's edges intersect any of our edges. + // if we got this far, then indeed the occludee is fully occluded by us return true; } From 850cfb920f8e190ea12a6ed1d07c1066c708c8a8 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 24 Jun 2013 22:11:33 -0700 Subject: [PATCH 04/55] added sorting polygons by size --- libraries/voxels/src/CoverageMap.cpp | 63 ++++++++++++++++++++++++---- libraries/voxels/src/CoverageMap.h | 3 ++ 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/libraries/voxels/src/CoverageMap.cpp b/libraries/voxels/src/CoverageMap.cpp index 56b8b62541..db2976a17f 100644 --- a/libraries/voxels/src/CoverageMap.cpp +++ b/libraries/voxels/src/CoverageMap.cpp @@ -235,6 +235,7 @@ void CoverageRegion::init() { _polygonArraySize = 0; _polygons = NULL; _polygonDistances = NULL; + _polygonSizes = NULL; } @@ -270,11 +271,16 @@ void CoverageRegion::erase() { delete[] _polygonDistances; _polygonDistances = NULL; } + if (_polygonSizes) { + delete[] _polygonSizes; + _polygonSizes = NULL; + } } void CoverageRegion::growPolygonArray() { VoxelProjectedPolygon** newPolygons = new VoxelProjectedPolygon*[_polygonArraySize + DEFAULT_GROW_SIZE]; - float* newDistances = new float[_polygonArraySize + DEFAULT_GROW_SIZE]; + float* newDistances = new float[_polygonArraySize + DEFAULT_GROW_SIZE]; + float* newSizes = new float[_polygonArraySize + DEFAULT_GROW_SIZE]; if (_polygons) { @@ -282,9 +288,12 @@ void CoverageRegion::growPolygonArray() { delete[] _polygons; memcpy(newDistances, _polygonDistances, sizeof(float) * _polygonCount); delete[] _polygonDistances; + memcpy(newSizes, _polygonSizes, sizeof(float) * _polygonCount); + delete[] _polygonSizes; } - _polygons = newPolygons; + _polygons = newPolygons; _polygonDistances = newDistances; + _polygonSizes = newSizes; _polygonArraySize = _polygonArraySize + DEFAULT_GROW_SIZE; //printLog("CoverageMap::growPolygonArray() _polygonArraySize=%d...\n",_polygonArraySize); } @@ -326,13 +335,28 @@ void CoverageRegion::storeInArray(VoxelProjectedPolygon* polygon) { growPolygonArray(); } - // This old code assumes that polygons will always be added in z-buffer order, but that doesn't seem to - // be a good assumption. So instead, we will need to sort this by distance. Use a binary search to find the - // insertion point in this array, and shift the array accordingly - const int IGNORED = NULL; - _polygonCount = insertIntoSortedArrays((void*)polygon, polygon->getDistance(), IGNORED, - (void**)_polygons, _polygonDistances, IGNORED, - _polygonCount, _polygonArraySize); + // As an experiment we're going to see if we get an improvement by storing the polygons in coverage area sorted order + // this means the bigger polygons are earlier in the array. We should have a higher probability of being occluded earlier + // in the list. We still check to see if the polygon is "in front" of the target polygon before we test occlusion. Since + // sometimes things come out of order. + const bool SORT_BY_SIZE = false; + if (SORT_BY_SIZE) { + // This old code assumes that polygons will always be added in z-buffer order, but that doesn't seem to + // be a good assumption. So instead, we will need to sort this by distance. Use a binary search to find the + // insertion point in this array, and shift the array accordingly + const int IGNORED = NULL; + float area = polygon->getBoundingBox().area(); + float reverseArea = 4.0f - area; +printLog("store by size area=%f reverse area=%f\n", area, reverseArea); + _polygonCount = insertIntoSortedArrays((void*)polygon, reverseArea, IGNORED, + (void**)_polygons, _polygonSizes, IGNORED, + _polygonCount, _polygonArraySize); + } else { + const int IGNORED = NULL; + _polygonCount = insertIntoSortedArrays((void*)polygon, polygon->getDistance(), IGNORED, + (void**)_polygons, _polygonDistances, IGNORED, + _polygonCount, _polygonArraySize); + } // Debugging and Optimization Tuning code. if (_polygonCount > _maxPolygonsUsed) { @@ -394,3 +418,24 @@ CoverageMapStorageResult CoverageRegion::checkRegion(VoxelProjectedPolygon* poly } return result; } + + + + +///////////////////////////////////////////////////////////////// +// Notes on improvements. +// +// Let's say that we are going to combine polygon projects together if they intersect. How would we do that? +// +// On "check/insert"... +// We start at top of QuadTree, and we check to see if the checkpolygon's bounding box overlaps with any bounding boxes of +// polygons in the current quad level. +// If it overlaps, we check to see if the "in map" polygon occludes the checkPolygon. +// This operation could create side data that tells us: +// 1) checkPolygon is COMPLETELY outside of levelPolygon << If so, no occlusion, and can't be combined +// 2) checkPolygon is COMPLETELY INSIDE of levelPolygon << If so, it is occluded and does not need to be combined +// 3) checkPolygon has some points INSIDE some OUTSIDE +// 3a) which vertices are "inside" +// 3b) which vertices are "outside" +// 3c) for all pairs of vertices for which one is "inside" and the other "outside" we can determine an +// intersection point. This point will be used in our "union" diff --git a/libraries/voxels/src/CoverageMap.h b/libraries/voxels/src/CoverageMap.h index 736f3a125e..083043fd14 100644 --- a/libraries/voxels/src/CoverageMap.h +++ b/libraries/voxels/src/CoverageMap.h @@ -50,7 +50,10 @@ private: int _polygonCount; // how many polygons at this level int _polygonArraySize; // how much room is there to store polygons at this level VoxelProjectedPolygon** _polygons; + + // we will use one or the other of these depending on settings in the code. float* _polygonDistances; + float* _polygonSizes; void growPolygonArray(); static const int DEFAULT_GROW_SIZE = 100; }; From 73ad8542351b48683c1999e021c8696edee772e3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 28 Jun 2013 22:05:32 -0700 Subject: [PATCH 05/55] latest version of quadtree coverage map --- interface/src/Application.cpp | 140 +++++++++ interface/src/Application.h | 13 + interface/src/VoxelSystem.cpp | 154 +++++++++- interface/src/VoxelSystem.h | 12 +- libraries/voxels/src/CoverageMap.cpp | 80 ++++- libraries/voxels/src/CoverageMap.h | 7 + libraries/voxels/src/GeometryUtil.cpp | 10 +- libraries/voxels/src/ViewFrustum.cpp | 55 +++- .../voxels/src/VoxelProjectedPolygon.cpp | 286 +++++++++++++++++- libraries/voxels/src/VoxelProjectedPolygon.h | 27 +- 10 files changed, 742 insertions(+), 42 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 34aef00950..bf3dabaf98 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1074,6 +1074,10 @@ void Application::doFalseColorizeOccluded() { _voxels.falseColorizeOccluded(); } +void Application::doFalseColorizeOccludedV2() { + _voxels.falseColorizeOccludedV2(); +} + void Application::doTrueVoxelColors() { _voxels.trueColorize(); } @@ -1438,6 +1442,7 @@ void Application::initMenu() { renderDebugMenu->addAction("FALSE Color Voxels by Distance", this, SLOT(doFalseColorizeByDistance())); renderDebugMenu->addAction("FALSE Color Voxel Out of View", this, SLOT(doFalseColorizeInView())); renderDebugMenu->addAction("FALSE Color Occluded Voxels", this, SLOT(doFalseColorizeOccluded()), Qt::CTRL | Qt::Key_O); + renderDebugMenu->addAction("FALSE Color Occluded V2 Voxels", this, SLOT(doFalseColorizeOccludedV2()), Qt::CTRL | Qt::Key_P); renderDebugMenu->addAction("Show TRUE Colors", this, SLOT(doTrueVoxelColors()), Qt::CTRL | Qt::Key_T); debugMenu->addAction("Wants Res-In", this, SLOT(setWantsResIn(bool)))->setCheckable(true); @@ -1446,6 +1451,12 @@ void Application::initMenu() { (_shouldLowPassFilter = debugMenu->addAction("Test: LowPass filter"))->setCheckable(true); debugMenu->addAction("Wants Occlusion Culling", this, SLOT(setWantsOcclusionCulling(bool)))->setCheckable(true); + (_renderCoverageMap = debugMenu->addAction("Render Coverage Map"))->setCheckable(true); + _renderCoverageMap->setShortcut(Qt::SHIFT | Qt::CTRL | Qt::Key_O); + (_renderCoverageMapV2 = debugMenu->addAction("Render Coverage Map V2"))->setCheckable(true); + _renderCoverageMapV2->setShortcut(Qt::SHIFT | Qt::CTRL | Qt::Key_P); + + QMenu* settingsMenu = menuBar->addMenu("Settings"); (_settingsAutosave = settingsMenu->addAction("Autosave"))->setCheckable(true); _settingsAutosave->setChecked(true); @@ -2180,6 +2191,7 @@ void Application::displaySide(Camera& whichCamera) { // brad's frustum for debugging if (_frustumOn->isChecked()) renderViewFrustum(_viewFrustum); + } void Application::displayOverlay() { @@ -2226,6 +2238,11 @@ void Application::displayOverlay() { glPointSize(1.0f); if (_renderStatsOn->isChecked()) { displayStats(); } + + // testing rendering coverage map + if (_renderCoverageMapV2->isChecked()) { renderCoverageMapV2(); } + if (_renderCoverageMap->isChecked()) { renderCoverageMap(); } + if (_logOn->isChecked()) { LogDisplay::instance.render(_glWidget->width(), _glWidget->height()); } // Show chat entry field @@ -2334,6 +2351,10 @@ void Application::displayStats() { } delete []perfStatLinesArray; // we're responsible for cleanup } + + + + } void Application::renderThrustAtVoxel(const glm::vec3& thrust) { @@ -2362,6 +2383,125 @@ void Application::renderLineToTouchedVoxel() { } } + +glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { + float horizontalScale = _glWidget->width() / 2.0f; + float verticalScale = _glWidget->height() / 2.0f; + + // -1,-1 is 0,windowHeight + // 1,1 is windowWidth,0 + + // -1,1 1,1 + // +-----------------------+ + // | | | + // | | | + // | -1,0 | | + // |-----------+-----------| + // | 0,0 | + // | | | + // | | | + // | | | + // +-----------------------+ + // -1,-1 1,-1 + + glm::vec2 screenPoint((projectedPoint.x + 1.0) * horizontalScale, + ((projectedPoint.y + 1.0) * -verticalScale) + _glWidget->height()); + + return screenPoint; +} + +// render the coverage map on screen +void Application::renderCoverageMapV2() { + + //printLog("renderCoverageMap()\n"); + + glDisable(GL_LIGHTING); + glLineWidth(2.0); + glBegin(GL_LINES); + glColor3f(0,1,1); + + renderCoverageMapsV2Recursively(&_voxels.myCoverageMapV2); + + glEnd(); + glEnable(GL_LIGHTING); +} + +void Application::renderCoverageMapsV2Recursively(CoverageMapV2* map) { + // render ourselves... + if (map->isCovered()) { + BoundingBox box = map->getBoundingBox(); + + glm::vec2 firstPoint = getScaledScreenPoint(box.getVertex(0)); + glm::vec2 lastPoint(firstPoint); + + for (int i = 1; i < box.getVertexCount(); i++) { + glm::vec2 thisPoint = getScaledScreenPoint(box.getVertex(i)); + + glVertex2f(lastPoint.x, lastPoint.y); + glVertex2f(thisPoint.x, thisPoint.y); + lastPoint = thisPoint; + } + + glVertex2f(lastPoint.x, lastPoint.y); + glVertex2f(firstPoint.x, firstPoint.y); + } else { + // iterate our children and call render on them. + for (int i = 0; i < CoverageMapV2::NUMBER_OF_CHILDREN; i++) { + CoverageMapV2* childMap = map->getChild(i); + if (childMap) { + renderCoverageMapsV2Recursively(childMap); + } + } + } +} + +// render the coverage map on screen +void Application::renderCoverageMap() { + + //printLog("renderCoverageMap()\n"); + + glDisable(GL_LIGHTING); + glLineWidth(2.0); + glBegin(GL_LINES); + glColor3f(0,0,1); + + renderCoverageMapsRecursively(&_voxels.myCoverageMap); + + glEnd(); + glEnable(GL_LIGHTING); +} + +void Application::renderCoverageMapsRecursively(CoverageMap* map) { + for (int i = 0; i < map->getPolygonCount(); i++) { + + VoxelProjectedPolygon* polygon = map->getPolygon(i); + + glm::vec2 firstPoint = getScaledScreenPoint(polygon->getVertex(0)); + glm::vec2 lastPoint(firstPoint); + + for (int i = 1; i < polygon->getVertexCount(); i++) { + glm::vec2 thisPoint = getScaledScreenPoint(polygon->getVertex(i)); + + glVertex2f(lastPoint.x, lastPoint.y); + glVertex2f(thisPoint.x, thisPoint.y); + lastPoint = thisPoint; + } + + glVertex2f(lastPoint.x, lastPoint.y); + glVertex2f(firstPoint.x, firstPoint.y); + } + + // iterate our children and call render on them. + for (int i = 0; i < CoverageMapV2::NUMBER_OF_CHILDREN; i++) { + CoverageMap* childMap = map->getChild(i); + if (childMap) { + renderCoverageMapsRecursively(childMap); + } + } +} + + + ///////////////////////////////////////////////////////////////////////////////////// // renderViewFrustum() // diff --git a/interface/src/Application.h b/interface/src/Application.h index f84ff6c20a..f92c4458ab 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -120,6 +120,7 @@ private slots: void doFalseRandomizeEveryOtherVoxelColors(); void doFalseColorizeByDistance(); void doFalseColorizeOccluded(); + void doFalseColorizeOccludedV2(); void doFalseColorizeInView(); void doTrueVoxelColors(); void doTreeStats(); @@ -141,6 +142,15 @@ private slots: void copyVoxels(); void pasteVoxels(); void runTests(); + + void renderCoverageMap(); + void renderCoverageMapsRecursively(CoverageMap* map); + + void renderCoverageMapV2(); + void renderCoverageMapsV2Recursively(CoverageMapV2* map); + + glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint); + private: static bool sendVoxelsOperation(VoxelNode* node, void* extraData); @@ -222,6 +232,9 @@ private: QAction* _fullScreenMode; // whether we are in full screen mode QAction* _frustumRenderModeAction; QAction* _settingsAutosave; // Whether settings are saved automatically + + QAction* _renderCoverageMapV2; + QAction* _renderCoverageMap; SerialInterface _serialHeadSensor; QNetworkAccessManager* _networkAccessManager; diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index bdd109aa97..430bb782e5 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -23,6 +23,7 @@ #include "Log.h" #include "VoxelConstants.h" #include "CoverageMap.h" +#include "CoverageMapV2.h" #include "InterfaceConfig.h" #include "renderer/ProgramObject.h" @@ -1160,6 +1161,7 @@ void VoxelSystem::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* dest struct FalseColorizeOccludedArgs { ViewFrustum* viewFrustum; CoverageMap* map; + CoverageMapV2* mapV2; VoxelTree* tree; long totalVoxels; long coloredVoxels; @@ -1179,9 +1181,11 @@ struct FalseColorizeSubTreeOperationArgs { }; bool VoxelSystem::falseColorizeSubTreeOperation(VoxelNode* node, void* extraData) { - FalseColorizeSubTreeOperationArgs* args = (FalseColorizeSubTreeOperationArgs*) extraData; - node->setFalseColor(args->color[0], args->color[1], args->color[2]); - args->voxelsTouched++; + if (node->getShouldRender()) { + FalseColorizeSubTreeOperationArgs* args = (FalseColorizeSubTreeOperationArgs*) extraData; + node->setFalseColor(args->color[0], args->color[1], args->color[2]); + args->voxelsTouched++; + } return true; } @@ -1261,12 +1265,141 @@ bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraDat } return true; // keep going! } + void VoxelSystem::falseColorizeOccluded() { PerformanceWarning warn(true, "falseColorizeOccluded()",true); - CoverageMap map; + myCoverageMap.erase(); + FalseColorizeOccludedArgs args; args.viewFrustum = Application::getInstance()->getViewFrustum(); - args.map = ↦ + args.map = &myCoverageMap; + args.totalVoxels = 0; + args.coloredVoxels = 0; + args.occludedVoxels = 0; + args.notOccludedVoxels = 0; + args.outOfView = 0; + args.subtreeVoxelsSkipped = 0; + args.nonLeaves = 0; + args.stagedForDeletion = 0; + args.nonLeavesOutOfView = 0; + args.nonLeavesOccluded = 0; + args.tree = _tree; + + VoxelProjectedPolygon::pointInside_calls = 0; + VoxelProjectedPolygon::occludes_calls = 0; + + glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE); + + _tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, position, (void*)&args); + + printLog("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n", + position.x, position.y, + args.totalVoxels, args.coloredVoxels, args.occludedVoxels, + args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped, + args.stagedForDeletion, + args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded, + VoxelProjectedPolygon::pointInside_calls, + VoxelProjectedPolygon::occludes_calls + ); + + + myCoverageMap.erase(); + + setupNewVoxelsForDrawing(); +} + +bool VoxelSystem::falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData) { + + FalseColorizeOccludedArgs* args = (FalseColorizeOccludedArgs*) extraData; + args->totalVoxels++; + + // if this node is staged for deletion, then just return + if (node->isStagedForDeletion()) { + args->stagedForDeletion++; + return true; + } + + // If we are a parent, let's see if we're completely occluded. + if (!node->isLeaf()) { + args->nonLeaves++; + + AABox voxelBox = node->getAABox(); + voxelBox.scale(TREE_SCALE); + VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(args->viewFrustum->getProjectedPolygon(voxelBox)); + + // If we're not all in view, then ignore it, and just return. But keep searching... + if (!voxelPolygon->getAllInView()) { + args->nonLeavesOutOfView++; + delete voxelPolygon; + return true; + } + + CoverageMapV2StorageResult result = args->mapV2->checkMap(voxelPolygon, false); + if (result == V2_OCCLUDED) { + args->nonLeavesOccluded++; + delete voxelPolygon; + + FalseColorizeSubTreeOperationArgs subArgs; + subArgs.color[0] = 0; + subArgs.color[1] = 255; + subArgs.color[2] = 0; + subArgs.voxelsTouched = 0; + + args->tree->recurseNodeWithOperation(node, falseColorizeSubTreeOperation, &subArgs ); + + args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1); + args->totalVoxels += (subArgs.voxelsTouched - 1); + + return false; + } + + delete voxelPolygon; + return true; // keep looking... + } + + if (node->isLeaf() && node->isColored() && node->getShouldRender()) { + args->coloredVoxels++; + + AABox voxelBox = node->getAABox(); + voxelBox.scale(TREE_SCALE); + VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(args->viewFrustum->getProjectedPolygon(voxelBox)); + + // If we're not all in view, then ignore it, and just return. But keep searching... + if (!voxelPolygon->getAllInView()) { + args->outOfView++; + delete voxelPolygon; + return true; + } + + CoverageMapV2StorageResult result = args->mapV2->checkMap(voxelPolygon, true); + if (result == V2_OCCLUDED) { + node->setFalseColor(255, 0, 0); + args->occludedVoxels++; + } else if (result == V2_STORED) { + args->notOccludedVoxels++; + //printLog("***** falseColorizeOccludedOperation() NODE is STORED *****\n"); + } else if (result == V2_DOESNT_FIT) { + //printLog("***** falseColorizeOccludedOperation() NODE DOESNT_FIT???? *****\n"); + } + delete voxelPolygon; // V2 maps don't store polygons, so we're always in charge of freeing + } + return true; // keep going! +} + + +void VoxelSystem::falseColorizeOccludedV2() { + PerformanceWarning warn(true, "falseColorizeOccludedV2()",true); + myCoverageMapV2.erase(); + + CoverageMapV2::wantDebugging = true; + + VoxelProjectedPolygon::pointInside_calls = 0; + VoxelProjectedPolygon::occludes_calls = 0; + + + FalseColorizeOccludedArgs args; + args.viewFrustum = Application::getInstance()->getViewFrustum(); + args.mapV2 = &myCoverageMapV2; args.totalVoxels = 0; args.coloredVoxels = 0; args.occludedVoxels = 0; @@ -1281,16 +1414,21 @@ void VoxelSystem::falseColorizeOccluded() { glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE); - _tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, position, (void*)&args); + _tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedV2Operation, position, (void*)&args); - printLog("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n", + printLog("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n", position.x, position.y, args.totalVoxels, args.coloredVoxels, args.occludedVoxels, args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped, args.stagedForDeletion, - args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded); + args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded, + VoxelProjectedPolygon::pointInside_calls, + VoxelProjectedPolygon::occludes_calls + ); + myCoverageMapV2.erase(); setupNewVoxelsForDrawing(); } + diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 5087308392..1a71b90b1e 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -11,11 +11,15 @@ #include "InterfaceConfig.h" #include + #include #include #include -#include + +#include #include +#include + #include "Camera.h" #include "Util.h" #include "world.h" @@ -57,6 +61,7 @@ public: void falseColorizeDistanceFromView(ViewFrustum* viewFrustum); void falseColorizeRandomEveryOther(); void falseColorizeOccluded(); + void falseColorizeOccludedV2(); void killLocalVoxels(); void setRenderPipelineWarnings(bool on) { _renderWarningsOn = on; }; @@ -84,6 +89,9 @@ public: void copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinationTree, bool rebaseToRoot); void copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destinationNode); + + CoverageMapV2 myCoverageMapV2; + CoverageMap myCoverageMap; protected: float _treeScale; @@ -123,6 +131,8 @@ private: static bool collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData); static bool falseColorizeOccludedOperation(VoxelNode* node, void* extraData); static bool falseColorizeSubTreeOperation(VoxelNode* node, void* extraData); + static bool falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData); + int updateNodeInArraysAsFullVBO(VoxelNode* node); int updateNodeInArraysAsPartialVBO(VoxelNode* node); diff --git a/libraries/voxels/src/CoverageMap.cpp b/libraries/voxels/src/CoverageMap.cpp index db2976a17f..92533786b8 100644 --- a/libraries/voxels/src/CoverageMap.cpp +++ b/libraries/voxels/src/CoverageMap.cpp @@ -131,17 +131,60 @@ BoundingBox CoverageMap::getChildBoundingBox(int childIndex) { return result; } +int CoverageMap::getPolygonCount() const { + return (_topHalf.getPolygonCount() + + _bottomHalf.getPolygonCount() + + _leftHalf.getPolygonCount() + + _rightHalf.getPolygonCount() + + _remainder.getPolygonCount()); +} + +VoxelProjectedPolygon* CoverageMap::getPolygon(int index) const { + int base = 0; + if ((index - base) < _topHalf.getPolygonCount()) { + return _topHalf.getPolygon((index - base)); + } + base += _topHalf.getPolygonCount(); + + if ((index - base) < _bottomHalf.getPolygonCount()) { + return _bottomHalf.getPolygon((index - base)); + } + base += _bottomHalf.getPolygonCount(); + + if ((index - base) < _leftHalf.getPolygonCount()) { + return _leftHalf.getPolygon((index - base)); + } + base += _leftHalf.getPolygonCount(); + + if ((index - base) < _rightHalf.getPolygonCount()) { + return _rightHalf.getPolygon((index - base)); + } + base += _rightHalf.getPolygonCount(); + + if ((index - base) < _remainder.getPolygonCount()) { + return _remainder.getPolygon((index - base)); + } + return NULL; +} + + + // possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, bool storeIt) { if (_isRoot) { _checkMapRootCalls++; + + //printLog("CoverageMap::checkMap()... storeIt=%s\n", debug::valueOf(storeIt)); + //polygon->printDebugDetails(); + } // short circuit: we don't handle polygons that aren't all in view, so, if the polygon in question is // not in view, then we just discard it with a DOESNT_FIT, this saves us time checking values later. if (!polygon->getAllInView()) { _notAllInView++; + //printLog("CoverageMap2::checkMap()... V2_OCCLUDED\n"); return DOESNT_FIT; } @@ -182,6 +225,14 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b // It's possible that this first set of checks might have resulted in an out of order polygon // in which case we just return.. if (result == STORED || result == OCCLUDED) { + + /* + if (result == STORED) + printLog("CoverageMap2::checkMap()... STORED\n"); + else + printLog("CoverageMap2::checkMap()... OCCLUDED\n"); + */ + return result; } @@ -195,7 +246,26 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b if (!_childMaps[i]) { _childMaps[i] = new CoverageMap(childMapBoundingBox, NOT_ROOT, _managePolygons); } - return _childMaps[i]->checkMap(polygon, storeIt); + result = _childMaps[i]->checkMap(polygon, storeIt); + + /* + switch (result) { + case STORED: + printLog("checkMap() = STORED\n"); + break; + case NOT_STORED: + printLog("checkMap() = NOT_STORED\n"); + break; + case OCCLUDED: + printLog("checkMap() = OCCLUDED\n"); + break; + default: + printLog("checkMap() = ????? \n"); + break; + } + */ + + return result; } } // if we got this far, then the polygon is in our bounding box, but doesn't fit in @@ -204,15 +274,19 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) { //printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area()); storeIn->storeInArray(polygon); + //printLog("CoverageMap2::checkMap()... STORED\n"); return STORED; } else { CoverageRegion::_tooSmallSkips++; + //printLog("CoverageMap2::checkMap()... NOT_STORED\n"); return NOT_STORED; } } else { + //printLog("CoverageMap2::checkMap()... NOT_STORED\n"); return NOT_STORED; } } + //printLog("CoverageMap2::checkMap()... DOESNT_FIT\n"); return DOESNT_FIT; } @@ -241,7 +315,7 @@ void CoverageRegion::init() { void CoverageRegion::erase() { -/**/ +/** if (_polygonCount) { printLog("CoverageRegion::erase()...\n"); printLog("_polygonCount=%d\n",_polygonCount); @@ -347,7 +421,7 @@ void CoverageRegion::storeInArray(VoxelProjectedPolygon* polygon) { const int IGNORED = NULL; float area = polygon->getBoundingBox().area(); float reverseArea = 4.0f - area; -printLog("store by size area=%f reverse area=%f\n", area, reverseArea); + //printLog("store by size area=%f reverse area=%f\n", area, reverseArea); _polygonCount = insertIntoSortedArrays((void*)polygon, reverseArea, IGNORED, (void**)_polygons, _polygonSizes, IGNORED, _polygonCount, _polygonArraySize); diff --git a/libraries/voxels/src/CoverageMap.h b/libraries/voxels/src/CoverageMap.h index 083043fd14..7fee7cb6a5 100644 --- a/libraries/voxels/src/CoverageMap.h +++ b/libraries/voxels/src/CoverageMap.h @@ -38,6 +38,9 @@ public: const char* getRegionName() const; + + int getPolygonCount() const { return _polygonCount; }; + VoxelProjectedPolygon* getPolygon(int index) const { return _polygons[index]; }; private: void init(); @@ -78,6 +81,10 @@ public: static bool wantDebugging; + int getPolygonCount() const; + VoxelProjectedPolygon* getPolygon(int index) const; + CoverageMap* getChild(int childIndex) const { return _childMaps[childIndex]; }; + private: void init(); diff --git a/libraries/voxels/src/GeometryUtil.cpp b/libraries/voxels/src/GeometryUtil.cpp index af2a6dfc95..69b566f857 100644 --- a/libraries/voxels/src/GeometryUtil.cpp +++ b/libraries/voxels/src/GeometryUtil.cpp @@ -127,11 +127,17 @@ bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm int d3 = computeDirection(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p1.x, r2p1.y); int d4 = computeDirection(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p2.x, r2p2.y); return (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && - ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) || + ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) + + // I think these following conditions are what handle the case of one end point + // being exactly on the second line. It seems like we sometimes want this to be + // considered "inside" and other times, maybe we don't... + || (d1 == 0 && isOnSegment(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p1.x, r1p1.y)) || (d2 == 0 && isOnSegment(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p2.x, r1p2.y)) || (d3 == 0 && isOnSegment(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p1.x, r2p1.y)) || - (d4 == 0 && isOnSegment(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p2.x, r2p2.y)); + (d4 == 0 && isOnSegment(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p2.x, r2p2.y)) + ; } bool isOnSegment(float xi, float yi, float xj, float yj, float xk, float yk) { diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index 36606e6702..cb6c0b219d 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -457,14 +457,20 @@ const int MAX_POSSIBLE_COMBINATIONS = 43; const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_PROJECTED_POLYGON_VERTEX_COUNT+1] = { // Number of vertices in shadow polygon for the visible faces, then a list of the index of each vertice from the AABox + +//0 {0}, // inside {4, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR}, // right - {4, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // left + {4, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR }, // left {0}, // n/a - {4, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // bottom - {6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR},//bottom, right - {6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR},//bottom, left + +//4 + {4, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR}, // bottom +//5 + {6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR },//bottom, right + {6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, },//bottom, left {0}, // n/a +//8 {4, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // top {6, TOP_RIGHT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // top, right {6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // top, left @@ -473,13 +479,17 @@ const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_PROJECTED_POLYGON_VERT {0}, // n/a {0}, // n/a {0}, // n/a +//16 {4, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front or near {6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, right {6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // front, left {0}, // n/a - {6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, // front,bottom - {6, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR}, //front,bottom,right - {6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, //front,bottom,left +//20 + {6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR }, // front,bottom +//21 + {6, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, //front,bottom,right +//22 + {6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR }, //front,bottom,left {0}, // n/a {6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, top {6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, top, right @@ -489,16 +499,27 @@ const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_PROJECTED_POLYGON_VERT {0}, // n/a {0}, // n/a {0}, // n/a - {4, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR}, // back - {6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR}, // back, right - {6, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR}, // back, left +//32 + {4, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR }, // back + {6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR}, // back, right +//34 + {6, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, TOP_RIGHT_FAR }, // back, left + + {0}, // n/a - {6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // back, bottom - {6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR},//back, bottom, right - {6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR},//back, bottom, left +//36 + {6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR}, // back, bottom + {6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR},//back, bottom, right + +// 38 + {6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR },//back, bottom, left {0}, // n/a - {6, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR}, // back, top + +// 40 + {6, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR}, // back, top + {6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, // back, top, right +//42 {6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // back, top, left }; @@ -512,6 +533,8 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { + ((_position.z < bottomNearRight.z) << 4) // 16 = front/near | the 6 defining + ((_position.z > topFarLeft.z ) << 5); // 32 = back/far | planes + //printLog(">>>>>>>>> ViewFrustum::getProjectedPolygon() lookup=%d\n",lookUp); + int vertexCount = hullVertexLookup[lookUp][0]; //look up number of vertices VoxelProjectedPolygon projectedPolygon(vertexCount); @@ -530,7 +553,7 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { projectedPolygon.setVertex(i, projectedPoint); } - /*** + /***/ // Now that we've got the polygon, if it extends beyond the clipping window, then let's clip it // NOTE: This clipping does not improve our overall performance. It basically causes more polygons to // end up in the same quad/half and so the polygon lists get longer, and that's more calls to polygon.occludes() @@ -552,7 +575,7 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { } delete[] clippedVertices; } - ***/ + /***/ } // set the distance from our camera position, to the closest vertex float distance = glm::distance(getPosition(), box.getCenter()); diff --git a/libraries/voxels/src/VoxelProjectedPolygon.cpp b/libraries/voxels/src/VoxelProjectedPolygon.cpp index dbebb298ad..d4c1559612 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.cpp +++ b/libraries/voxels/src/VoxelProjectedPolygon.cpp @@ -11,6 +11,20 @@ #include "Log.h" #include "SharedUtil.h" +glm::vec2 BoundingBox::getVertex(int vertexNumber) const { + switch (vertexNumber) { + case BoundingBox::BOTTOM_LEFT: + return corner; + case BoundingBox::TOP_LEFT: + return glm::vec2(corner.x, corner.y + size.y); + case BoundingBox::BOTTOM_RIGHT: + return glm::vec2(corner.x + size.x, corner.y); + case BoundingBox::TOP_RIGHT: + return corner + size; + } + assert(false); // not allowed + return glm::vec2(0,0); +} BoundingBox BoundingBox::topHalf() const { float halfY = size.y/2.0f; @@ -45,6 +59,15 @@ bool BoundingBox::contains(const BoundingBox& box) const { ); }; +bool BoundingBox::contains(const glm::vec2& point) const { + return ( _set && + (point.x > corner.x) && + (point.y > corner.y) && + (point.x < corner.x + size.x) && + (point.y < corner.y + size.y) + ); +}; + void BoundingBox::explandToInclude(const BoundingBox& box) { if (!_set) { corner = box.corner; @@ -74,6 +97,21 @@ void BoundingBox::printDebugDetails(const char* label) const { } +long VoxelProjectedPolygon::pointInside_calls = 0; +long VoxelProjectedPolygon::occludes_calls = 0; + + +VoxelProjectedPolygon::VoxelProjectedPolygon(const BoundingBox& box) : + _vertexCount(4), + _maxX(-FLT_MAX), _maxY(-FLT_MAX), _minX(FLT_MAX), _minY(FLT_MAX), + _distance(0) +{ + for (int i = 0; i < _vertexCount; i++) { + setVertex(i, box.getVertex(i)); + } +} + + void VoxelProjectedPolygon::setVertex(int vertex, const glm::vec2& point) { _vertices[vertex] = point; @@ -93,7 +131,10 @@ void VoxelProjectedPolygon::setVertex(int vertex, const glm::vec2& point) { }; +// can be optimized with new pointInside() bool VoxelProjectedPolygon::occludes(const VoxelProjectedPolygon& occludee, bool checkAllInView) const { + + VoxelProjectedPolygon::occludes_calls++; // if we are completely out of view, then we definitely don't occlude! // if the occludee is completely out of view, then we also don't occlude it @@ -113,23 +154,156 @@ bool VoxelProjectedPolygon::occludes(const VoxelProjectedPolygon& occludee, bool return false; } + // we need to test for identity as well, because in the case of identity, none of the points + // will be "inside" but we don't want to bail early on the first non-inside point + bool potentialIdenity = false; + if ((occludee.getVertexCount() == getVertexCount()) && (getBoundingBox().contains(occludee.getBoundingBox())) ) { + potentialIdenity = true; + } // if we got this far, then check each vertex of the occludee, if all those points // are inside our polygon, then the tested occludee is fully occluded + int pointsInside = 0; for(int i = 0; i < occludee.getVertexCount(); i++) { - if (!pointInside(occludee.getVertex(i))) { - return false; + bool vertexMatched = false; + if (!pointInside(occludee.getVertex(i), &vertexMatched)) { + + // so the point we just tested isn't inside, but it might have matched a vertex + // if it didn't match a vertext, then we bail because we can't be an identity + // or if we're not expecting identity, then we also bail early, no matter what + if (!potentialIdenity || !vertexMatched) { + return false; + } + } else { + pointsInside++; } } - // If we are a concave polygon, then it's not good enough to know that all of the occludee's points are - // inside us, we also need to make sure than none of the occludee's edges intersect any of our edges. + // we're only here if all points are inside matched and/or we had a potentialIdentity we need to check + if (pointsInside == occludee.getVertexCount()) { + return true; + } + + // If we have the potential for identity, then test to see if we match, if we match, we occlude + if (potentialIdenity) { + return matches(occludee); + } - // if we got this far, then indeed the occludee is fully occluded by us - return true; + return false; // if we got this far, then we're not occluded } -bool VoxelProjectedPolygon::pointInside(const glm::vec2& point) const { - // first check the bounding boxes, the point must be fully within the boounding box of this shadow +bool VoxelProjectedPolygon::occludes(const BoundingBox& occludee) const { + + VoxelProjectedPolygon::occludes_calls++; + + // first check the bounding boxes, the occludee must be fully within the boounding box of this shadow + if ((occludee.getMaxX() > getMaxX()) || + (occludee.getMaxY() > getMaxY()) || + (occludee.getMinX() < getMinX()) || + (occludee.getMinY() < getMinY())) { + return false; + } + + // we need to test for identity as well, because in the case of identity, none of the points + // will be "inside" but we don't want to bail early on the first non-inside point + bool potentialIdenity = false; + + if ((occludee.getVertexCount() == getVertexCount()) && (getBoundingBox().contains(occludee)) ) { + potentialIdenity = true; + } + // if we got this far, then check each vertex of the occludee, if all those points + // are inside our polygon, then the tested occludee is fully occluded + int pointsInside = 0; + for(int i = 0; i < occludee.getVertexCount(); i++) { + bool vertexMatched = false; + if (!pointInside(occludee.getVertex(i), &vertexMatched)) { + + // so the point we just tested isn't inside, but it might have matched a vertex + // if it didn't match a vertext, then we bail because we can't be an identity + // or if we're not expecting identity, then we also bail early, no matter what + if (!potentialIdenity || !vertexMatched) { + return false; + } + } else { + pointsInside++; + } + } + + // we're only here if all points are inside matched and/or we had a potentialIdentity we need to check + if (pointsInside == occludee.getVertexCount()) { + return true; + } + + // If we have the potential for identity, then test to see if we match, if we match, we occlude + if (potentialIdenity) { + return matches(occludee); + } + + return false; // if we got this far, then we're not occluded +} + +bool VoxelProjectedPolygon::matches(const VoxelProjectedPolygon& testee) const { + if (testee.getVertexCount() != getVertexCount()) { + return false; + } + int vertextCount = getVertexCount(); + // find which testee vertex matches our first polygon vertex. + glm::vec2 polygonVertex = getVertex(0); + int originIndex = 0; + for(int i = 0; i < vertextCount; i++) { + glm::vec2 testeeVertex = testee.getVertex(i); + + // if they match, we found our origin. + if (testeeVertex == polygonVertex) { + originIndex = i; + break; + } + } + // Now, starting at the originIndex, walk the vertices of both the testee and ourselves + + for(int i = 0; i < vertextCount; i++) { + glm::vec2 testeeVertex = testee.getVertex((i + originIndex) % vertextCount); + glm::vec2 polygonVertex = getVertex(i); + if (testeeVertex != polygonVertex) { + return false; // we don't match, therefore we're not the same + } + } + return true; // all of our vertices match, therefore we're the same +} + +bool VoxelProjectedPolygon::matches(const BoundingBox& testee) const { + if (testee.getVertexCount() != getVertexCount()) { + return false; + } + int vertextCount = getVertexCount(); + // find which testee vertex matches our first polygon vertex. + glm::vec2 polygonVertex = getVertex(0); + int originIndex = 0; + for(int i = 0; i < vertextCount; i++) { + glm::vec2 testeeVertex = testee.getVertex(i); + + // if they match, we found our origin. + if (testeeVertex == polygonVertex) { + originIndex = i; + break; + } + } + // Now, starting at the originIndex, walk the vertices of both the testee and ourselves + + for(int i = 0; i < vertextCount; i++) { + glm::vec2 testeeVertex = testee.getVertex((i + originIndex) % vertextCount); + glm::vec2 polygonVertex = getVertex(i); + if (testeeVertex != polygonVertex) { + return false; // we don't match, therefore we're not the same + } + } + return true; // all of our vertices match, therefore we're the same +} + +bool VoxelProjectedPolygon::pointInside(const glm::vec2& point, bool* matchesVertex) const { + + VoxelProjectedPolygon::pointInside_calls++; + + // first check the bounding boxes, the point must be fully within the boounding box of this polygon if ((point.x > getMaxX()) || (point.y > getMaxY()) || (point.x < getMinX()) || @@ -137,10 +311,37 @@ bool VoxelProjectedPolygon::pointInside(const glm::vec2& point) const { return false; } - float e = (getMaxX() - getMinX()) / 100.0f; // some epsilon + // consider each edge of this polygon as a potential separating axis + // check the point against each edge + for (int i = 0; i < getVertexCount(); i++) { + glm::vec2 start = getVertex(i); + glm::vec2 end = getVertex((i + 1) % getVertexCount()); + float a = start.y - end.y; + float b = end.x - start.x; + float c = a * start.x + b * start.y; + if (a * point.x + b * point.y < c) { + return false; + } + } + + return true; +} + +/* +bool VoxelProjectedPolygon::pointInside(const glm::vec2& point, bool* matchesVertex) const { + // first check the bounding boxes, the point must be fully within the boounding box of this shadow + if ((point.x > getMaxX()) || + (point.y > getMaxY()) || + (point.x < getMinX()) || + (point.y < getMinY())) { + return false; + } + + //float e = (getMaxX() - getMinX()) / 100.0f; // some epsilon + float e = 1.0f; // some epsilon // We need to have one ray that goes from a known outside position to the point in question. We'll pick a - // start point just outside of our min X + // start point outside of our min X, min Y glm::vec2 r1p1(getMinX() - e, point.y); glm::vec2 r1p2(point); @@ -149,17 +350,30 @@ bool VoxelProjectedPolygon::pointInside(const glm::vec2& point) const { // Test the ray against all sides int intersections = 0; + bool someVertexMatches = false; for (int i = 0; i < getVertexCount(); i++) { r2p2 = getVertex(i); + + // if the point in question matches one of our vetices, then we consider it to NOT be inside. + if (point.x == r2p2.x && point.y == r2p2.y) { + // caller wants to know if we match any of the vertices + if (matchesVertex) { + *matchesVertex = true; + } + return false; + } + + // if we're still processing, then check for intersections if (doLineSegmentsIntersect(r1p1, r1p2, r2p1, r2p2)) { intersections++; } r2p1 = r2p2; // set up for next side } - // If odd number of intersections, we're inside + // If odd number of intersections and we got here, we're inside return ((intersections & 1) == 1); } +*/ void VoxelProjectedPolygon::printDebugDetails() const { printf("VoxelProjectedPolygon..."); @@ -171,4 +385,54 @@ void VoxelProjectedPolygon::printDebugDetails() const { } } +bool VoxelProjectedPolygon::intersects(const BoundingBox& box) const { + VoxelProjectedPolygon testee(box); + return intersectsOnAxes(testee) && testee.intersectsOnAxes(*this); +} + + +bool VoxelProjectedPolygon::intersects(const VoxelProjectedPolygon& testee) const { + return intersectsOnAxes(testee) && testee.intersectsOnAxes(*this); +} + +// +// Tests the edges of this polygon as potential separating axes for this polygon and the +// specified other. +// +// @return false if the polygons are disjoint on any of this polygon's axes, true if they +// intersect on all axes. +// +// Note: this only works on +// +// +bool VoxelProjectedPolygon::intersectsOnAxes(const VoxelProjectedPolygon& testee) const { + + // consider each edge of this polygon as a potential separating axis + for (int i = 0; i < getVertexCount(); i++) { + glm::vec2 start = getVertex(i); + glm::vec2 end = getVertex((i + 1) % getVertexCount()); + float a = start.y - end.y; + float b = end.x - start.x; + float c = a * start.x + b * start.y; + + // if all vertices fall outside the edge, the polygons are disjoint + // points that are ON the edge, are considered to be "outside" + for (int j = 0; j < testee.getVertexCount(); j++) { + glm::vec2 testeeVertex = testee.getVertex(j); + + // in comparison below: + // >= will cause points on edge to be considered inside + // > will cause points on edge to be considered outside + + float c2 = a * testeeVertex.x + b * testeeVertex.y; + if (c2 >= c) { + goto CONTINUE_OUTER; + } + } + return false; + CONTINUE_OUTER: ; + } + return true; +} + diff --git a/libraries/voxels/src/VoxelProjectedPolygon.h b/libraries/voxels/src/VoxelProjectedPolygon.h index 2a4b1092a6..0681ce4fc1 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.h +++ b/libraries/voxels/src/VoxelProjectedPolygon.h @@ -17,19 +17,32 @@ typedef glm::vec2 ProjectedVertices[MAX_CLIPPED_PROJECTED_POLYGON_VERTEX_COUNT]; class BoundingBox { public: + enum { BOTTOM_LEFT, BOTTOM_RIGHT, TOP_RIGHT, TOP_LEFT, VERTEX_COUNT }; + BoundingBox(glm::vec2 corner, glm::vec2 size) : corner(corner), size(size), _set(true) {}; BoundingBox() : _set(false) {}; glm::vec2 corner; glm::vec2 size; bool contains(const BoundingBox& box) const; + bool contains(const glm::vec2& point) const; + bool pointInside(const glm::vec2& point) const { return contains(point); }; + void explandToInclude(const BoundingBox& box); float area() const { return size.x * size.y; }; + int getVertexCount() const { return VERTEX_COUNT; }; + glm::vec2 getVertex(int vertexNumber) const; + BoundingBox topHalf() const; BoundingBox bottomHalf() const; BoundingBox leftHalf() const; BoundingBox rightHalf() const; + + float getMaxX() const { return corner.x + size.x; } + float getMaxY() const { return corner.y + size.y; } + float getMinX() const { return corner.x; } + float getMinY() const { return corner.y; } void printDebugDetails(const char* label=NULL) const; private: @@ -39,6 +52,8 @@ private: class VoxelProjectedPolygon { public: + VoxelProjectedPolygon(const BoundingBox& box); + VoxelProjectedPolygon(int vertexCount = 0) : _vertexCount(vertexCount), _maxX(-FLT_MAX), _maxY(-FLT_MAX), _minX(FLT_MAX), _minY(FLT_MAX), @@ -60,8 +75,15 @@ public: bool getAllInView() const { return _allInView; }; void setAllInView(bool allInView) { _allInView = allInView; }; + bool pointInside(const glm::vec2& point, bool* matchesVertex = NULL) const; bool occludes(const VoxelProjectedPolygon& occludee, bool checkAllInView = false) const; - bool pointInside(const glm::vec2& point) const; + bool occludes(const BoundingBox& occludee) const; + bool intersects(const VoxelProjectedPolygon& testee) const; + bool intersects(const BoundingBox& box) const; + bool matches(const VoxelProjectedPolygon& testee) const; + bool matches(const BoundingBox& testee) const; + + bool intersectsOnAxes(const VoxelProjectedPolygon& testee) const; float getMaxX() const { return _maxX; } float getMaxY() const { return _maxY; } @@ -73,6 +95,9 @@ public: }; void printDebugDetails() const; + + static long pointInside_calls; + static long occludes_calls; private: int _vertexCount; From 2b8b7589bea443bb1bc9663abb2d7de5533f2469 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 28 Jun 2013 22:05:46 -0700 Subject: [PATCH 06/55] latest version of quadtree coverage map --- libraries/voxels/src/CoverageMapV2.cpp | 327 +++++++++++++++++++++++++ libraries/voxels/src/CoverageMapV2.h | 69 ++++++ 2 files changed, 396 insertions(+) create mode 100644 libraries/voxels/src/CoverageMapV2.cpp create mode 100644 libraries/voxels/src/CoverageMapV2.h diff --git a/libraries/voxels/src/CoverageMapV2.cpp b/libraries/voxels/src/CoverageMapV2.cpp new file mode 100644 index 0000000000..343fa68a3c --- /dev/null +++ b/libraries/voxels/src/CoverageMapV2.cpp @@ -0,0 +1,327 @@ +// +// CoverageMapV2.cpp - +// hifi +// +// Added by Brad Hefta-Gaub on 06/11/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include + +#include "CoverageMapV2.h" +#include +#include +#include "Log.h" + +int CoverageMapV2::_mapCount = 0; +int CoverageMapV2::_checkMapRootCalls = 0; +int CoverageMapV2::_notAllInView = 0; +bool CoverageMapV2::wantDebugging = false; + +const BoundingBox CoverageMapV2::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-1.f,-1.f), glm::vec2(2.f,2.f)); + +// Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space. +// +// (0,0) (windowWidth, 0) +// -1,1 1,1 +// +-----------------------+ +// | | | +// | | | +// | -1,0 | | +// |-----------+-----------| +// | 0,0 | +// | | | +// | | | +// | | | +// +-----------------------+ +// -1,-1 1,-1 +// (0,windowHeight) (windowWidth,windowHeight) +// + +// Choosing a minimum sized polygon. Since we know a typical window is approximately 1500 pixels wide +// then a pixel on our screen will be ~ 2.0/1500 or 0.0013 "units" wide, similarly pixels are typically +// about that tall as well. If we say that polygons should be at least 10x10 pixels to be considered "big enough" +// then we can calculate a reasonable polygon area +const int TYPICAL_SCREEN_WIDTH_IN_PIXELS = 1500; +const int MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS = 10; +const float TYPICAL_SCREEN_PIXEL_WIDTH = (2.0f / TYPICAL_SCREEN_WIDTH_IN_PIXELS); +const float CoverageMapV2::MINIMUM_POLYGON_AREA_TO_STORE = (TYPICAL_SCREEN_PIXEL_WIDTH * MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS) * + (TYPICAL_SCREEN_PIXEL_WIDTH * MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS); +const float CoverageMapV2::NOT_COVERED = FLT_MAX; +const float CoverageMapV2::MINIMUM_OCCLUSION_CHECK_AREA = MINIMUM_POLYGON_AREA_TO_STORE/10.0f; // one quarter the size of poly + + +CoverageMapV2::CoverageMapV2(BoundingBox boundingBox, bool isRoot, bool isCovered, float coverageDistance) : + _isRoot(isRoot), + _myBoundingBox(boundingBox), + _isCovered(isCovered), + _coveredDistance(coverageDistance) +{ + _mapCount++; + init(); + //printLog("CoverageMapV2 created... _mapCount=%d\n",_mapCount); +}; + +CoverageMapV2::~CoverageMapV2() { + erase(); +}; + +void CoverageMapV2::erase() { + + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + if (_childMaps[i]) { + delete _childMaps[i]; + _childMaps[i] = NULL; + } + } + + if (_isRoot && wantDebugging) { + printLog("CoverageMapV2 last to be deleted...\n"); + printLog("MINIMUM_POLYGON_AREA_TO_STORE=%f\n",MINIMUM_POLYGON_AREA_TO_STORE); + printLog("_mapCount=%d\n",_mapCount); + printLog("_checkMapRootCalls=%d\n",_checkMapRootCalls); + printLog("_notAllInView=%d\n",_notAllInView); + _mapCount = 0; + _checkMapRootCalls = 0; + _notAllInView = 0; + } +} + +void CoverageMapV2::init() { + memset(_childMaps,0,sizeof(_childMaps)); +} + +// 0 = bottom, left +// 1 = bottom, right +// 2 = top, left +// 3 = top, right +BoundingBox CoverageMapV2::getChildBoundingBox(int childIndex) { + const int RIGHT_BIT = 1; + const int TOP_BIT = 2; + // initialize to our corner, and half our size + BoundingBox result(_myBoundingBox.corner,_myBoundingBox.size/2.0f); + // if our "right" bit is set, then add size.x to the corner + if ((childIndex & RIGHT_BIT) == RIGHT_BIT) { + result.corner.x += result.size.x; + } + // if our "top" bit is set, then add size.y to the corner + if ((childIndex & TOP_BIT) == TOP_BIT) { + result.corner.y += result.size.y; + } + return result; +} + +// possible results: +// OCCLUDED - node is occluded by the polygon +// INTERSECT - node and polygon intersect (note: polygon may be occluded by node) +// NO_INTERSECT - node and polygon intersect +CoverageMapV2StorageResult CoverageMapV2::checkNode(const VoxelProjectedPolygon* polygon) { + + float x1 = _myBoundingBox.corner.x; + float y1 = _myBoundingBox.corner.y; + float sx = _myBoundingBox.size.x; + float sy = _myBoundingBox.size.y; + //assert(_myBoundingBox.area() >= MINIMUM_OCCLUSION_CHECK_AREA/10.0f); + + // if we are really small, then we report that we don't intersect, this allows us to stop + // recusing as we get to the smalles edge of the polygon + if (_myBoundingBox.area() < MINIMUM_OCCLUSION_CHECK_AREA) { + //printLog("CoverageMapV2::checkNode() -- (_myBoundingBox.area() < MINIMUM_OCCLUSION_CHECK_AREA) -- returning V2_NO_INTERSECT\n"); + return V2_NO_INTERSECT; + } + + if (polygon->occludes(_myBoundingBox)) { + //printLog("CoverageMapV2::checkNode() returning OCCLUDED\n"); + //polygon->printDebugDetails(); + //_myBoundingBox.printDebugDetails(); + return V2_OCCLUDED; + } else if (polygon->intersects(_myBoundingBox)) { + //printLog("CoverageMapV2::checkNode() -- polygon->intersects(_myBoundingBox) -- returning V2_INTERSECT\n"); + return V2_INTERSECT; + } + + // if we got here and we're the root, then we know we interesect + if (_isRoot) { + //printLog("CoverageMapV2::checkNode() -- _isRoot -- returning V2_INTERSECT\n"); + return V2_INTERSECT; + } + + //printLog("CoverageMapV2::checkNode() -- bottom -- returning V2_NO_INTERSECT\n"); + return V2_NO_INTERSECT; +} + +// possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT +CoverageMapV2StorageResult CoverageMapV2::checkMap(const VoxelProjectedPolygon* polygon, bool storeIt) { + if (_isRoot) { + _checkMapRootCalls++; + + //printLog("CoverageMap2::checkMap()... storeIt=%s\n", debug::valueOf(storeIt)); + //polygon->printDebugDetails(); + } else { + assert(false); // we should only call checkMap() on the root map + } + + // short circuit: if we're the root node (only case we're here), and we're covered, and this polygon is deeper than our + // covered depth, then this polygon is occluded! + if (_isCovered && _coveredDistance < polygon->getDistance()) { + if (!storeIt) { + printLog("CoverageMap2::checkMap()... V2_OCCLUDED storeIt=FALSE ------- (_isCovered && _coveredDistance < polygon->getDistance())\n"); + } + return V2_OCCLUDED; + } + + // short circuit: we don't handle polygons that aren't all in view, so, if the polygon in question is + // not in view, then we just discard it with a DOESNT_FIT, this saves us time checking values later. + if (!polygon->getAllInView()) { + _notAllInView++; + //printLog("CoverageMap2::checkMap()... V2_DOESNT_FIT\n"); + return V2_DOESNT_FIT; + } + + bool polygonIsCompletelyCovered = true; // assume the best. + + // this will recursively set the quad tree map as covered at a certain depth. It will also determine if the polygon + // is occluded by existing coverage set in the map + CoverageMapV2StorageResult result = coverNode(polygon, polygonIsCompletelyCovered, storeIt); + + // If we determined that the polygon was indeed covered already, then report back + if (polygonIsCompletelyCovered) { + //printLog("CoverageMap2::checkMap()... V2_OCCLUDED ------- (polygonIsCompletelyCovered)\n"); + if (!storeIt) { + printLog("CoverageMap2::checkMap()... V2_OCCLUDED storeIt=FALSE ------- (polygonIsCompletelyCovered)\n"); + } + return V2_OCCLUDED; + } + if (storeIt) { + //printLog("CoverageMap2::checkMap()... V2_STORED\n"); + return V2_STORED; // otherwise report that we STORED it + } + //printLog("CoverageMap2::checkMap()... V2_NOT_STORED\n"); + return V2_NOT_STORED; // unless we weren't asked to store it, then we didn't +} + +// recurse down the quad tree, marking nodes that are fully covered by the polygon, that would give us essentially +// a bitmap of the polygon, these nodes are by definition covered for the next polygon we check. +// as we went down this tree, the nodes are either already marked as covered, or not, if any of them were not already marked +// as covered then the polygon isn't occluded. We use the polygonIsCompletelyCovered reference, which is assumed to be +// optimistically set to true by the highest caller. We will logically AND this with all of the "isCovered" states for all +// the nodes that are covered by the polygon. Then end result will tell us if the polygon was occluded +CoverageMapV2StorageResult CoverageMapV2::coverNode(const VoxelProjectedPolygon* polygon, + bool& polygonIsCompletelyCovered, bool storeIt) { + //printLog("CoverageMapV2::coverNode()... BEFORE checkNode() \n"); + //_myBoundingBox.printDebugDetails("coverNode()"); + + CoverageMapV2StorageResult coverageState = checkNode(polygon); + + //printLog("CoverageMapV2::coverNode()... AFTER checkNode() \n"); + + // + // if node is super small, then we need to stop recursion, we probably should do this by saying + // at a certain small size of node, an intersect is as good as an occlude. this is handled by checkNode() + // + if (coverageState == V2_NO_INTERSECT) { + //printLog("CoverageMapV2::coverNode()... V2_NO_INTERSECT returning false \n"); + if (!storeIt) { + //printLog("CoverageMap2::checkMap()... coverageState == V2_NO_INTERSECT storeIt=FALSE polygonIsCompletelyCovered=%s\n",debug::valueOf(polygonIsCompletelyCovered)); + } + return V2_NO_INTERSECT; // don't recurse further, this node isn't covered + } + if (coverageState == V2_OCCLUDED) { // this node's rect is completely covered by the polygon + // if this node is already covered, and it's coverage depth is in front of our polygon depth + // then we logically AND true into the completelyCovered state, if this node is not covered, or it's + // depth is greater than the polygon, then this polygon is not covered at this part of it + // and we should set the isCompletelyCovered to false, which this accomplishes + polygonIsCompletelyCovered = polygonIsCompletelyCovered && (_isCovered && _coveredDistance < polygon->getDistance()); + if (!storeIt && !_isCovered) { + printLog("CoverageMap2::checkMap()... coverageState == V2_OCCLUDED storeIt=FALSE _isCovered=FALSE polygonIsCompletelyCovered=%s\n",debug::valueOf(polygonIsCompletelyCovered)); + } + + if (storeIt) { + _coveredDistance = _isCovered ? std::min(_coveredDistance, polygon->getDistance()) : polygon->getDistance(); + _isCovered = true; // we are definitely covered + } + //printLog("CoverageMapV2::coverNode()... V2_OCCLUDED returning _isCovered=%s \n", debug::valueOf(_isCovered)); + + if (!storeIt) { + printLog("CoverageMap2::checkMap()... coverageState == V2_OCCLUDED storeIt=FALSE polygonIsCompletelyCovered=%s _isCovered=%s _coveredDistance=%f polygon->getDistance()=%f\n",debug::valueOf(polygonIsCompletelyCovered), debug::valueOf(_isCovered),_coveredDistance,polygon->getDistance()); + } + + // If this node is covered by the polygon, then we want to report either OCCLUDED or NOT_OCCLUDED + return ((_isCovered && _coveredDistance < polygon->getDistance()) ? V2_OCCLUDED : V2_NOT_OCCLUDED); // don't recurse further + } + if (coverageState == V2_INTERSECT) { + //printLog("CoverageMapV2::coverNode()... V2_INTERSECT case... check children \n"); + + // NOTE: if we're covered, then we don't really need to recurse any further down! + if (_isCovered && _coveredDistance < polygon->getDistance()) { + //printLog("CoverageMapV2::coverNode()... V2_INTERSECT AND _isCovered... can we pop here??? \n"); + polygonIsCompletelyCovered = polygonIsCompletelyCovered && (_isCovered && _coveredDistance < polygon->getDistance()); + + if (!storeIt) { + printLog("CoverageMap2::checkMap()... coverageState == V2_INTERSECT storeIt=FALSE polygonIsCompletelyCovered=%s\n",debug::valueOf(polygonIsCompletelyCovered)); + } + + // If this node intersects the polygon, then we want to report either OCCLUDED or V2_INTERSECT + return ((_isCovered && _coveredDistance < polygon->getDistance()) ? V2_OCCLUDED : V2_INTERSECT); // don't recurse further + } + + //recurse deeper, and perform same operation on child quads nodes + bool allChildrenOccluded = true; // assume the best + float maxChildCoveredDepth = NOT_COVERED; + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + BoundingBox childMapBoundingBox = getChildBoundingBox(i); + // if no child map exists yet, then create it + if (!_childMaps[i]) { + // children get created with the coverage state of their parent. + _childMaps[i] = new CoverageMapV2(childMapBoundingBox, NOT_ROOT, _isCovered, _coveredDistance); + } + + //printLog("CoverageMapV2::coverNode()... calling coverNode() for child[%d] \n", i); + CoverageMapV2StorageResult childResult = _childMaps[i]->coverNode(polygon, polygonIsCompletelyCovered, storeIt); + //printLog("CoverageMapV2::coverNode()... DONE calling coverNode() for child[%d] childCovered=%s, _childMaps[i]->_isCovered=%s \n", i, + // debug::valueOf(childCovered), debug::valueOf(_childMaps[i]->_isCovered)); + + + if (allChildrenOccluded && _childMaps[i]->_isCovered) { + maxChildCoveredDepth = std::max(maxChildCoveredDepth, _childMaps[i]->_coveredDistance); + } else { + allChildrenOccluded = false; + } + } + // if all the children are covered, this makes our quad tree "shallower" because it records that + // entire quad is covered, it uses the "furthest" z-order so that if a shalower polygon comes through + // we won't assume its occluded + if (allChildrenOccluded && storeIt) { + _isCovered = true; + _coveredDistance = maxChildCoveredDepth; + //printLog("CoverageMapV2::coverNode()... V2_INTERSECT (allChildrenCovered && storeIt) returning _isCovered=%s \n", debug::valueOf(_isCovered)); + + // If this node intersects the polygon, then we want to report either OCCLUDED or V2_INTERSECT + return ((_isCovered && _coveredDistance < polygon->getDistance()) ? V2_OCCLUDED : V2_INTERSECT); // don't recurse further + } + //printLog("CoverageMapV2::coverNode()... V2_INTERSECT returning false \n"); + + if (!storeIt) { + printLog("CoverageMap2::checkMap()... coverageState == V2_INTERSECT LINE 296 storeIt=FALSE polygonIsCompletelyCovered=%s allChildrenOccluded=%s _isCovered=%s\n", + debug::valueOf(polygonIsCompletelyCovered), + debug::valueOf(allChildrenOccluded), + debug::valueOf(_isCovered)); + _myBoundingBox.printDebugDetails(); + } + + // If we got here, we intersect, but we don't occlude + return V2_INTERSECT; + } + + if (!storeIt) { + printLog("CoverageMap2::checkMap()... coverageState == V2_INTERSECT LINE 304 storeIt=FALSE polygonIsCompletelyCovered=%s\n",debug::valueOf(polygonIsCompletelyCovered)); + } + + + //printLog("CoverageMapV2::coverNode()... bottom of function returning false \n"); + + // not sure we should get here! maybe assert! + assert(false); + return coverageState; +} + diff --git a/libraries/voxels/src/CoverageMapV2.h b/libraries/voxels/src/CoverageMapV2.h new file mode 100644 index 0000000000..b415489289 --- /dev/null +++ b/libraries/voxels/src/CoverageMapV2.h @@ -0,0 +1,69 @@ +// +// CoverageMapV2.h - 2D CoverageMapV2 Quad tree for storage of VoxelProjectedPolygons +// hifi +// +// Added by Brad Hefta-Gaub on 06/11/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef _COVERAGE_MAP_V2_ +#define _COVERAGE_MAP_V2_ + +#include + +#include "VoxelProjectedPolygon.h" + +typedef enum { + V2_DOESNT_FIT, V2_STORED, V2_NOT_STORED, + V2_INTERSECT, V2_NO_INTERSECT, + V2_OCCLUDED, V2_NOT_OCCLUDED +} CoverageMapV2StorageResult; + +class CoverageMapV2 { + +public: + static const int NUMBER_OF_CHILDREN = 4; + static const bool NOT_ROOT = false; + static const bool IS_ROOT = true; + static const BoundingBox ROOT_BOUNDING_BOX; + static const float MINIMUM_POLYGON_AREA_TO_STORE; + static const float NOT_COVERED; + static const float MINIMUM_OCCLUSION_CHECK_AREA; + static bool wantDebugging; + + CoverageMapV2(BoundingBox boundingBox = ROOT_BOUNDING_BOX, bool isRoot = IS_ROOT, + bool isCovered = false, float coverageDistance = NOT_COVERED); + ~CoverageMapV2(); + + CoverageMapV2StorageResult checkMap(const VoxelProjectedPolygon* polygon, bool storeIt = true); + + BoundingBox getChildBoundingBox(int childIndex); + const BoundingBox& getBoundingBox() const { return _myBoundingBox; }; + CoverageMapV2* getChild(int childIndex) const { return _childMaps[childIndex]; }; + bool isCovered() const { return _isCovered; }; + + void erase(); // erase the coverage map + + void render(); + + +private: + CoverageMapV2StorageResult checkNode(const VoxelProjectedPolygon* polygon); + CoverageMapV2StorageResult coverNode(const VoxelProjectedPolygon* polygon, bool& polygonIsCompletelyCovered, bool storeIt); + + void init(); + + bool _isRoot; + BoundingBox _myBoundingBox; + CoverageMapV2* _childMaps[NUMBER_OF_CHILDREN]; + + bool _isCovered; + float _coveredDistance; + + static int _mapCount; + static int _checkMapRootCalls; + static int _notAllInView; +}; + + +#endif // _COVERAGE_MAP_V2_ From c7e691a0108bad6127b8c1b618f5734b2d7529b6 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 29 Jun 2013 00:15:57 -0700 Subject: [PATCH 07/55] cleaned up CoverageMapV2 recursion code, fixes crazy false occlusion --- interface/src/Application.cpp | 2 +- interface/src/VoxelSystem.cpp | 4 +- libraries/voxels/src/CoverageMapV2.cpp | 291 +++++++++---------------- libraries/voxels/src/CoverageMapV2.h | 4 +- 4 files changed, 110 insertions(+), 191 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bf3dabaf98..d5ed296804 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1516,7 +1516,7 @@ void Application::init() { _headMouseX = _mouseX = _glWidget->width() / 2; _headMouseY = _mouseY = _glWidget->height() / 2; - _stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0); + //_stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0); _myAvatar.init(); _myAvatar.setPosition(START_LOCATION); diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 430bb782e5..3607bdc7df 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -1303,7 +1303,7 @@ void VoxelSystem::falseColorizeOccluded() { ); - myCoverageMap.erase(); + //myCoverageMap.erase(); setupNewVoxelsForDrawing(); } @@ -1425,7 +1425,7 @@ void VoxelSystem::falseColorizeOccludedV2() { VoxelProjectedPolygon::pointInside_calls, VoxelProjectedPolygon::occludes_calls ); - myCoverageMapV2.erase(); + //myCoverageMapV2.erase(); setupNewVoxelsForDrawing(); diff --git a/libraries/voxels/src/CoverageMapV2.cpp b/libraries/voxels/src/CoverageMapV2.cpp index 343fa68a3c..f5591bb324 100644 --- a/libraries/voxels/src/CoverageMapV2.cpp +++ b/libraries/voxels/src/CoverageMapV2.cpp @@ -111,217 +111,136 @@ BoundingBox CoverageMapV2::getChildBoundingBox(int childIndex) { return result; } -// possible results: -// OCCLUDED - node is occluded by the polygon -// INTERSECT - node and polygon intersect (note: polygon may be occluded by node) -// NO_INTERSECT - node and polygon intersect -CoverageMapV2StorageResult CoverageMapV2::checkNode(const VoxelProjectedPolygon* polygon) { - - float x1 = _myBoundingBox.corner.x; - float y1 = _myBoundingBox.corner.y; - float sx = _myBoundingBox.size.x; - float sy = _myBoundingBox.size.y; - //assert(_myBoundingBox.area() >= MINIMUM_OCCLUSION_CHECK_AREA/10.0f); - - // if we are really small, then we report that we don't intersect, this allows us to stop - // recusing as we get to the smalles edge of the polygon - if (_myBoundingBox.area() < MINIMUM_OCCLUSION_CHECK_AREA) { - //printLog("CoverageMapV2::checkNode() -- (_myBoundingBox.area() < MINIMUM_OCCLUSION_CHECK_AREA) -- returning V2_NO_INTERSECT\n"); - return V2_NO_INTERSECT; - } - - if (polygon->occludes(_myBoundingBox)) { - //printLog("CoverageMapV2::checkNode() returning OCCLUDED\n"); - //polygon->printDebugDetails(); - //_myBoundingBox.printDebugDetails(); - return V2_OCCLUDED; - } else if (polygon->intersects(_myBoundingBox)) { - //printLog("CoverageMapV2::checkNode() -- polygon->intersects(_myBoundingBox) -- returning V2_INTERSECT\n"); - return V2_INTERSECT; - } - - // if we got here and we're the root, then we know we interesect - if (_isRoot) { - //printLog("CoverageMapV2::checkNode() -- _isRoot -- returning V2_INTERSECT\n"); - return V2_INTERSECT; - } - - //printLog("CoverageMapV2::checkNode() -- bottom -- returning V2_NO_INTERSECT\n"); - return V2_NO_INTERSECT; -} - // possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT CoverageMapV2StorageResult CoverageMapV2::checkMap(const VoxelProjectedPolygon* polygon, bool storeIt) { - if (_isRoot) { - _checkMapRootCalls++; - - //printLog("CoverageMap2::checkMap()... storeIt=%s\n", debug::valueOf(storeIt)); - //polygon->printDebugDetails(); - } else { - assert(false); // we should only call checkMap() on the root map - } - + assert(_isRoot); // you can only call this on the root map!!! + _checkMapRootCalls++; + // short circuit: if we're the root node (only case we're here), and we're covered, and this polygon is deeper than our // covered depth, then this polygon is occluded! if (_isCovered && _coveredDistance < polygon->getDistance()) { - if (!storeIt) { - printLog("CoverageMap2::checkMap()... V2_OCCLUDED storeIt=FALSE ------- (_isCovered && _coveredDistance < polygon->getDistance())\n"); - } return V2_OCCLUDED; } - + // short circuit: we don't handle polygons that aren't all in view, so, if the polygon in question is // not in view, then we just discard it with a DOESNT_FIT, this saves us time checking values later. if (!polygon->getAllInView()) { _notAllInView++; - //printLog("CoverageMap2::checkMap()... V2_DOESNT_FIT\n"); return V2_DOESNT_FIT; } - - bool polygonIsCompletelyCovered = true; // assume the best. - - // this will recursively set the quad tree map as covered at a certain depth. It will also determine if the polygon - // is occluded by existing coverage set in the map - CoverageMapV2StorageResult result = coverNode(polygon, polygonIsCompletelyCovered, storeIt); + + // Here's where we recursively check the polygon against the coverage map. We need to maintain two pieces of state. + // The first state is: have we seen at least one "fully occluded" map items. If we haven't then we don't track the covered + // state of the polygon. + // The second piece of state is: Are all of our "fully occluded" map items "covered". If even one of these occluded map + // items is not covered, then our polygon is not covered. + bool seenOccludedMapNodes = false; + bool allOccludedMapNodesCovered = false; - // If we determined that the polygon was indeed covered already, then report back - if (polygonIsCompletelyCovered) { - //printLog("CoverageMap2::checkMap()... V2_OCCLUDED ------- (polygonIsCompletelyCovered)\n"); - if (!storeIt) { - printLog("CoverageMap2::checkMap()... V2_OCCLUDED storeIt=FALSE ------- (polygonIsCompletelyCovered)\n"); - } + recurseMap(polygon, storeIt, seenOccludedMapNodes, allOccludedMapNodesCovered); + + // Ok, no matter how we were called, if all our occluded map nodes are covered, then we know this polygon + // is occluded, otherwise, we will report back to the caller about whether or not we stored the polygon + if (allOccludedMapNodesCovered) { return V2_OCCLUDED; - } + } if (storeIt) { - //printLog("CoverageMap2::checkMap()... V2_STORED\n"); return V2_STORED; // otherwise report that we STORED it } - //printLog("CoverageMap2::checkMap()... V2_NOT_STORED\n"); return V2_NOT_STORED; // unless we weren't asked to store it, then we didn't } -// recurse down the quad tree, marking nodes that are fully covered by the polygon, that would give us essentially -// a bitmap of the polygon, these nodes are by definition covered for the next polygon we check. -// as we went down this tree, the nodes are either already marked as covered, or not, if any of them were not already marked -// as covered then the polygon isn't occluded. We use the polygonIsCompletelyCovered reference, which is assumed to be -// optimistically set to true by the highest caller. We will logically AND this with all of the "isCovered" states for all -// the nodes that are covered by the polygon. Then end result will tell us if the polygon was occluded -CoverageMapV2StorageResult CoverageMapV2::coverNode(const VoxelProjectedPolygon* polygon, - bool& polygonIsCompletelyCovered, bool storeIt) { - //printLog("CoverageMapV2::coverNode()... BEFORE checkNode() \n"); - //_myBoundingBox.printDebugDetails("coverNode()"); +void CoverageMapV2::recurseMap(const VoxelProjectedPolygon* polygon, bool storeIt, + bool& seenOccludedMapNodes, bool& allOccludedMapNodesCovered) { - CoverageMapV2StorageResult coverageState = checkNode(polygon); - - //printLog("CoverageMapV2::coverNode()... AFTER checkNode() \n"); - - // - // if node is super small, then we need to stop recursion, we probably should do this by saying - // at a certain small size of node, an intersect is as good as an occlude. this is handled by checkNode() - // - if (coverageState == V2_NO_INTERSECT) { - //printLog("CoverageMapV2::coverNode()... V2_NO_INTERSECT returning false \n"); - if (!storeIt) { - //printLog("CoverageMap2::checkMap()... coverageState == V2_NO_INTERSECT storeIt=FALSE polygonIsCompletelyCovered=%s\n",debug::valueOf(polygonIsCompletelyCovered)); - } - return V2_NO_INTERSECT; // don't recurse further, this node isn't covered - } - if (coverageState == V2_OCCLUDED) { // this node's rect is completely covered by the polygon - // if this node is already covered, and it's coverage depth is in front of our polygon depth - // then we logically AND true into the completelyCovered state, if this node is not covered, or it's - // depth is greater than the polygon, then this polygon is not covered at this part of it - // and we should set the isCompletelyCovered to false, which this accomplishes - polygonIsCompletelyCovered = polygonIsCompletelyCovered && (_isCovered && _coveredDistance < polygon->getDistance()); - if (!storeIt && !_isCovered) { - printLog("CoverageMap2::checkMap()... coverageState == V2_OCCLUDED storeIt=FALSE _isCovered=FALSE polygonIsCompletelyCovered=%s\n",debug::valueOf(polygonIsCompletelyCovered)); - } - - if (storeIt) { - _coveredDistance = _isCovered ? std::min(_coveredDistance, polygon->getDistance()) : polygon->getDistance(); - _isCovered = true; // we are definitely covered - } - //printLog("CoverageMapV2::coverNode()... V2_OCCLUDED returning _isCovered=%s \n", debug::valueOf(_isCovered)); - - if (!storeIt) { - printLog("CoverageMap2::checkMap()... coverageState == V2_OCCLUDED storeIt=FALSE polygonIsCompletelyCovered=%s _isCovered=%s _coveredDistance=%f polygon->getDistance()=%f\n",debug::valueOf(polygonIsCompletelyCovered), debug::valueOf(_isCovered),_coveredDistance,polygon->getDistance()); - } - - // If this node is covered by the polygon, then we want to report either OCCLUDED or NOT_OCCLUDED - return ((_isCovered && _coveredDistance < polygon->getDistance()) ? V2_OCCLUDED : V2_NOT_OCCLUDED); // don't recurse further - } - if (coverageState == V2_INTERSECT) { - //printLog("CoverageMapV2::coverNode()... V2_INTERSECT case... check children \n"); - - // NOTE: if we're covered, then we don't really need to recurse any further down! - if (_isCovered && _coveredDistance < polygon->getDistance()) { - //printLog("CoverageMapV2::coverNode()... V2_INTERSECT AND _isCovered... can we pop here??? \n"); - polygonIsCompletelyCovered = polygonIsCompletelyCovered && (_isCovered && _coveredDistance < polygon->getDistance()); - - if (!storeIt) { - printLog("CoverageMap2::checkMap()... coverageState == V2_INTERSECT storeIt=FALSE polygonIsCompletelyCovered=%s\n",debug::valueOf(polygonIsCompletelyCovered)); - } - - // If this node intersects the polygon, then we want to report either OCCLUDED or V2_INTERSECT - return ((_isCovered && _coveredDistance < polygon->getDistance()) ? V2_OCCLUDED : V2_INTERSECT); // don't recurse further - } - - //recurse deeper, and perform same operation on child quads nodes - bool allChildrenOccluded = true; // assume the best - float maxChildCoveredDepth = NOT_COVERED; - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - BoundingBox childMapBoundingBox = getChildBoundingBox(i); - // if no child map exists yet, then create it - if (!_childMaps[i]) { - // children get created with the coverage state of their parent. - _childMaps[i] = new CoverageMapV2(childMapBoundingBox, NOT_ROOT, _isCovered, _coveredDistance); - } - - //printLog("CoverageMapV2::coverNode()... calling coverNode() for child[%d] \n", i); - CoverageMapV2StorageResult childResult = _childMaps[i]->coverNode(polygon, polygonIsCompletelyCovered, storeIt); - //printLog("CoverageMapV2::coverNode()... DONE calling coverNode() for child[%d] childCovered=%s, _childMaps[i]->_isCovered=%s \n", i, - // debug::valueOf(childCovered), debug::valueOf(_childMaps[i]->_isCovered)); - - - if (allChildrenOccluded && _childMaps[i]->_isCovered) { - maxChildCoveredDepth = std::max(maxChildCoveredDepth, _childMaps[i]->_coveredDistance); - } else { - allChildrenOccluded = false; - } - } - // if all the children are covered, this makes our quad tree "shallower" because it records that - // entire quad is covered, it uses the "furthest" z-order so that if a shalower polygon comes through - // we won't assume its occluded - if (allChildrenOccluded && storeIt) { - _isCovered = true; - _coveredDistance = maxChildCoveredDepth; - //printLog("CoverageMapV2::coverNode()... V2_INTERSECT (allChildrenCovered && storeIt) returning _isCovered=%s \n", debug::valueOf(_isCovered)); - - // If this node intersects the polygon, then we want to report either OCCLUDED or V2_INTERSECT - return ((_isCovered && _coveredDistance < polygon->getDistance()) ? V2_OCCLUDED : V2_INTERSECT); // don't recurse further - } - //printLog("CoverageMapV2::coverNode()... V2_INTERSECT returning false \n"); - - if (!storeIt) { - printLog("CoverageMap2::checkMap()... coverageState == V2_INTERSECT LINE 296 storeIt=FALSE polygonIsCompletelyCovered=%s allChildrenOccluded=%s _isCovered=%s\n", - debug::valueOf(polygonIsCompletelyCovered), - debug::valueOf(allChildrenOccluded), - debug::valueOf(_isCovered)); - _myBoundingBox.printDebugDetails(); - } - - // If we got here, we intersect, but we don't occlude - return V2_INTERSECT; + // if we are really small, then we act like we don't intersect, this allows us to stop + // recusing as we get to the smalles edge of the polygon + if (_myBoundingBox.area() < MINIMUM_OCCLUSION_CHECK_AREA) { + return; // stop recursion, we're done! } - if (!storeIt) { - printLog("CoverageMap2::checkMap()... coverageState == V2_INTERSECT LINE 304 storeIt=FALSE polygonIsCompletelyCovered=%s\n",debug::valueOf(polygonIsCompletelyCovered)); + // Determine if this map node intersects the polygon and/or is fully covered by the polygon + // There are a couple special cases: If we're the root, we are assumed to intersect with all + // polygons. Also, any map node that is fully occluded also intersects. + bool nodeIsCoveredByPolygon = polygon->occludes(_myBoundingBox); + bool nodeIsIntersectedByPolygon = nodeIsCoveredByPolygon || _isRoot || polygon->intersects(_myBoundingBox); + + // If we don't intersect, then we can just return, we're done recursing + if (!nodeIsIntersectedByPolygon) { + return; // stop recursion, we're done! } - - //printLog("CoverageMapV2::coverNode()... bottom of function returning false \n"); + // At this point, we know our node intersects with the polygon. If this node is covered, then we want to treat it + // as if the node was fully covered, because this allows us to short circuit further recursion... + if (_isCovered && _coveredDistance < polygon->getDistance()) { + nodeIsCoveredByPolygon = true; // fake it till you make it + } - // not sure we should get here! maybe assert! - assert(false); - return coverageState; -} + // If this node in the map is fully covered by our polygon, then we don't need to recurse any further, but + // we do need to do some bookkeeping. + if (nodeIsCoveredByPolygon) { + // If this is the very first fully covered node we've seen, then we're initialize our allOccludedMapNodesCovered + // to be our current covered state. This has the following effect: if this node isn't already covered, then by + // definition, we know that at least one node for this polygon isn't covered, and therefore we aren't fully covered. + if (!seenOccludedMapNodes) { + allOccludedMapNodesCovered = (_isCovered && _coveredDistance < polygon->getDistance()); + // We need to mark that we've seen at least one node of our polygon! ;) + seenOccludedMapNodes = true; + } else { + // If this is our second or later node of our polygon, then we need to track our allOccludedMapNodesCovered state + allOccludedMapNodesCovered = allOccludedMapNodesCovered && + (_isCovered && _coveredDistance < polygon->getDistance()); + } + + // if we're in store mode then we want to record that this node is covered. + if (storeIt) { + _isCovered = true; + // store the minimum distance of our previous known distance, or our current polygon's distance. This is because + // we know that we're at least covered at this distance, but if we had previously identified that we're covered + // at a shallower distance, then we want to maintain that distance + _coveredDistance = std::min(polygon->getDistance(), _coveredDistance); + + // Note: this might be a good chance to delete child maps, but we're not going to do that at this point because + // we're trying to maintain the known distances in the lower portion of the tree. + } + + // and since this node of the quad map is covered, we can safely stop recursion. because we know all smaller map + // nodes will also be covered. + return; + } + + // If we got here, then it means we know that this node is not fully covered by the polygon, but it does intersect + // with the polygon. + + // Another case is that we aren't yet marked as covered, and so we should recurse and process smaller quad tree nodes. + // Note: we use this to determine if we can collapse the child quad trees and mark this node as covered + bool allChildrenOccluded = true; + float maxChildCoveredDepth = NOT_COVERED; + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + BoundingBox childMapBoundingBox = getChildBoundingBox(i); + // if no child map exists yet, then create it + if (!_childMaps[i]) { + // children get created with the coverage state of their parent. + _childMaps[i] = new CoverageMapV2(childMapBoundingBox, NOT_ROOT, _isCovered, _coveredDistance); + } + _childMaps[i]->recurseMap(polygon, storeIt, seenOccludedMapNodes, allOccludedMapNodesCovered); + + // if so far, all of our children are covered, then record our furthest coverage distance + if (allChildrenOccluded && _childMaps[i]->_isCovered) { + maxChildCoveredDepth = std::max(maxChildCoveredDepth, _childMaps[i]->_coveredDistance); + } else { + // otherwise, at least one of our children is not covered, so not all are covered + allChildrenOccluded = false; + } + } + // if all the children are covered, this makes our quad tree "shallower" because it records that + // entire quad is covered, it uses the "furthest" z-order so that if a shalower polygon comes through + // we won't assume its occluded + if (allChildrenOccluded && storeIt) { + _isCovered = true; + _coveredDistance = maxChildCoveredDepth; + } + + // normal exit case... return... +} \ No newline at end of file diff --git a/libraries/voxels/src/CoverageMapV2.h b/libraries/voxels/src/CoverageMapV2.h index b415489289..bce54263c9 100644 --- a/libraries/voxels/src/CoverageMapV2.h +++ b/libraries/voxels/src/CoverageMapV2.h @@ -48,8 +48,8 @@ public: private: - CoverageMapV2StorageResult checkNode(const VoxelProjectedPolygon* polygon); - CoverageMapV2StorageResult coverNode(const VoxelProjectedPolygon* polygon, bool& polygonIsCompletelyCovered, bool storeIt); + void recurseMap(const VoxelProjectedPolygon* polygon, bool storeIt, + bool& seenOccludedMapNodes, bool& allOccludedMapNodesCovered); void init(); From f764a8d6095e0cdb7ee2ff5ea54beea4aaea0a3e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 29 Jun 2013 00:25:04 -0700 Subject: [PATCH 08/55] DRY up code in polygon --- .../voxels/src/VoxelProjectedPolygon.cpp | 86 +++---------------- 1 file changed, 10 insertions(+), 76 deletions(-) diff --git a/libraries/voxels/src/VoxelProjectedPolygon.cpp b/libraries/voxels/src/VoxelProjectedPolygon.cpp index d4c1559612..778ee8e525 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.cpp +++ b/libraries/voxels/src/VoxelProjectedPolygon.cpp @@ -191,54 +191,9 @@ bool VoxelProjectedPolygon::occludes(const VoxelProjectedPolygon& occludee, bool return false; // if we got this far, then we're not occluded } -bool VoxelProjectedPolygon::occludes(const BoundingBox& occludee) const { - - VoxelProjectedPolygon::occludes_calls++; - - // first check the bounding boxes, the occludee must be fully within the boounding box of this shadow - if ((occludee.getMaxX() > getMaxX()) || - (occludee.getMaxY() > getMaxY()) || - (occludee.getMinX() < getMinX()) || - (occludee.getMinY() < getMinY())) { - return false; - } - - // we need to test for identity as well, because in the case of identity, none of the points - // will be "inside" but we don't want to bail early on the first non-inside point - bool potentialIdenity = false; - - if ((occludee.getVertexCount() == getVertexCount()) && (getBoundingBox().contains(occludee)) ) { - potentialIdenity = true; - } - // if we got this far, then check each vertex of the occludee, if all those points - // are inside our polygon, then the tested occludee is fully occluded - int pointsInside = 0; - for(int i = 0; i < occludee.getVertexCount(); i++) { - bool vertexMatched = false; - if (!pointInside(occludee.getVertex(i), &vertexMatched)) { - - // so the point we just tested isn't inside, but it might have matched a vertex - // if it didn't match a vertext, then we bail because we can't be an identity - // or if we're not expecting identity, then we also bail early, no matter what - if (!potentialIdenity || !vertexMatched) { - return false; - } - } else { - pointsInside++; - } - } - - // we're only here if all points are inside matched and/or we had a potentialIdentity we need to check - if (pointsInside == occludee.getVertexCount()) { - return true; - } - - // If we have the potential for identity, then test to see if we match, if we match, we occlude - if (potentialIdenity) { - return matches(occludee); - } - - return false; // if we got this far, then we're not occluded +bool VoxelProjectedPolygon::occludes(const BoundingBox& boxOccludee) const { + VoxelProjectedPolygon testee(boxOccludee); + return occludes(testee); } bool VoxelProjectedPolygon::matches(const VoxelProjectedPolygon& testee) const { @@ -270,33 +225,9 @@ bool VoxelProjectedPolygon::matches(const VoxelProjectedPolygon& testee) const { return true; // all of our vertices match, therefore we're the same } -bool VoxelProjectedPolygon::matches(const BoundingBox& testee) const { - if (testee.getVertexCount() != getVertexCount()) { - return false; - } - int vertextCount = getVertexCount(); - // find which testee vertex matches our first polygon vertex. - glm::vec2 polygonVertex = getVertex(0); - int originIndex = 0; - for(int i = 0; i < vertextCount; i++) { - glm::vec2 testeeVertex = testee.getVertex(i); - - // if they match, we found our origin. - if (testeeVertex == polygonVertex) { - originIndex = i; - break; - } - } - // Now, starting at the originIndex, walk the vertices of both the testee and ourselves - - for(int i = 0; i < vertextCount; i++) { - glm::vec2 testeeVertex = testee.getVertex((i + originIndex) % vertextCount); - glm::vec2 polygonVertex = getVertex(i); - if (testeeVertex != polygonVertex) { - return false; // we don't match, therefore we're not the same - } - } - return true; // all of our vertices match, therefore we're the same +bool VoxelProjectedPolygon::matches(const BoundingBox& box) const { + VoxelProjectedPolygon testee(box); + return matches(testee); } bool VoxelProjectedPolygon::pointInside(const glm::vec2& point, bool* matchesVertex) const { @@ -328,6 +259,9 @@ bool VoxelProjectedPolygon::pointInside(const glm::vec2& point, bool* matchesVer } /* + +// This old version of pointInside works on concave or convex polygons. But it's slower, and since we don't need +// to support concave polygons, we don't need to do this extra heavy lifting bool VoxelProjectedPolygon::pointInside(const glm::vec2& point, bool* matchesVertex) const { // first check the bounding boxes, the point must be fully within the boounding box of this shadow if ((point.x > getMaxX()) || @@ -402,7 +336,7 @@ bool VoxelProjectedPolygon::intersects(const VoxelProjectedPolygon& testee) cons // @return false if the polygons are disjoint on any of this polygon's axes, true if they // intersect on all axes. // -// Note: this only works on +// Note: this only works on convex polygons // // bool VoxelProjectedPolygon::intersectsOnAxes(const VoxelProjectedPolygon& testee) const { From 015ff85cea21c99be53cdfdd81c96b20453c35be Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 1 Jul 2013 21:39:56 -0700 Subject: [PATCH 09/55] good progress on merging polygons --- interface/src/Application.cpp | 24 + libraries/shared/src/SharedUtil.cpp | 22 + libraries/shared/src/SharedUtil.h | 5 + libraries/voxels/src/CoverageMap.cpp | 54 +- libraries/voxels/src/CoverageMap.h | 3 + libraries/voxels/src/ViewFrustum.cpp | 28 +- .../voxels/src/VoxelProjectedPolygon.cpp | 747 ++++++++++++++++++ libraries/voxels/src/VoxelProjectedPolygon.h | 31 +- 8 files changed, 890 insertions(+), 24 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d5ed296804..4b5faaf4ed 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2475,6 +2475,30 @@ void Application::renderCoverageMapsRecursively(CoverageMap* map) { for (int i = 0; i < map->getPolygonCount(); i++) { VoxelProjectedPolygon* polygon = map->getPolygon(i); + + if (polygon->getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_BOTTOM)) { + glColor3f(.5,0,0); // dark red + } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT | PROJECTION_TOP)) { + glColor3f(0,.5,0); // dark green + } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT)) { + glColor3f(.5,.5,0); // dark yellow + } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT)) { + glColor3f(.5,.5,.5); // gray + } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT | PROJECTION_TOP)) { + glColor3f(0,0,1); // Blue + } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT | PROJECTION_BOTTOM)) { + glColor3f(.5,0,.5); // dark magenta + } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_BOTTOM)) { + glColor3f(1,0,0); // red + } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_TOP)) { + glColor3f(1,0,1); // magenta + } else if (polygon->getProjectionType() == (PROJECTION_NEAR)) { + glColor3f(1,1,0); // yellow + } else if (polygon->getProjectionType() == (PROJECTION_FAR | PROJECTION_RIGHT | PROJECTION_BOTTOM)) { + glColor3f(0,.5,.5); // dark cyan + } else { + glColor3f(0,0,1); + } glm::vec2 firstPoint = getScaledScreenPoint(polygon->getVertex(0)); glm::vec2 lastPoint(firstPoint); diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index bdc35ff3f5..aaa21a5a7b 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -440,3 +440,25 @@ int insertIntoSortedArrays(void* value, float key, int originalIndex, return -1; // error case } +int removeFromSortedArrays(void* value, void** valueArray, float* keyArray, int* originalIndexArray, + int currentCount, int maxCount) { + + int i = 0; + if (currentCount > 0) { + while (i < currentCount && value != valueArray[i]) { + i++; + } + + if (value == valueArray[i] && i < currentCount) { + // i is the location of the item we were looking for + // shift array elements to the left + memmove(&valueArray[i], &valueArray[i + 1], sizeof(void*) * ((currentCount-1) - i)); + memmove(&keyArray[i], &keyArray[i + 1], sizeof(float) * ((currentCount-1) - i)); + if (originalIndexArray) { + memmove(&originalIndexArray[i], &originalIndexArray[i + 1], sizeof(int) * ((currentCount-1) - i)); + } + return currentCount-1; + } + } + return -1; // error case +} diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 2c9e1e6317..f134828945 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -88,6 +88,11 @@ int insertIntoSortedArrays(void* value, float key, int originalIndex, void** valueArray, float* keyArray, int* originalIndexArray, int currentCount, int maxCount); +int removeFromSortedArrays(void* value, void** valueArray, float* keyArray, int* originalIndexArray, + int currentCount, int maxCount); + + + // Helper Class for debugging class debug { public: diff --git a/libraries/voxels/src/CoverageMap.cpp b/libraries/voxels/src/CoverageMap.cpp index 92533786b8..82c529ded6 100644 --- a/libraries/voxels/src/CoverageMap.cpp +++ b/libraries/voxels/src/CoverageMap.cpp @@ -196,6 +196,7 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b bool fitsInAHalf = false; // Check each half of the box independently +/** if (_topHalf.contains(polygonBox)) { result = _topHalf.checkRegion(polygon, polygonBox, storeIt); storeIn = &_topHalf; @@ -213,6 +214,7 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b storeIn = &_rightHalf; fitsInAHalf = true; } +**/ // if we got this far, there are one of two possibilities, either a polygon doesn't fit // in one of the halves, or it did fit, but it wasn't occluded by anything only in that @@ -239,6 +241,7 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b // if we made it here, then it means the polygon being stored is not occluded // at this level of the quad tree, so we can continue to insert it into the map. // First we check to see if it fits in any of our sub maps +if (false) { for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { BoundingBox childMapBoundingBox = getChildBoundingBox(i); if (childMapBoundingBox.contains(polygon->getBoundingBox())) { @@ -268,6 +271,7 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b return result; } } +} // if we got this far, then the polygon is in our bounding box, but doesn't fit in // any of our child bounding boxes, so we should add it here. if (storeIt) { @@ -397,13 +401,53 @@ int CoverageRegion::_tooSmallSkips = 0; int CoverageRegion::_outOfOrderPolygon = 0; int CoverageRegion::_clippedPolygons = 0; + +bool CoverageRegion::mergeItemsInArray(VoxelProjectedPolygon* seed, bool seedInArray) { + for (int i = 0; i < _polygonCount; i++) { + VoxelProjectedPolygon* otherPolygon = _polygons[i]; + if (otherPolygon->canMerge(*seed)) { + otherPolygon->merge(*seed); + + if (seedInArray) { + const int IGNORED = NULL; + // remove this otherOtherPolygon for our polygon array + _polygonCount = removeFromSortedArrays((void*)seed, + (void**)_polygons, _polygonDistances, IGNORED, + _polygonCount, _polygonArraySize); + _totalPolygons--; + } + + //printLog("_polygonCount=%d\n",_polygonCount); + + // clean up + if (_managePolygons) { + delete seed; + } + + // Now run again using our newly merged polygon as the seed + mergeItemsInArray(otherPolygon, true); + + return true; + } + } + return false; +} + // just handles storage in the array, doesn't test for occlusion or // determining if this is the correct map to store in! void CoverageRegion::storeInArray(VoxelProjectedPolygon* polygon) { - _totalPolygons++; - _currentCoveredBounds.explandToInclude(polygon->getBoundingBox()); + + + // Before we actually store this polygon in the array, check to see if this polygon can be merged to any of the existing + // polygons already in our array. + if (mergeItemsInArray(polygon, false)) { + return; // exit early + } + + // only after we attempt to merge! + _totalPolygons++; if (_polygonArraySize < _polygonCount + 1) { growPolygonArray(); @@ -414,11 +458,11 @@ void CoverageRegion::storeInArray(VoxelProjectedPolygon* polygon) { // in the list. We still check to see if the polygon is "in front" of the target polygon before we test occlusion. Since // sometimes things come out of order. const bool SORT_BY_SIZE = false; + const int IGNORED = NULL; if (SORT_BY_SIZE) { // This old code assumes that polygons will always be added in z-buffer order, but that doesn't seem to // be a good assumption. So instead, we will need to sort this by distance. Use a binary search to find the // insertion point in this array, and shift the array accordingly - const int IGNORED = NULL; float area = polygon->getBoundingBox().area(); float reverseArea = 4.0f - area; //printLog("store by size area=%f reverse area=%f\n", area, reverseArea); @@ -458,13 +502,13 @@ CoverageMapStorageResult CoverageRegion::checkRegion(VoxelProjectedPolygon* poly // check to make sure this polygon isn't occluded by something at this level for (int i = 0; i < _polygonCount; i++) { VoxelProjectedPolygon* polygonAtThisLevel = _polygons[i]; + // Check to make sure that the polygon in question is "behind" the polygon in the list // otherwise, we don't need to test it's occlusion (although, it means we've potentially // added an item previously that may be occluded??? Is that possible? Maybe not, because two // voxels can't have the exact same outline. So one occludes the other, they can't both occlude // each other. - - + _occlusionTests++; if (polygonAtThisLevel->occludes(*polygon)) { // if the polygonAtThisLevel is actually behind the one we're inserting, then we don't diff --git a/libraries/voxels/src/CoverageMap.h b/libraries/voxels/src/CoverageMap.h index 7fee7cb6a5..6c0a1f86cb 100644 --- a/libraries/voxels/src/CoverageMap.h +++ b/libraries/voxels/src/CoverageMap.h @@ -59,6 +59,9 @@ private: float* _polygonSizes; void growPolygonArray(); static const int DEFAULT_GROW_SIZE = 100; + + bool mergeItemsInArray(VoxelProjectedPolygon* seed, bool seedInArray); + }; class CoverageMap { diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index cb6c0b219d..b2976c8907 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -461,7 +461,7 @@ const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_PROJECTED_POLYGON_VERT //0 {0}, // inside {4, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR}, // right - {4, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR }, // left + {4, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR }, // left {0}, // n/a //4 @@ -480,20 +480,25 @@ const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_PROJECTED_POLYGON_VERT {0}, // n/a {0}, // n/a //16 - {4, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front or near - {6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, right - {6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // front, left + {4, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, // front or near + + {6, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, // front, right + {6, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, }, // front, left {0}, // n/a //20 - {6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR }, // front,bottom + {6, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, // front,bottom + //21 {6, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, //front,bottom,right //22 - {6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR }, //front,bottom,left + {6, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR }, //front,bottom,left {0}, // n/a - {6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, top - {6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, top, right - {6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // front, top, left + + {6, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // front, top + + {6, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR }, // front, top, right + + {6, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR }, // front, top, left {0}, // n/a {0}, // n/a {0}, // n/a @@ -553,7 +558,7 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { projectedPolygon.setVertex(i, projectedPoint); } - /***/ + /*** // Now that we've got the polygon, if it extends beyond the clipping window, then let's clip it // NOTE: This clipping does not improve our overall performance. It basically causes more polygons to // end up in the same quad/half and so the polygon lists get longer, and that's more calls to polygon.occludes() @@ -574,6 +579,8 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { projectedPolygon.setVertex(i, clippedVertices[i]); } delete[] clippedVertices; + + lookUp += PROJECTION_CLIPPED; } /***/ } @@ -582,5 +589,6 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { projectedPolygon.setDistance(distance); projectedPolygon.setAnyInView(anyPointsInView); projectedPolygon.setAllInView(allPointsInView); + projectedPolygon.setProjectionType(lookUp); // remember the projection type return projectedPolygon; } diff --git a/libraries/voxels/src/VoxelProjectedPolygon.cpp b/libraries/voxels/src/VoxelProjectedPolygon.cpp index 778ee8e525..07a4d04c48 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.cpp +++ b/libraries/voxels/src/VoxelProjectedPolygon.cpp @@ -369,4 +369,751 @@ bool VoxelProjectedPolygon::intersectsOnAxes(const VoxelProjectedPolygon& testee return true; } +bool VoxelProjectedPolygon::canMerge(const VoxelProjectedPolygon& that) const { + // RIGHT/NEAR + // LEFT/NEAR + if ( + (getProjectionType() == that.getProjectionType()) && + ( + getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR) || + getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR) + ) + ) { + if (getVertex(1) == that.getVertex(0) && getVertex(4) == that.getVertex(5)) { + return true; + } + if (getVertex(0) == that.getVertex(1) && getVertex(5) == that.getVertex(4)) { + return true; + } + if (getVertex(2) == that.getVertex(1) && getVertex(3) == that.getVertex(4)) { + return true; + } + if (getVertex(1) == that.getVertex(2) && getVertex(4) == that.getVertex(3)) { + return true; + } + } + // NEAR/BOTTOM + if ( + (getProjectionType() == that.getProjectionType()) && + ( + getProjectionType() == (PROJECTION_NEAR | PROJECTION_BOTTOM) + ) + ) { + if (getVertex(0) == that.getVertex(5) && getVertex(3) == that.getVertex(4)) { + return true; + } + if (getVertex(5) == that.getVertex(0) && getVertex(4) == that.getVertex(3)) { + return true; + } + if (getVertex(1) == that.getVertex(0) && getVertex(2) == that.getVertex(3)) { + return true; + } + if (getVertex(0) == that.getVertex(1) && getVertex(3) == that.getVertex(2)) { + return true; + } + } + + // NEAR/TOP + if ( + (getProjectionType() == that.getProjectionType()) && + ( + getProjectionType() == (PROJECTION_NEAR | PROJECTION_TOP) + ) + ) { + if (getVertex(0) == that.getVertex(5) && getVertex(1) == that.getVertex(2)) { + return true; + } + if (getVertex(5) == that.getVertex(0) && getVertex(2) == that.getVertex(1)) { + return true; + } + if (getVertex(4) == that.getVertex(5) && getVertex(3) == that.getVertex(2)) { + return true; + } + if (getVertex(5) == that.getVertex(4) && getVertex(2) == that.getVertex(3)) { + return true; + } + } + + // RIGHT/NEAR & NEAR/RIGHT/TOP + // LEFT/NEAR & NEAR/LEFT/TOP + if ( + ((getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_TOP)) && + (that.getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR))) + || + ((getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR | PROJECTION_TOP)) && + (that.getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR))) + ) + { + if (getVertex(5) == that.getVertex(0) && getVertex(3) == that.getVertex(2)) { + return true; + } + } + // RIGHT/NEAR & NEAR/RIGHT/TOP + // LEFT/NEAR & NEAR/LEFT/TOP + if ( + ((that.getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_TOP)) && + (getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR))) + || + ((that.getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR | PROJECTION_TOP)) && + (getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR))) + + ) + { + if (getVertex(0) == that.getVertex(5) && getVertex(2) == that.getVertex(3)) { + return true; + } + } + + // RIGHT/NEAR & NEAR/RIGHT/BOTTOM + // NEAR/LEFT & NEAR/LEFT/BOTTOM + if ( + ((that.getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_BOTTOM)) && + (getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR))) + || + ((that.getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR | PROJECTION_BOTTOM)) && + (getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR))) + + ) + { + if (getVertex(5) == that.getVertex(0) && getVertex(3) == that.getVertex(2)) { + return true; + } + } + // RIGHT/NEAR & NEAR/RIGHT/BOTTOM + // NEAR/LEFT & NEAR/LEFT/BOTTOM + if ( + ((getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_BOTTOM)) && + (that.getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR))) + || + ((getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR | PROJECTION_BOTTOM)) && + (that.getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR))) + ) + { + if (getVertex(0) == that.getVertex(5) && getVertex(2) == that.getVertex(3)) { + return true; + } + } + + // NEAR/TOP & NEAR + if ( + (getProjectionType() == (PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_NEAR | PROJECTION_TOP )) + ) + { + if (getVertex(0) == that.getVertex(5) && getVertex(1) == that.getVertex(2)) { + return true; + } + } + + // NEAR/TOP & NEAR + if ( + (that.getProjectionType() == (PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_NEAR | PROJECTION_TOP )) + ) + { + if (getVertex(5) == that.getVertex(0) && getVertex(2) == that.getVertex(1)) { + return true; + } + } + + // NEAR/BOTTOM & NEAR + if ( + (getProjectionType() == (PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_NEAR | PROJECTION_BOTTOM )) + ) + { + if (getVertex(2) == that.getVertex(3) && getVertex(3) == that.getVertex(0)) { + return true; + } + } + + // NEAR/BOTTOM & NEAR + if ( + (that.getProjectionType() == (PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_NEAR | PROJECTION_BOTTOM )) + ) + { + if (getVertex(3) == that.getVertex(2) && getVertex(0) == that.getVertex(3)) { + return true; + } + } + + // NEAR/RIGHT & NEAR + if ( + (getProjectionType() == (PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT )) + ) + { + if (getVertex(0) == that.getVertex(1) && getVertex(3) == that.getVertex(4)) { + return true; + } + } + + // NEAR/RIGHT & NEAR + if ( + (that.getProjectionType() == (PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT )) + ) + { + if (getVertex(1) == that.getVertex(0) && getVertex(4) == that.getVertex(3)) { + return true; + } + } + + // NEAR/LEFT & NEAR + if ( + (getProjectionType() == (PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT )) + ) + { + if (getVertex(1) == that.getVertex(1) && getVertex(2) == that.getVertex(4)) { + return true; + } + } + + // NEAR/LEFT & NEAR + if ( + (that.getProjectionType() == (PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT )) + ) + { + if (getVertex(1) == that.getVertex(0) && getVertex(4) == that.getVertex(3)) { + return true; + } + } + + + // NEAR/RIGHT/BOTTOM & NEAR/BOTTOM + if ( + (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT )) + ) + { + if (getVertex(1) == that.getVertex(2) && getVertex(5) == that.getVertex(4)) { + return true; + } + } + + // NEAR/RIGHT/BOTTOM & NEAR/BOTTOM + if ( + (that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT )) + ) + { + if (getVertex(2) == that.getVertex(1) && getVertex(4) == that.getVertex(5)) { + return true; + } + } + + + // RIGHT/NEAR/BOTTOM + // RIGHT/NEAR/TOP + // LEFT/NEAR/BOTTOM + // LEFT/NEAR/TOP + if ( + (getProjectionType() == that.getProjectionType()) && + ( + getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_BOTTOM ) || + getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_TOP ) || + getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR | PROJECTION_BOTTOM ) || + getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR | PROJECTION_TOP ) + ) + ) { + if (getVertex(0) == that.getVertex(5) && getVertex(2) == that.getVertex(3)) { + return true; + } + if (getVertex(5) == that.getVertex(0) && getVertex(3) == that.getVertex(2)) { + return true; + } + if (getVertex(2) == that.getVertex(1) && getVertex(4) == that.getVertex(5)) { + return true; + } + if (getVertex(1) == that.getVertex(2) && getVertex(5) == that.getVertex(4)) { + return true; + } + if (getVertex(1) == that.getVertex(0) && getVertex(3) == that.getVertex(4)) { + return true; + } + if (getVertex(0) == that.getVertex(1) && getVertex(4) == that.getVertex(3)) { + return true; + } + } + + return false; +} + + +void VoxelProjectedPolygon::merge(const VoxelProjectedPolygon& that) { + + // RIGHT/NEAR + // LEFT/NEAR + if ( + (getProjectionType() == that.getProjectionType()) && + ( + getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR) || + getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR) + ) + ) { + if (getVertex(1) == that.getVertex(0) && getVertex(4) == that.getVertex(5)) { + //setVertex(0, this.getVertex(0)); // no change + setVertex(1, that.getVertex(1)); + setVertex(2, that.getVertex(2)); + setVertex(3, that.getVertex(3)); + setVertex(4, that.getVertex(4)); + //setVertex(5, this.getVertex(5)); // no change + return; // done + } + if (getVertex(0) == that.getVertex(1) && getVertex(5) == that.getVertex(4)) { + setVertex(0, that.getVertex(0)); + //setVertex(1, this.getVertex(1)); // no change + //setVertex(2, this.getVertex(2)); // no change + //setVertex(3, this.getVertex(3)); // no change + //setVertex(4, that.getVertex(4)); // no change + setVertex(5, that.getVertex(5)); + return; // done + } + if (getVertex(2) == that.getVertex(1) && getVertex(3) == that.getVertex(4)) { + //setVertex(0, this.getVertex(0)); // no change + //setVertex(1, this.getVertex(1)); // no change + setVertex(2, that.getVertex(2)); + setVertex(3, that.getVertex(3)); + //setVertex(4, this.getVertex(4)); // no change + //setVertex(5, that.getVertex(5)); // no change + return; // done + } + if (getVertex(1) == that.getVertex(2) && getVertex(4) == that.getVertex(3)) { + setVertex(0, that.getVertex(0)); + setVertex(1, that.getVertex(1)); + //setVertex(2, this.getVertex(2)); // no change + //setVertex(3, that.getVertex(3)); // no change + setVertex(4, that.getVertex(4)); + setVertex(5, that.getVertex(5)); + return; // done + } + } + + // NEAR/BOTTOM + if ( + (getProjectionType() == that.getProjectionType()) && + ( + getProjectionType() == (PROJECTION_NEAR | PROJECTION_BOTTOM) + ) + ) { + if (getVertex(0) == that.getVertex(5) && getVertex(3) == that.getVertex(4)) { + setVertex(0, that.getVertex(0)); + setVertex(1, that.getVertex(1)); + setVertex(2, that.getVertex(2)); + setVertex(3, that.getVertex(3)); + //setVertex(4, this.getVertex(4)); // no change + //setVertex(5, that.getVertex(5)); // no change + return; // done + } + if (getVertex(5) == that.getVertex(0) && getVertex(4) == that.getVertex(3)) { + //setVertex(0, this.getVertex(0)); // no change + //setVertex(1, that.getVertex(1)); // no change + //setVertex(2, this.getVertex(2)); // no change + //setVertex(3, that.getVertex(3)); // no change + setVertex(4, that.getVertex(4)); + setVertex(5, that.getVertex(5)); + return; // done + } + if (getVertex(1) == that.getVertex(0) && getVertex(2) == that.getVertex(3)) { + //setVertex(0, this.getVertex(0)); // no change + setVertex(1, that.getVertex(1)); + setVertex(2, that.getVertex(2)); + //setVertex(3, that.getVertex(3)); // no change + //setVertex(4, this.getVertex(4)); // no change + //setVertex(5, that.getVertex(5)); // no change + return; // done + } + if (getVertex(0) == that.getVertex(1) && getVertex(3) == that.getVertex(2)) { + setVertex(0, that.getVertex(0)); + //setVertex(1, this.getVertex(1)); // no change + //setVertex(2, that.getVertex(2)); // no change + setVertex(3, that.getVertex(3)); + setVertex(4, that.getVertex(4)); + setVertex(5, that.getVertex(5)); + return; // done + } + } + + // NEAR/TOP + if ( + (getProjectionType() == that.getProjectionType()) && + ( + getProjectionType() == (PROJECTION_NEAR | PROJECTION_TOP) + ) + ) { + if (getVertex(0) == that.getVertex(5) && getVertex(1) == that.getVertex(2)) { + setVertex(0, that.getVertex(0)); + setVertex(1, that.getVertex(1)); + //setVertex(2, this.getVertex(2)); // no change + //setVertex(3, that.getVertex(3)); // no change + //setVertex(4, this.getVertex(4)); // no change + //setVertex(5, that.getVertex(5)); // no change + return; // done + } + if (getVertex(5) == that.getVertex(0) && getVertex(2) == that.getVertex(1)) { + //setVertex(0, this.getVertex(0)); // no change + //setVertex(1, that.getVertex(1)); // no change + setVertex(2, that.getVertex(2)); + setVertex(3, that.getVertex(3)); + setVertex(4, that.getVertex(4)); + setVertex(5, that.getVertex(5)); + return; // done + } + if (getVertex(4) == that.getVertex(5) && getVertex(3) == that.getVertex(2)) { + //setVertex(0, this.getVertex(0)); // no change + //setVertex(1, that.getVertex(1)); // no change + //setVertex(2, that.getVertex(2)); // no change + setVertex(3, that.getVertex(3)); + setVertex(4, that.getVertex(4)); + //setVertex(5, that.getVertex(5)); // no change + return; // done + } + if (getVertex(5) == that.getVertex(4) && getVertex(2) == that.getVertex(3)) { + setVertex(0, that.getVertex(0)); + setVertex(1, that.getVertex(1)); + setVertex(2, that.getVertex(2)); + //setVertex(3, this.getVertex(3)); // no change + //setVertex(4, that.getVertex(3)); // no change + setVertex(5, that.getVertex(5)); + return; // done + } + } + + + // RIGHT/NEAR & NEAR/RIGHT/TOP + // LEFT/NEAR & NEAR/LEFT/TOP + if ( + ((getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_TOP)) && + (that.getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR))) + || + ((getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR | PROJECTION_TOP)) && + (that.getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR))) + ) + { + if (getVertex(5) == that.getVertex(0) && getVertex(3) == that.getVertex(2)) { + //setVertex(0, this.getVertex(0)); // no change + //setVertex(1, this.getVertex(1)); // no change + //setVertex(2, this.getVertex(2)); // no change + setVertex(3, that.getVertex(3)); + setVertex(4, that.getVertex(4)); + setVertex(5, that.getVertex(5)); + setProjectionType((PROJECTION_RIGHT | PROJECTION_NEAR)); + return; // done + } + } + + // RIGHT/NEAR & NEAR/RIGHT/TOP + // LEFT/NEAR & NEAR/LEFT/TOP + if ( + ((that.getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_TOP)) && + (getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR))) + || + ((that.getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR | PROJECTION_TOP)) && + (getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR))) + + ) + { + if (getVertex(0) == that.getVertex(5) && getVertex(2) == that.getVertex(3)) { + setVertex(0, that.getVertex(0)); + setVertex(1, that.getVertex(1)); + setVertex(2, that.getVertex(2)); + //setVertex(3, this.getVertex(3)); // no change + //setVertex(4, this.getVertex(4)); // no change + //setVertex(5, this.getVertex(5)); // no change + //setProjectionType((PROJECTION_RIGHT | PROJECTION_NEAR)); // no change + return; // done + } + } + + // RIGHT/NEAR & NEAR/RIGHT/BOTTOM + // NEAR/LEFT & NEAR/LEFT/BOTTOM + if ( + ((that.getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_BOTTOM)) && + (getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR))) + || + ((that.getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR | PROJECTION_BOTTOM)) && + (getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR))) + + ) + { + if (getVertex(5) == that.getVertex(0) && getVertex(3) == that.getVertex(2)) { + //setVertex(0, this.getVertex(0)); // no change + //setVertex(1, this.getVertex(1)); // no change + //setVertex(2, this.getVertex(2)); // no change + setVertex(3, that.getVertex(3)); + setVertex(4, that.getVertex(4)); + setVertex(5, that.getVertex(5)); + //setProjectionType((PROJECTION_RIGHT | PROJECTION_NEAR)); // no change + return; // done + } + } + // RIGHT/NEAR & NEAR/RIGHT/BOTTOM + // NEAR/LEFT & NEAR/LEFT/BOTTOM + if ( + ((getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_BOTTOM)) && + (that.getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR))) + || + ((getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR | PROJECTION_BOTTOM)) && + (that.getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR))) + ) + { + if (getVertex(0) == that.getVertex(5) && getVertex(2) == that.getVertex(3)) { + setVertex(0, that.getVertex(0)); + setVertex(1, that.getVertex(1)); + setVertex(2, that.getVertex(2)); + //setVertex(3, this.getVertex(3)); // no change + //setVertex(4, this.getVertex(4)); // no change + //setVertex(5, this.getVertex(5)); // no change + setProjectionType((PROJECTION_RIGHT | PROJECTION_NEAR)); + return; // done + } + } + + + // NEAR/TOP & NEAR + if ( + (getProjectionType() == (PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_NEAR | PROJECTION_TOP )) + ) + { + if (getVertex(0) == that.getVertex(5) && getVertex(1) == that.getVertex(2)) { + setVertex(0, that.getVertex(0)); + setVertex(1, that.getVertex(1)); + //setVertex(2, this.getVertex(2)); // no change + //setVertex(3, this.getVertex(3)); // no change + //setVertexCount(4); // no change + //setProjectionType((PROJECTION_NEAR)); // no change + return; // done + } + } + + // NEAR/TOP & NEAR + if ( + (that.getProjectionType() == (PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_NEAR | PROJECTION_TOP )) + ) + { + if (getVertex(5) == that.getVertex(0) && getVertex(2) == that.getVertex(1)) { + //setVertex(0, this.getVertex(0)); // no change + //setVertex(1, this.getVertex(1)); // no change + setVertex(2, that.getVertex(2)); + setVertex(3, that.getVertex(3)); + setVertexCount(4); + setProjectionType((PROJECTION_NEAR)); + return; // done + } + } + + // NEAR/BOTTOM & NEAR + if ( + (getProjectionType() == (PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_NEAR | PROJECTION_BOTTOM )) + ) + { + if (getVertex(2) == that.getVertex(3) && getVertex(3) == that.getVertex(0)) { + //setVertex(0, this.getVertex(0)); // no change + //setVertex(1, this.getVertex(1)); // no change + setVertex(2, that.getVertex(4)); + setVertex(3, that.getVertex(5)); + //setVertexCount(4); // no change + //setProjectionType((PROJECTION_NEAR)); // no change + } + } + + // NEAR/BOTTOM & NEAR + if ( + (that.getProjectionType() == (PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_NEAR | PROJECTION_BOTTOM )) + ) + { + if (getVertex(3) == that.getVertex(2) && getVertex(0) == that.getVertex(3)) { + setVertex(0, that.getVertex(0)); + setVertex(1, that.getVertex(1)); + setVertex(2, getVertex(4)); + setVertex(3, getVertex(5)); + setVertexCount(4); + setProjectionType((PROJECTION_NEAR)); + return; // done + } + } + + // NEAR/RIGHT & NEAR + if ( + (getProjectionType() == (PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT )) + ) + { + if (getVertex(0) == that.getVertex(1) && getVertex(3) == that.getVertex(4)) { + setVertex(0, that.getVertex(0)); + //setVertex(1, this.getVertex(1)); // no change + //setVertex(2, this.getVertex(2)); // no change + setVertex(3, that.getVertex(5)); + //setVertexCount(4); // no change + //setProjectionType((PROJECTION_NEAR)); // no change + } + } + + // NEAR/RIGHT & NEAR + if ( + (that.getProjectionType() == (PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT )) + ) + { + if (getVertex(1) == that.getVertex(0) && getVertex(4) == that.getVertex(3)) { + //setVertex(0, this.getVertex(0)); // no change + setVertex(1, that.getVertex(1)); + setVertex(2, that.getVertex(2)); + setVertex(3, getVertex(5)); + setVertexCount(4); + setProjectionType((PROJECTION_NEAR)); + return; // done + } + } + + // NEAR/LEFT & NEAR + if ( + (getProjectionType() == (PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT )) + ) + { + if (getVertex(1) == that.getVertex(1) && getVertex(2) == that.getVertex(4)) { + //setVertex(0, this.getVertex()); // no change + setVertex(1, that.getVertex(2)); + setVertex(2, that.getVertex(3)); + //setVertex(3, this.getVertex(3)); // no change + //setVertexCount(4); // no change + //setProjectionType((PROJECTION_NEAR)); // no change + return; // done + } + } + + // NEAR/LEFT & NEAR + if ( + (that.getProjectionType() == (PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT )) + ) + { + if (getVertex(1) == that.getVertex(0) && getVertex(4) == that.getVertex(3)) { + setVertex(0, that.getVertex(0)); + setVertex(1, getVertex(2)); + setVertex(2, getVertex(3)); + setVertex(3, that.getVertex(3)); + setVertexCount(4); + setProjectionType((PROJECTION_NEAR)); + return; // done + } + } + + + // NEAR/RIGHT/BOTTOM & NEAR/BOTTOM + if ( + (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT )) + ) + { + if (getVertex(1) == that.getVertex(2) && getVertex(5) == that.getVertex(4)) { + setVertex(0, that.getVertex(0)); + setVertex(1, that.getVertex(1)); + //setVertex(2, this.getVertex(2)); // no change + //setVertex(3, this.getVertex(3)); // no change + //setVertex(4, this.getVertex(4)); // no change + setVertex(5, that.getVertex(5)); + return; // done + } + } + + // NEAR/RIGHT/BOTTOM & NEAR/BOTTOM + if ( + (that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT )) + ) + { + if (getVertex(2) == that.getVertex(1) && getVertex(4) == that.getVertex(5)) { + //setVertex(0, this.getVertex(0)); // no change + //setVertex(1, this.getVertex(1)); // no change + setVertex(2, that.getVertex(2)); + setVertex(3, that.getVertex(3)); + setVertex(4, that.getVertex(4)); + //setVertex(5, this.getVertex(5)); // no change + setProjectionType((PROJECTION_BOTTOM | PROJECTION_NEAR)); + return; // done + } + } + + + // RIGHT/NEAR/BOTTOM + // RIGHT/NEAR/TOP + // LEFT/NEAR/BOTTOM + // LEFT/NEAR/TOP + if ( + (getProjectionType() == that.getProjectionType()) && + ( + getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_BOTTOM ) || + getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_TOP ) || + getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR | PROJECTION_BOTTOM ) || + getProjectionType() == (PROJECTION_LEFT | PROJECTION_NEAR | PROJECTION_TOP ) + ) + ) { + if (getVertex(0) == that.getVertex(5) && getVertex(2) == that.getVertex(3)) { + setVertex(0, that.getVertex(0)); + setVertex(1, that.getVertex(1)); + setVertex(2, that.getVertex(2)); + //setVertex(3, this.getVertex(3)); // no change + //setVertex(4, this.getVertex(4)); // no change + //setVertex(5, this.getVertex(5)); // no change + return; // done + } + if (getVertex(5) == that.getVertex(0) && getVertex(3) == that.getVertex(2)) { + //setVertex(0, this.getVertex(0)); // no change + //setVertex(1, this.getVertex(1)); // no change + //setVertex(2, this.getVertex(2)); // no change + setVertex(3, that.getVertex(3)); + setVertex(4, that.getVertex(4)); + setVertex(5, that.getVertex(5)); + return; // done + } + if (getVertex(2) == that.getVertex(1) && getVertex(4) == that.getVertex(5)) { + //setVertex(0, this.getVertex(0)); // no change + //setVertex(1, this.getVertex(1)); // no change + setVertex(2, that.getVertex(2)); + setVertex(3, that.getVertex(3)); + setVertex(4, that.getVertex(4)); + //setVertex(5, this.getVertex(5)); // no change + return; // done + } + if (getVertex(1) == that.getVertex(2) && getVertex(5) == that.getVertex(4)) { + setVertex(0, that.getVertex(0)); + setVertex(1, that.getVertex(1)); + //setVertex(2, this.getVertex(2)); // no change + //setVertex(3, this.getVertex(3)); // no change + //setVertex(4, this.getVertex(4)); // no change + setVertex(5, that.getVertex(5)); + return; // done + } + // if this.([1],[3]) == that.([0],[4]) then create polygon: this.[0], that.[1], that.[2], that.[3], this.[4], this.[5] + if (getVertex(1) == that.getVertex(0) && getVertex(3) == that.getVertex(4)) { + //setVertex(0, this.getVertex(0)); // no change + setVertex(1, that.getVertex(1)); + setVertex(2, that.getVertex(2)); + setVertex(3, that.getVertex(3)); + //setVertex(4, this.getVertex(4)); // no change + //setVertex(5, this.getVertex(5)); // no change + return; // done + } + // if this.([0],[4]) == that.([1],[3]) then create polygon: that.[0], this.[1], this.[2], this.[3], that.[4], that.[5] + if (getVertex(0) == that.getVertex(1) && getVertex(4) == that.getVertex(3)) { + setVertex(0, that.getVertex(0)); + //setVertex(1, this.getVertex(1)); // no change + //setVertex(2, this.getVertex(2)); // no change + //setVertex(3, this.getVertex(3)); // no change + setVertex(4, that.getVertex(4)); + setVertex(5, that.getVertex(5)); + return; // done + } + } + +} diff --git a/libraries/voxels/src/VoxelProjectedPolygon.h b/libraries/voxels/src/VoxelProjectedPolygon.h index 0681ce4fc1..47d661a39d 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.h +++ b/libraries/voxels/src/VoxelProjectedPolygon.h @@ -49,6 +49,14 @@ private: bool _set; }; +const int PROJECTION_RIGHT = 1; +const int PROJECTION_LEFT = 2; +const int PROJECTION_BOTTOM = 4; +const int PROJECTION_TOP = 8; +const int PROJECTION_NEAR = 16; +const int PROJECTION_FAR = 32; +const int PROJECTION_CLIPPED = 64; + class VoxelProjectedPolygon { public: @@ -64,16 +72,18 @@ public: const ProjectedVertices& getVertices() const { return _vertices; }; const glm::vec2& getVertex(int i) const { return _vertices[i]; }; void setVertex(int vertex, const glm::vec2& point); - int getVertexCount() const { return _vertexCount; }; - void setVertexCount(int vertexCount) { _vertexCount = vertexCount; }; - float getDistance() const { return _distance; } - void setDistance(float distance) { _distance = distance; } + int getVertexCount() const { return _vertexCount; }; + void setVertexCount(int vertexCount) { _vertexCount = vertexCount; }; + float getDistance() const { return _distance; } + void setDistance(float distance) { _distance = distance; } + bool getAnyInView() const { return _anyInView; }; + void setAnyInView(bool anyInView) { _anyInView = anyInView; }; + bool getAllInView() const { return _allInView; }; + void setAllInView(bool allInView) { _allInView = allInView; }; + void setProjectionType(unsigned char type) { _projectionType = type; }; + unsigned char getProjectionType() const { return _projectionType; }; - bool getAnyInView() const { return _anyInView; }; - void setAnyInView(bool anyInView) { _anyInView = anyInView; }; - bool getAllInView() const { return _allInView; }; - void setAllInView(bool allInView) { _allInView = allInView; }; bool pointInside(const glm::vec2& point, bool* matchesVertex = NULL) const; bool occludes(const VoxelProjectedPolygon& occludee, bool checkAllInView = false) const; @@ -82,8 +92,10 @@ public: bool intersects(const BoundingBox& box) const; bool matches(const VoxelProjectedPolygon& testee) const; bool matches(const BoundingBox& testee) const; - bool intersectsOnAxes(const VoxelProjectedPolygon& testee) const; + + bool canMerge(const VoxelProjectedPolygon& that) const; + void merge(const VoxelProjectedPolygon& that); // replaces vertices of this with new merged version float getMaxX() const { return _maxX; } float getMaxY() const { return _maxY; } @@ -109,6 +121,7 @@ private: float _distance; bool _anyInView; // if any points are in view bool _allInView; // if all points are in view + unsigned char _projectionType; }; From be88f07678b582b2849bb135231048b970d228ae Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 2 Jul 2013 11:47:30 -0700 Subject: [PATCH 10/55] more progress on merging --- .../voxels/src/VoxelProjectedPolygon.cpp | 76 ++++++++++++++++--- 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/libraries/voxels/src/VoxelProjectedPolygon.cpp b/libraries/voxels/src/VoxelProjectedPolygon.cpp index 07a4d04c48..7a127dece7 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.cpp +++ b/libraries/voxels/src/VoxelProjectedPolygon.cpp @@ -585,9 +585,10 @@ bool VoxelProjectedPolygon::canMerge(const VoxelProjectedPolygon& that) const { // NEAR/RIGHT/BOTTOM & NEAR/BOTTOM + // NEAR/LEFT/BOTTOM & NEAR/BOTTOM if ( - (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && - (that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT )) + ((getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT ))) ) { if (getVertex(1) == that.getVertex(2) && getVertex(5) == that.getVertex(4)) { @@ -596,17 +597,38 @@ bool VoxelProjectedPolygon::canMerge(const VoxelProjectedPolygon& that) const { } // NEAR/RIGHT/BOTTOM & NEAR/BOTTOM + // NEAR/LEFT/BOTTOM & NEAR/BOTTOM if ( - (that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && - (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT )) + ((that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT ))) ) { if (getVertex(2) == that.getVertex(1) && getVertex(4) == that.getVertex(5)) { return true; } } + + // NEAR/LEFT/BOTTOM & NEAR/BOTTOM + if ( + ((getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_LEFT ))) + ) + { + if (getVertex(2) == that.getVertex(0) && getVertex(4) == that.getVertex(4)) { + return true; + } + } - + // NEAR/LEFT/BOTTOM & NEAR/BOTTOM + if ( + ((that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_LEFT ))) + ) + { + if (getVertex(0) == that.getVertex(2) && getVertex(4) == that.getVertex(4)) { + return true; + } + } // RIGHT/NEAR/BOTTOM // RIGHT/NEAR/TOP // LEFT/NEAR/BOTTOM @@ -1011,8 +1033,8 @@ void VoxelProjectedPolygon::merge(const VoxelProjectedPolygon& that) { // NEAR/RIGHT/BOTTOM & NEAR/BOTTOM if ( - (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && - (that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT )) + ((getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT ))) ) { if (getVertex(1) == that.getVertex(2) && getVertex(5) == that.getVertex(4)) { @@ -1028,8 +1050,8 @@ void VoxelProjectedPolygon::merge(const VoxelProjectedPolygon& that) { // NEAR/RIGHT/BOTTOM & NEAR/BOTTOM if ( - (that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && - (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT )) + ((that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT ))) ) { if (getVertex(2) == that.getVertex(1) && getVertex(4) == that.getVertex(5)) { @@ -1044,6 +1066,42 @@ void VoxelProjectedPolygon::merge(const VoxelProjectedPolygon& that) { } } + // NEAR/LEFT/BOTTOM & NEAR/BOTTOM + if ( + ((getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_LEFT ))) + ) + { + if (getVertex(2) == that.getVertex(0) && getVertex(4) == that.getVertex(4)) { + //setVertex(0, this.getVertex(0)); // no change + //setVertex(1, this.getVertex(1)); // no change + setVertex(2, that.getVertex(1)); + setVertex(3, that.getVertex(2)); + setVertex(4, that.getVertex(3)); + //setVertex(5, this.getVertex(5)); // no change + return; // done + } + } + + // NEAR/LEFT/BOTTOM & NEAR/BOTTOM + if ( + ((that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_LEFT ))) + ) + { + if (getVertex(0) == that.getVertex(2) && getVertex(4) == that.getVertex(4)) { + // we need to do this in an unusual order, because otherwise we'd overwrite our own values + setVertex(4, getVertex(3)); + setVertex(3, getVertex(2)); + setVertex(2, getVertex(1)); + setVertex(0, that.getVertex(0)); + setVertex(1, that.getVertex(1)); + setVertex(5, that.getVertex(5)); + setProjectionType((PROJECTION_BOTTOM | PROJECTION_NEAR)); + return; // done + } + } + // RIGHT/NEAR/BOTTOM // RIGHT/NEAR/TOP From c7ff0736868b182da685e963d77f3335d6abd0d6 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 2 Jul 2013 15:50:40 -0700 Subject: [PATCH 11/55] more progress on merging --- interface/src/Application.cpp | 12 ++-- libraries/voxels/src/CoverageMap.cpp | 2 +- libraries/voxels/src/ViewFrustum.cpp | 4 ++ .../voxels/src/VoxelProjectedPolygon.cpp | 68 +++++++++++++++++-- 4 files changed, 74 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4b5faaf4ed..628396a36a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2478,26 +2478,26 @@ void Application::renderCoverageMapsRecursively(CoverageMap* map) { if (polygon->getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_BOTTOM)) { glColor3f(.5,0,0); // dark red - } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT | PROJECTION_TOP)) { - glColor3f(0,.5,0); // dark green } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT)) { glColor3f(.5,.5,0); // dark yellow } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT)) { glColor3f(.5,.5,.5); // gray - } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT | PROJECTION_TOP)) { - glColor3f(0,0,1); // Blue } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT | PROJECTION_BOTTOM)) { glColor3f(.5,0,.5); // dark magenta } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_BOTTOM)) { - glColor3f(1,0,0); // red + glColor3f(.75,0,0); // red } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_TOP)) { glColor3f(1,0,1); // magenta + } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT | PROJECTION_TOP)) { + glColor3f(0,0,1); // Blue + } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT | PROJECTION_TOP)) { + glColor3f(0,1,0); // green } else if (polygon->getProjectionType() == (PROJECTION_NEAR)) { glColor3f(1,1,0); // yellow } else if (polygon->getProjectionType() == (PROJECTION_FAR | PROJECTION_RIGHT | PROJECTION_BOTTOM)) { glColor3f(0,.5,.5); // dark cyan } else { - glColor3f(0,0,1); + glColor3f(1,0,0); } glm::vec2 firstPoint = getScaledScreenPoint(polygon->getVertex(0)); diff --git a/libraries/voxels/src/CoverageMap.cpp b/libraries/voxels/src/CoverageMap.cpp index 82c529ded6..d6986aed28 100644 --- a/libraries/voxels/src/CoverageMap.cpp +++ b/libraries/voxels/src/CoverageMap.cpp @@ -516,7 +516,7 @@ CoverageMapStorageResult CoverageRegion::checkRegion(VoxelProjectedPolygon* poly if (polygonAtThisLevel->getDistance() >= polygon->getDistance()) { _outOfOrderPolygon++; if (storeIt) { - if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) { + if (true || polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) { //printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area()); storeInArray(polygon); return STORED; diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index b2976c8907..3f94a34803 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -541,6 +541,10 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { //printLog(">>>>>>>>> ViewFrustum::getProjectedPolygon() lookup=%d\n",lookUp); int vertexCount = hullVertexLookup[lookUp][0]; //look up number of vertices + + if (vertexCount == 0) { + printLog(">>>>>>>>> ViewFrustum::getProjectedPolygon() lookup=%d vertexCount=%d\n", lookUp, vertexCount); + } VoxelProjectedPolygon projectedPolygon(vertexCount); diff --git a/libraries/voxels/src/VoxelProjectedPolygon.cpp b/libraries/voxels/src/VoxelProjectedPolygon.cpp index 7a127dece7..efccf88c7a 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.cpp +++ b/libraries/voxels/src/VoxelProjectedPolygon.cpp @@ -370,6 +370,9 @@ bool VoxelProjectedPolygon::intersectsOnAxes(const VoxelProjectedPolygon& testee } bool VoxelProjectedPolygon::canMerge(const VoxelProjectedPolygon& that) const { + +//return false; + // RIGHT/NEAR // LEFT/NEAR if ( @@ -582,12 +585,33 @@ bool VoxelProjectedPolygon::canMerge(const VoxelProjectedPolygon& that) const { return true; } } + + // NEAR/RIGHT/TOP & NEAR/TOP + if ( + ((getProjectionType() == (PROJECTION_TOP | PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_TOP | PROJECTION_NEAR | PROJECTION_RIGHT ))) + ) + { + if (getVertex(0) == that.getVertex(1) && getVertex(4) == that.getVertex(3)) { + return true; + } + } + + // NEAR/RIGHT/TOP & NEAR/TOP + if ( + ((that.getProjectionType() == (PROJECTION_TOP | PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_TOP | PROJECTION_NEAR | PROJECTION_RIGHT ))) + ) + { + if (getVertex(1) == that.getVertex(0) && getVertex(3) == that.getVertex(4)) { + return true; + } + } // NEAR/RIGHT/BOTTOM & NEAR/BOTTOM - // NEAR/LEFT/BOTTOM & NEAR/BOTTOM if ( - ((getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && + ((getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && (that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT ))) ) { @@ -597,7 +621,6 @@ bool VoxelProjectedPolygon::canMerge(const VoxelProjectedPolygon& that) const { } // NEAR/RIGHT/BOTTOM & NEAR/BOTTOM - // NEAR/LEFT/BOTTOM & NEAR/BOTTOM if ( ((that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT ))) @@ -1030,10 +1053,45 @@ void VoxelProjectedPolygon::merge(const VoxelProjectedPolygon& that) { } } + // NEAR/RIGHT/TOP & NEAR/TOP + if ( + ((getProjectionType() == (PROJECTION_TOP | PROJECTION_NEAR )) && + (that.getProjectionType() == (PROJECTION_TOP | PROJECTION_NEAR | PROJECTION_RIGHT ))) + ) + { + if (getVertex(0) == that.getVertex(1) && getVertex(4) == that.getVertex(3)) { + setVertex(0, that.getVertex(0)); + //setVertex(1, this.getVertex(1)); // no change + //setVertex(2, this.getVertex(2)); // no change + //setVertex(3, this.getVertex(3)); // no change + setVertex(4, that.getVertex(4)); + setVertex(5, that.getVertex(5)); + return; // done + } + } + + // NEAR/RIGHT/TOP & NEAR/TOP + if ( + ((that.getProjectionType() == (PROJECTION_TOP | PROJECTION_NEAR )) && + (getProjectionType() == (PROJECTION_TOP | PROJECTION_NEAR | PROJECTION_RIGHT ))) + ) + { + if (getVertex(1) == that.getVertex(0) && getVertex(3) == that.getVertex(4)) { + //setVertex(0, this.getVertex(0)); // no change + setVertex(1, that.getVertex(1)); + setVertex(2, that.getVertex(2)); + setVertex(3, that.getVertex(3)); + //setVertex(4, this.getVertex(4)); // no change + //setVertex(5, this.getVertex(5)); // no change + setProjectionType((PROJECTION_TOP | PROJECTION_NEAR)); + return; // done + } + } + // NEAR/RIGHT/BOTTOM & NEAR/BOTTOM if ( - ((getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && + ((getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && (that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT ))) ) { @@ -1051,7 +1109,7 @@ void VoxelProjectedPolygon::merge(const VoxelProjectedPolygon& that) { // NEAR/RIGHT/BOTTOM & NEAR/BOTTOM if ( ((that.getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR )) && - (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT ))) + (getProjectionType() == (PROJECTION_BOTTOM | PROJECTION_NEAR | PROJECTION_RIGHT ))) ) { if (getVertex(2) == that.getVertex(1) && getVertex(4) == that.getVertex(5)) { From f45f0610f545fa7d7a0eef114d866fb8f0c81faa Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 2 Jul 2013 16:54:55 -0700 Subject: [PATCH 12/55] fixed FOV preference with projection matrix --- interface/src/Application.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index afb9e9a2f7..8dc2e6007c 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -432,6 +432,9 @@ void Application::paintGL() { void Application::resizeGL(int width, int height) { float aspectRatio = ((float)width/(float)height); // based on screen resize + // reset the camera FOV to our preference... + _myCamera.setFieldOfView(_horizontalFieldOfView); + // get the lens details from the current camera Camera& camera = _viewFrustumFromOffset->isChecked() ? _viewFrustumOffsetCamera : _myCamera; float nearClip = camera.getNearClip(); From c795a37be2ca52562834156dcb28d613ffd98916 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 3 Jul 2013 10:55:33 -0700 Subject: [PATCH 13/55] tweak perfstats to not display WARNING! when caller asked it to always print (non-warning case) --- libraries/shared/src/PerfStat.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/PerfStat.cpp b/libraries/shared/src/PerfStat.cpp index c376cfcb3e..9bceb0b790 100644 --- a/libraries/shared/src/PerfStat.cpp +++ b/libraries/shared/src/PerfStat.cpp @@ -110,12 +110,12 @@ PerformanceWarning::~PerformanceWarning() { if ((_alwaysDisplay || _renderWarningsOn) && elapsedmsec > 1) { if (elapsedmsec > 1000) { double elapsedsec = (end - _start) / 1000000.0; - printLog("WARNING! %s took %lf seconds\n", _message, elapsedsec); + printLog("%s%s took %lf seconds\n", (_alwaysDisplay ? "" : "WARNING!"), _message, elapsedsec); } else { - printLog("WARNING! %s took %lf milliseconds\n", _message, elapsedmsec); + printLog("%s%s took %lf milliseconds\n", (_alwaysDisplay ? "" : "WARNING!"), _message, elapsedmsec); } } else if (_alwaysDisplay) { - printLog("WARNING! %s took %lf milliseconds\n", _message, elapsedmsec); + printLog("%s took %lf milliseconds\n", _message, elapsedmsec); } }; From a05650e74a21d4ce83599bbeb27013098f84ae02 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 3 Jul 2013 10:56:08 -0700 Subject: [PATCH 14/55] removed debug code --- libraries/voxels/src/ViewFrustum.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index 3f94a34803..7b7b0d2894 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -542,10 +542,6 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { int vertexCount = hullVertexLookup[lookUp][0]; //look up number of vertices - if (vertexCount == 0) { - printLog(">>>>>>>>> ViewFrustum::getProjectedPolygon() lookup=%d vertexCount=%d\n", lookUp, vertexCount); - } - VoxelProjectedPolygon projectedPolygon(vertexCount); bool pointInView = true; From 5ef2e2de5deb1c96a897ff202712091947778cd3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 3 Jul 2013 10:58:30 -0700 Subject: [PATCH 15/55] debug statistics for intersect calls --- libraries/voxels/src/VoxelProjectedPolygon.cpp | 6 +++--- libraries/voxels/src/VoxelProjectedPolygon.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/voxels/src/VoxelProjectedPolygon.cpp b/libraries/voxels/src/VoxelProjectedPolygon.cpp index efccf88c7a..61cbe6574e 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.cpp +++ b/libraries/voxels/src/VoxelProjectedPolygon.cpp @@ -99,6 +99,7 @@ void BoundingBox::printDebugDetails(const char* label) const { long VoxelProjectedPolygon::pointInside_calls = 0; long VoxelProjectedPolygon::occludes_calls = 0; +long VoxelProjectedPolygon::intersects_calls = 0; VoxelProjectedPolygon::VoxelProjectedPolygon(const BoundingBox& box) : @@ -321,11 +322,12 @@ void VoxelProjectedPolygon::printDebugDetails() const { bool VoxelProjectedPolygon::intersects(const BoundingBox& box) const { VoxelProjectedPolygon testee(box); - return intersectsOnAxes(testee) && testee.intersectsOnAxes(*this); + return intersects(testee); } bool VoxelProjectedPolygon::intersects(const VoxelProjectedPolygon& testee) const { + VoxelProjectedPolygon::intersects_calls++; return intersectsOnAxes(testee) && testee.intersectsOnAxes(*this); } @@ -371,8 +373,6 @@ bool VoxelProjectedPolygon::intersectsOnAxes(const VoxelProjectedPolygon& testee bool VoxelProjectedPolygon::canMerge(const VoxelProjectedPolygon& that) const { -//return false; - // RIGHT/NEAR // LEFT/NEAR if ( diff --git a/libraries/voxels/src/VoxelProjectedPolygon.h b/libraries/voxels/src/VoxelProjectedPolygon.h index 47d661a39d..5846b3d34d 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.h +++ b/libraries/voxels/src/VoxelProjectedPolygon.h @@ -110,6 +110,7 @@ public: static long pointInside_calls; static long occludes_calls; + static long intersects_calls; private: int _vertexCount; From 2d6e522c35c28a7f0cbb6b26c8aca906c9c546ee Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 3 Jul 2013 10:59:36 -0700 Subject: [PATCH 16/55] debug stats --- interface/src/VoxelSystem.cpp | 25 ++++++++++++++----------- libraries/voxels/src/VoxelTree.cpp | 4 +++- libraries/voxels/src/VoxelTree.h | 5 +++-- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index c35ac952b0..84c02d1d61 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -1090,13 +1090,13 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { args.expectedMax = _voxelsInWriteArrays; _tree->recurseTreeWithOperation(collectStatsForTreesAndVBOsOperation,&args); - printLog("_voxelsDirty=%s _voxelsInWriteArrays=%ld minDirty=%ld maxDirty=%ld \n", debug::valueOf(_voxelsDirty), - _voxelsInWriteArrays, minDirty, maxDirty); - - printLog("stats: total %ld, leaves %ld, dirty %ld, colored %ld, shouldRender %ld, inVBO %ld\n", + printLog("Local Voxel Tree Statistics:\n total nodes %ld \n leaves %ld \n dirty %ld \n colored %ld \n shouldRender %ld \n", args.totalNodes, args.leafNodes, args.dirtyNodes, args.coloredNodes, args.shouldRenderNodes); - printLog("inVBO %ld, nodesInVBOOverExpectedMax %ld, duplicateVBOIndex %ld, nodesInVBONotShouldRender %ld\n", + printLog(" _voxelsDirty=%s \n _voxelsInWriteArrays=%ld \n minDirty=%ld \n maxDirty=%ld \n", debug::valueOf(_voxelsDirty), + _voxelsInWriteArrays, minDirty, maxDirty); + + printLog(" inVBO %ld \n nodesInVBOOverExpectedMax %ld \n duplicateVBOIndex %ld \n nodesInVBONotShouldRender %ld \n", args.nodesInVBO, args.nodesInVBOOverExpectedMax, args.duplicateVBOIndex, args.nodesInVBONotShouldRender); glBufferIndex minInVBO = GLBUFFER_INDEX_UNKNOWN; @@ -1109,7 +1109,7 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { } } - printLog("minInVBO=%ld maxInVBO=%ld _voxelsInWriteArrays=%ld _voxelsInReadArrays=%ld\n", + printLog(" minInVBO=%ld \n maxInVBO=%ld \n _voxelsInWriteArrays=%ld \n _voxelsInReadArrays=%ld \n", minInVBO, maxInVBO, _voxelsInWriteArrays, _voxelsInReadArrays); } @@ -1289,19 +1289,21 @@ void VoxelSystem::falseColorizeOccluded() { VoxelProjectedPolygon::pointInside_calls = 0; VoxelProjectedPolygon::occludes_calls = 0; + VoxelProjectedPolygon::intersects_calls = 0; glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE); _tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, position, (void*)&args); - printLog("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n", + printLog("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n", position.x, position.y, args.totalVoxels, args.coloredVoxels, args.occludedVoxels, args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped, args.stagedForDeletion, args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded, VoxelProjectedPolygon::pointInside_calls, - VoxelProjectedPolygon::occludes_calls + VoxelProjectedPolygon::occludes_calls, + VoxelProjectedPolygon::intersects_calls ); @@ -1397,7 +1399,7 @@ void VoxelSystem::falseColorizeOccludedV2() { VoxelProjectedPolygon::pointInside_calls = 0; VoxelProjectedPolygon::occludes_calls = 0; - + VoxelProjectedPolygon::intersects_calls = 0; FalseColorizeOccludedArgs args; args.viewFrustum = Application::getInstance()->getViewFrustum(); @@ -1418,14 +1420,15 @@ void VoxelSystem::falseColorizeOccludedV2() { _tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedV2Operation, position, (void*)&args); - printLog("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n", + printLog("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n", position.x, position.y, args.totalVoxels, args.coloredVoxels, args.occludedVoxels, args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped, args.stagedForDeletion, args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded, VoxelProjectedPolygon::pointInside_calls, - VoxelProjectedPolygon::occludes_calls + VoxelProjectedPolygon::occludes_calls, + VoxelProjectedPolygon::intersects_calls ); //myCoverageMapV2.erase(); diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index b16425370e..083765aa0b 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1097,6 +1097,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp // Keep track of how deep we've encoded. currentEncodeLevel++; + + params.maxLevelReached = std::max(currentEncodeLevel,params.maxLevelReached); // If we've reached our max Search Level, then stop searching. if (currentEncodeLevel >= params.maxEncodeLevel) { @@ -1358,7 +1360,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp int childTreeBytesOut = encodeTreeBitstreamRecursion(childNode, outputBuffer, availableBytes, bag, params, thisLevel); - + // remember this for reshuffling recursiveSliceSizes[originalIndex] = childTreeBytesOut; allSlicesSize += childTreeBytesOut; diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 72ea9e2326..3d87fbc557 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -36,6 +36,7 @@ typedef enum {GRADIENT, RANDOM, NATURAL} creationMode; class EncodeBitstreamParams { public: int maxEncodeLevel; + int maxLevelReached; const ViewFrustum* viewFrustum; bool includeColor; bool includeExistsBits; @@ -47,7 +48,7 @@ public: EncodeBitstreamParams( int maxEncodeLevel = INT_MAX, - const ViewFrustum* viewFrustum = IGNORE_VIEW_FRUSTUM, + const ViewFrustum* viewFrustum = IGNORE_VIEW_FRUSTUM, bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS, int chopLevels = 0, @@ -55,8 +56,8 @@ public: const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM, bool wantOcclusionCulling= NO_OCCLUSION_CULLING, CoverageMap* map = IGNORE_COVERAGE_MAP) : - maxEncodeLevel (maxEncodeLevel), + maxLevelReached (0), viewFrustum (viewFrustum), includeColor (includeColor), includeExistsBits (includeExistsBits), From dc4a5ea276c294033a85c726f6e58943417ddb29 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 3 Jul 2013 12:42:34 -0700 Subject: [PATCH 17/55] added some timing debugging, and bail early if voxel server is slow (cherry picked from commit 485c603d4fe8dbd0bedc8ac2dec717cece2e7076) --- voxel-server/src/VoxelAgentData.cpp | 3 ++- voxel-server/src/VoxelAgentData.h | 5 ++++ voxel-server/src/main.cpp | 38 +++++++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/voxel-server/src/VoxelAgentData.cpp b/voxel-server/src/VoxelAgentData.cpp index 289b1161e6..98990011da 100644 --- a/voxel-server/src/VoxelAgentData.cpp +++ b/voxel-server/src/VoxelAgentData.cpp @@ -16,7 +16,8 @@ VoxelAgentData::VoxelAgentData(Agent* owningAgent) : _viewSent(false), _voxelPacketAvailableBytes(MAX_VOXEL_PACKET_SIZE), _maxSearchLevel(1), - _maxLevelReachedInLastSearch(1) + _maxLevelReachedInLastSearch(1), + _lastTimeBagEmpty(0) { _voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE]; _voxelPacketAt = _voxelPacket; diff --git a/voxel-server/src/VoxelAgentData.h b/voxel-server/src/VoxelAgentData.h index 6606530b94..e26b85151c 100644 --- a/voxel-server/src/VoxelAgentData.h +++ b/voxel-server/src/VoxelAgentData.h @@ -50,6 +50,10 @@ public: bool getViewSent() const { return _viewSent; }; void setViewSent(bool viewSent) { _viewSent = viewSent; } + long long getLastTimeBagEmpty() const { return _lastTimeBagEmpty; }; + void setLastTimeBagEmpty(long long now) { _lastTimeBagEmpty = now; }; + + private: VoxelAgentData(const VoxelAgentData &); VoxelAgentData& operator= (const VoxelAgentData&); @@ -63,6 +67,7 @@ private: int _maxLevelReachedInLastSearch; ViewFrustum _currentViewFrustum; ViewFrustum _lastKnownViewFrustum; + long long _lastTimeBagEmpty; }; diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index a73a8dd09a..e21f46f6ca 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -46,6 +46,7 @@ const float MAX_CUBE = 0.05f; const int VOXEL_SEND_INTERVAL_USECS = 100 * 1000; int PACKETS_PER_CLIENT_PER_INTERVAL = 30; +const int SENDING_TIME_TO_SPARE = 20 * 1000; // usec of sending interval to spare for calculating voxels const int MAX_VOXEL_TREE_DEPTH_LEVELS = 4; @@ -263,6 +264,23 @@ void deepestLevelVoxelDistributor(AgentList* agentList, // If the current view frustum has changed OR we have nothing to send, then search against // the current view frustum for things to send. if (viewFrustumChanged || agentData->nodeBag.isEmpty()) { + if (::debugVoxelSending) { + printf("(viewFrustumChanged=%s || agentData->nodeBag.isEmpty() =%s)...\n", + debug::valueOf(viewFrustumChanged),debug::valueOf(agentData->nodeBag.isEmpty())); + long long now = usecTimestampNow(); + if (agentData->getLastTimeBagEmpty() > 0) { + float elapsedSceneSend = (now - agentData->getLastTimeBagEmpty()) / 1000000.0f; + printf(" elapsed time to send scene = %f seconds\n", elapsedSceneSend); + } + agentData->setLastTimeBagEmpty(now); + } + + // if our view has changed, we need to reset these things... + if (viewFrustumChanged) { + agentData->nodeBag.deleteAll(); + agentData->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 @@ -310,6 +328,22 @@ void deepestLevelVoxelDistributor(AgentList* agentList, bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { + + // Check to see if we're taking too long, and if so bail early... + long long now = usecTimestampNow(); + long elapsedUsec = (now - start); + long elapsedUsecPerPacket = (truePacketsSent == 0) ? 0 : (elapsedUsec / truePacketsSent); + long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec); + + if (elapsedUsecPerPacket + SENDING_TIME_TO_SPARE > usecRemaining) { + if (::debugVoxelSending) { + printf("packetLoop() usecRemaining=%ld bailing early took %ld usecs to generate %d bytes in %d packets (%ld usec avg), %d nodes still to send\n", + usecRemaining, elapsedUsec, trueBytesSent, truePacketsSent, elapsedUsecPerPacket, + agentData->nodeBag.count()); + } + break; + } + if (!agentData->nodeBag.isEmpty()) { VoxelNode* subTree = agentData->nodeBag.extract(); @@ -322,7 +356,7 @@ void deepestLevelVoxelDistributor(AgentList* agentList, bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, agentData->nodeBag, params); - + if (agentData->getAvailable() >= bytesWritten) { agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten); } else { @@ -379,7 +413,7 @@ void deepestLevelVoxelDistributor(AgentList* agentList, if (agentData->nodeBag.isEmpty()) { agentData->updateLastKnownViewFrustum(); agentData->setViewSent(true); - agentData->map.erase(); + agentData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes } From 873144ded360112d26c77f6426905f6ac3b27658 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 3 Jul 2013 14:40:59 -0700 Subject: [PATCH 18/55] tweaks to debug messages --- voxel-server/src/main.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index e21f46f6ca..29f1c2c72e 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -270,7 +270,13 @@ void deepestLevelVoxelDistributor(AgentList* agentList, long long now = usecTimestampNow(); if (agentData->getLastTimeBagEmpty() > 0) { float elapsedSceneSend = (now - agentData->getLastTimeBagEmpty()) / 1000000.0f; - printf(" elapsed time to send scene = %f seconds\n", elapsedSceneSend); + + if (viewFrustumChanged) { + printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); + } else { + printf("elapsed time to send scene = %f seconds", elapsedSceneSend); + } + printf(" [occlusionCulling: %s]\n", debug::valueOf(agentData->getWantOcclusionCulling())); } agentData->setLastTimeBagEmpty(now); } From e4579fe6c6de9c27453ad1608160b0d83a8575ea Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 3 Jul 2013 15:28:39 -0700 Subject: [PATCH 19/55] make regions and submaps configurable --- libraries/voxels/src/CoverageMap.cpp | 92 ++++++++++++++-------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/libraries/voxels/src/CoverageMap.cpp b/libraries/voxels/src/CoverageMap.cpp index d6986aed28..67e8387f5b 100644 --- a/libraries/voxels/src/CoverageMap.cpp +++ b/libraries/voxels/src/CoverageMap.cpp @@ -196,25 +196,26 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b bool fitsInAHalf = false; // Check each half of the box independently -/** - if (_topHalf.contains(polygonBox)) { - result = _topHalf.checkRegion(polygon, polygonBox, storeIt); - storeIn = &_topHalf; - fitsInAHalf = true; - } else if (_bottomHalf.contains(polygonBox)) { - result = _bottomHalf.checkRegion(polygon, polygonBox, storeIt); - storeIn = &_bottomHalf; - fitsInAHalf = true; - } else if (_leftHalf.contains(polygonBox)) { - result = _leftHalf.checkRegion(polygon, polygonBox, storeIt); - storeIn = &_leftHalf; - fitsInAHalf = true; - } else if (_rightHalf.contains(polygonBox)) { - result = _rightHalf.checkRegion(polygon, polygonBox, storeIt); - storeIn = &_rightHalf; - fitsInAHalf = true; + const bool useRegions = true; // for now we will continue to use regions + if (useRegions) { + if (_topHalf.contains(polygonBox)) { + result = _topHalf.checkRegion(polygon, polygonBox, storeIt); + storeIn = &_topHalf; + fitsInAHalf = true; + } else if (_bottomHalf.contains(polygonBox)) { + result = _bottomHalf.checkRegion(polygon, polygonBox, storeIt); + storeIn = &_bottomHalf; + fitsInAHalf = true; + } else if (_leftHalf.contains(polygonBox)) { + result = _leftHalf.checkRegion(polygon, polygonBox, storeIt); + storeIn = &_leftHalf; + fitsInAHalf = true; + } else if (_rightHalf.contains(polygonBox)) { + result = _rightHalf.checkRegion(polygon, polygonBox, storeIt); + storeIn = &_rightHalf; + fitsInAHalf = true; + } } -**/ // if we got this far, there are one of two possibilities, either a polygon doesn't fit // in one of the halves, or it did fit, but it wasn't occluded by anything only in that @@ -241,37 +242,38 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b // if we made it here, then it means the polygon being stored is not occluded // at this level of the quad tree, so we can continue to insert it into the map. // First we check to see if it fits in any of our sub maps -if (false) { - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - BoundingBox childMapBoundingBox = getChildBoundingBox(i); - if (childMapBoundingBox.contains(polygon->getBoundingBox())) { - // if no child map exists yet, then create it - if (!_childMaps[i]) { - _childMaps[i] = new CoverageMap(childMapBoundingBox, NOT_ROOT, _managePolygons); - } - result = _childMaps[i]->checkMap(polygon, storeIt); + const bool useChildMaps = true; // for now we will continue to use child maps + if (useChildMaps) { + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + BoundingBox childMapBoundingBox = getChildBoundingBox(i); + if (childMapBoundingBox.contains(polygon->getBoundingBox())) { + // if no child map exists yet, then create it + if (!_childMaps[i]) { + _childMaps[i] = new CoverageMap(childMapBoundingBox, NOT_ROOT, _managePolygons); + } + result = _childMaps[i]->checkMap(polygon, storeIt); - /* - switch (result) { - case STORED: - printLog("checkMap() = STORED\n"); - break; - case NOT_STORED: - printLog("checkMap() = NOT_STORED\n"); - break; - case OCCLUDED: - printLog("checkMap() = OCCLUDED\n"); - break; - default: - printLog("checkMap() = ????? \n"); - break; - } - */ + /* + switch (result) { + case STORED: + printLog("checkMap() = STORED\n"); + break; + case NOT_STORED: + printLog("checkMap() = NOT_STORED\n"); + break; + case OCCLUDED: + printLog("checkMap() = OCCLUDED\n"); + break; + default: + printLog("checkMap() = ????? \n"); + break; + } + */ - return result; + return result; + } } } -} // if we got this far, then the polygon is in our bounding box, but doesn't fit in // any of our child bounding boxes, so we should add it here. if (storeIt) { From 1f0df9a94501a0ab3a09ebd765faf96aaf062093 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 8 Jul 2013 20:52:14 -0700 Subject: [PATCH 20/55] added scanTreeWithOcclusion() for testing --- voxel-server/src/main.cpp | 217 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 678cf98348..5bdab5b742 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -65,6 +65,9 @@ bool wantSearchForColoredNodes = false; EnvironmentData environmentData[3]; +void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMap* coverageMap); + + void randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) { // randomly generate children for this node // the first level of the tree (where levelsToGo = MAX_VOXEL_TREE_DEPTH_LEVELS) has all 8 @@ -303,6 +306,10 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, } else { nodeData->nodeBag.insert(serverTree.rootNode); } + + + // hack, just test scanning the tree here! + //scanTreeWithOcclusion(&serverTree, &nodeData->getCurrentViewFrustum(), &nodeData->map); } long long end = usecTimestampNow(); @@ -753,3 +760,213 @@ int main(int argc, const char * argv[]) { return 0; } + + + +struct ScanTreeArgs { + ViewFrustum* viewFrustum; + CoverageMap* map; +// CoverageMapV2* mapV2; + VoxelTree* tree; + long totalVoxels; + long coloredVoxels; + long occludedVoxels; + long notOccludedVoxels; + + long outOfView; // not in the view frustum + long leavesOutOfView; // not in the view frustum + + long tooFar; // out of LOD + long leavesTooFar; // out of LOD + + long notAllInView; // in the view frustum, but the projection can't be calculated, assume it needs to be sent + long subtreeVoxelsSkipped; + + long nonLeaves; + long nonLeavesNotAllInView; + long nonLeavesOutOfView; // not in the view frustum + long nonLeavesOccluded; + long nonLeavesTooFar; + + long stagedForDeletion; + long doesntFit; +}; + +struct CountSubTreeOperationArgs { + long voxelsTouched; +}; + + +bool scanTreeWithOcclusionOperation(VoxelNode* node, void* extraData) { + + ScanTreeArgs* args = (ScanTreeArgs*) extraData; + args->totalVoxels++; + + // if this node is not in the view frustum, then just return, since we know none of it's children will be + // in the view. + if (!node->isInView(*args->viewFrustum)) { + args->outOfView++; + if (node->isLeaf()) { + args->leavesOutOfView++; + } else { + args->nonLeavesOutOfView++; + } + return false; + } + +/* + // If this node is too far for LOD... + float distance = node->distanceToCamera(*args->viewFrustum); + float boundaryDistance = boundaryDistanceForRenderLevel(numberOfThreeBitSectionsInCode(node->getOctalCode())+1); + + if (distance >= boundaryDistance) { + args->tooFar++; + if (node->isLeaf()) { + args->leavesTooFar++; + } else { + args->nonLeavesTooFar++; + } + return false; + } +*/ + + + //return true; // just check inview or not + + // If we are a parent, let's see if we're completely occluded. + if (!node->isLeaf()) { + args->nonLeaves++; + + AABox voxelBox = node->getAABox(); + voxelBox.scale(TREE_SCALE); + VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(args->viewFrustum->getProjectedPolygon(voxelBox)); + + // If we're not all in view, then ignore it, and just return. But keep searching... + if (!voxelPolygon->getAllInView()) { + args->nonLeavesNotAllInView++; + delete voxelPolygon; + + // we can't determine occlusion, so assume it's not + return true; + } + + CoverageMapStorageResult result = args->map->checkMap(voxelPolygon, false); + if (result == OCCLUDED) { + args->nonLeavesOccluded++; + delete voxelPolygon; + + /** Maybe we want to do this? But it's really only for debug accounting ****** + CountSubTreeOperationArgs subArgs; + subArgs.voxelsTouched = 0; + + args->tree->recurseNodeWithOperation(node, countSubTreeOperation, &subArgs ); + args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1); + args->totalVoxels += (subArgs.voxelsTouched - 1); + *****/ + + return false; + } + + delete voxelPolygon; + return true; // keep looking... + } + + // we don't want to check shouldRender in the server... that's not used + if (node->isLeaf() && node->isColored() /*&& node->getShouldRender()*/) { + args->coloredVoxels++; + + AABox voxelBox = node->getAABox(); + voxelBox.scale(TREE_SCALE); + VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(args->viewFrustum->getProjectedPolygon(voxelBox)); + + // If we're not all in view, then ignore it, and just return. But keep searching... + if (!voxelPolygon->getAllInView()) { + args->notAllInView++; + delete voxelPolygon; + + // But, this is one we do want to send... + + return true; + } + + CoverageMapStorageResult result = args->map->checkMap(voxelPolygon, true); + if (result == OCCLUDED) { + args->occludedVoxels++; + // This is one we don't want to send... + + } else if (result == STORED) { + args->notOccludedVoxels++; + // This is one we do want to send... + } else if (result == DOESNT_FIT) { + args->doesntFit++; + // Not sure what to do with this one... probably send it? + } + } + return true; // keep going! +} + +void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMap* coverageMap) { + PerformanceWarning warn(true, "scanTreeWithOcclusion()",true); + //CoverageMap::wantDebugging = false; + //coverageMap->erase(); + + ScanTreeArgs args; + args.viewFrustum = viewFrustum; + args.map = coverageMap; + args.totalVoxels = 0; + args.coloredVoxels = 0; + args.occludedVoxels = 0; + args.notOccludedVoxels = 0; + args.notAllInView = 0; + args.outOfView = 0; + args.leavesOutOfView = 0; + args.nonLeavesOutOfView = 0; + args.subtreeVoxelsSkipped = 0; + args.nonLeaves = 0; + args.stagedForDeletion = 0; + args.nonLeavesNotAllInView = 0; + args.nonLeavesOccluded = 0; + args.doesntFit = 0; + args.tooFar = 0; + args.leavesTooFar = 0; + args.nonLeavesTooFar = 0; + args.tree = tree; + + VoxelProjectedPolygon::pointInside_calls = 0; + VoxelProjectedPolygon::occludes_calls = 0; + VoxelProjectedPolygon::intersects_calls = 0; + + glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE); + + tree->recurseTreeWithOperationDistanceSorted(scanTreeWithOcclusionOperation, position, (void*)&args); + + printf("scanTreeWithOcclusion()\n"); + printf(" position=(%f,%f)\n", position.x, position.y); + printf(" total=%ld\n", args.totalVoxels); + printf(" colored=%ld\n", args.coloredVoxels); + printf("\n"); + printf(" occluded=%ld\n", args.occludedVoxels); + printf(" notOccluded=%ld\n", args.notOccludedVoxels); + printf(" nonLeavesOccluded=%ld\n", args.nonLeavesOccluded); + printf("\n"); + printf(" outOfView=%ld\n", args.outOfView); + printf(" leavesOutOfView=%ld\n", args.leavesOutOfView); + printf(" nonLeavesOutOfView=%ld\n", args.nonLeavesOutOfView); + printf("\n"); + printf(" tooFar=%ld\n", args.tooFar); + printf(" leavesTooFar=%ld\n", args.leavesTooFar); + printf(" nonLeavesTooFar=%ld\n", args.nonLeavesTooFar); + printf("\n"); + printf(" nonLeaves=%ld\n", args.nonLeaves); + printf(" notAllInView=%ld\n", args.notAllInView); + printf(" nonLeavesNotAllInView=%ld\n", args.nonLeavesNotAllInView); + printf(" subtreeVoxelsSkipped=%ld\n", args.subtreeVoxelsSkipped); + printf(" stagedForDeletion=%ld\n", args.stagedForDeletion); + printf(" pointInside_calls=%ld\n", VoxelProjectedPolygon::pointInside_calls); + printf(" occludes_calls=%ld\n", VoxelProjectedPolygon::occludes_calls); + printf(" intersects_calls=%ld\n", VoxelProjectedPolygon::intersects_calls); + + CoverageMap::wantDebugging = true; + coverageMap->erase();// print debug results... + +} From 44357fcfb91a8377a25ebc2ea4e0fbb17970aea1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 9 Jul 2013 13:17:21 -0700 Subject: [PATCH 21/55] add 'level' param to tree recursion --- interface/src/Application.cpp | 2 +- interface/src/Application.h | 2 +- interface/src/VoxelSystem.cpp | 28 +++++++++---------- interface/src/VoxelSystem.h | 24 ++++++++--------- libraries/voxels/src/ViewFrustum.cpp | 8 +++--- libraries/voxels/src/ViewFrustum.h | 2 +- libraries/voxels/src/VoxelTree.cpp | 40 ++++++++++++++++++---------- libraries/voxels/src/VoxelTree.h | 11 ++++---- voxel-edit/src/main.cpp | 2 +- 9 files changed, 67 insertions(+), 52 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6254d38e22..66fc55c51c 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1268,7 +1268,7 @@ struct SendVoxelsOperationArgs { }; -bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) { +bool Application::sendVoxelsOperation(VoxelNode* node, int level, void* extraData) { SendVoxelsOperationArgs* args = (SendVoxelsOperationArgs*)extraData; if (node->isColored()) { unsigned char* nodeOctalCode = node->getOctalCode(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 8d1cc276d4..fe53c6edfa 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -164,7 +164,7 @@ private: static void broadcastToNodes(unsigned char* data, size_t bytes, const char type); static void sendVoxelServerAddScene(); - static bool sendVoxelsOperation(VoxelNode* node, void* extraData); + static bool sendVoxelsOperation(VoxelNode* node, int level, void* extraData); static void sendAvatarVoxelURLMessage(const QUrl& url); static void processAvatarVoxelURLMessage(unsigned char *packetData, size_t dataBytes); static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail); diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 791729a442..0bc9b104be 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -701,7 +701,7 @@ void VoxelSystem::killLocalVoxels() { } -bool VoxelSystem::randomColorOperation(VoxelNode* node, void* extraData) { +bool VoxelSystem::randomColorOperation(VoxelNode* node, int level, void* extraData) { _nodeCount++; if (node->isColored()) { nodeColor newColor = { 255, randomColorValue(150), randomColorValue(150), 1 }; @@ -717,7 +717,7 @@ void VoxelSystem::randomizeVoxelColors() { setupNewVoxelsForDrawing(); } -bool VoxelSystem::falseColorizeRandomOperation(VoxelNode* node, void* extraData) { +bool VoxelSystem::falseColorizeRandomOperation(VoxelNode* node, int level, void* extraData) { _nodeCount++; // always false colorize node->setFalseColor(255, randomColorValue(150), randomColorValue(150)); @@ -731,7 +731,7 @@ void VoxelSystem::falseColorizeRandom() { setupNewVoxelsForDrawing(); } -bool VoxelSystem::trueColorizeOperation(VoxelNode* node, void* extraData) { +bool VoxelSystem::trueColorizeOperation(VoxelNode* node, int level, void* extraData) { _nodeCount++; node->setFalseColored(false); return true; @@ -746,7 +746,7 @@ void VoxelSystem::trueColorize() { } // Will false colorize voxels that are not in view -bool VoxelSystem::falseColorizeInViewOperation(VoxelNode* node, void* extraData) { +bool VoxelSystem::falseColorizeInViewOperation(VoxelNode* node, int level, void* extraData) { const ViewFrustum* viewFrustum = (const ViewFrustum*) extraData; _nodeCount++; if (node->isColored()) { @@ -766,7 +766,7 @@ void VoxelSystem::falseColorizeInView(ViewFrustum* viewFrustum) { } // Will false colorize voxels based on distance from view -bool VoxelSystem::falseColorizeDistanceFromViewOperation(VoxelNode* node, void* extraData) { +bool VoxelSystem::falseColorizeDistanceFromViewOperation(VoxelNode* node, int level, void* extraData) { ViewFrustum* viewFrustum = (ViewFrustum*) extraData; if (node->isColored()) { float distance = node->distanceToCamera(*viewFrustum); @@ -789,7 +789,7 @@ float VoxelSystem::_minDistance = FLT_MAX; // Helper function will get the distance from view range, would be nice if you could just keep track // of this as voxels are created and/or colored... seems like some transform math could do that so // we wouldn't need to do two passes of the tree -bool VoxelSystem::getDistanceFromViewRangeOperation(VoxelNode* node, void* extraData) { +bool VoxelSystem::getDistanceFromViewRangeOperation(VoxelNode* node, int level, void* extraData) { ViewFrustum* viewFrustum = (ViewFrustum*) extraData; // only do this for truly colored voxels... if (node->isColored()) { @@ -842,7 +842,7 @@ public: // "Remove" voxels from the tree that are not in view. We don't actually delete them, // we remove them from the tree and place them into a holding area for later deletion -bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) { +bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, int level, void* extraData) { removeOutOfViewArgs* args = (removeOutOfViewArgs*)extraData; // If our node was previously added to the don't recurse bag, then return false to @@ -977,7 +977,7 @@ public: bool colorThis; }; -bool VoxelSystem::falseColorizeRandomEveryOtherOperation(VoxelNode* node, void* extraData) { +bool VoxelSystem::falseColorizeRandomEveryOtherOperation(VoxelNode* node, int level, void* extraData) { falseColorizeRandomEveryOtherArgs* args = (falseColorizeRandomEveryOtherArgs*)extraData; args->totalNodes++; if (node->isColored()) { @@ -1030,7 +1030,7 @@ public: bool hasIndexFound[MAX_VOXELS_PER_SYSTEM]; }; -bool VoxelSystem::collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData) { +bool VoxelSystem::collectStatsForTreesAndVBOsOperation(VoxelNode* node, int level, void* extraData) { collectStatsForTreesAndVBOsArgs* args = (collectStatsForTreesAndVBOsArgs*)extraData; args->totalNodes++; @@ -1182,7 +1182,7 @@ struct FalseColorizeSubTreeOperationArgs { long voxelsTouched; }; -bool VoxelSystem::falseColorizeSubTreeOperation(VoxelNode* node, void* extraData) { +bool VoxelSystem::falseColorizeSubTreeOperation(VoxelNode* node, int level, void* extraData) { if (node->getShouldRender()) { FalseColorizeSubTreeOperationArgs* args = (FalseColorizeSubTreeOperationArgs*) extraData; node->setFalseColor(args->color[0], args->color[1], args->color[2]); @@ -1191,7 +1191,7 @@ bool VoxelSystem::falseColorizeSubTreeOperation(VoxelNode* node, void* extraData return true; } -bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraData) { +bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, int level, void* extraData) { FalseColorizeOccludedArgs* args = (FalseColorizeOccludedArgs*) extraData; args->totalVoxels++; @@ -1228,7 +1228,7 @@ bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraDat subArgs.color[2] = 0; subArgs.voxelsTouched = 0; - args->tree->recurseNodeWithOperation(node, falseColorizeSubTreeOperation, &subArgs ); + args->tree->recurseNodeWithOperation(node, level, falseColorizeSubTreeOperation, &subArgs ); args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1); args->totalVoxels += (subArgs.voxelsTouched - 1); @@ -1312,7 +1312,7 @@ void VoxelSystem::falseColorizeOccluded() { setupNewVoxelsForDrawing(); } -bool VoxelSystem::falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData) { +bool VoxelSystem::falseColorizeOccludedV2Operation(VoxelNode* node, int level, void* extraData) { FalseColorizeOccludedArgs* args = (FalseColorizeOccludedArgs*) extraData; args->totalVoxels++; @@ -1349,7 +1349,7 @@ bool VoxelSystem::falseColorizeOccludedV2Operation(VoxelNode* node, void* extraD subArgs.color[2] = 0; subArgs.voxelsTouched = 0; - args->tree->recurseNodeWithOperation(node, falseColorizeSubTreeOperation, &subArgs ); + args->tree->recurseNodeWithOperation(node, level, falseColorizeSubTreeOperation, &subArgs ); args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1); args->totalVoxels += (subArgs.voxelsTouched - 1); diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 470ec79aa2..0b7b271411 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -120,18 +120,18 @@ private: bool _renderWarningsOn; // Operation functions for tree recursion methods static int _nodeCount; - static bool randomColorOperation(VoxelNode* node, void* extraData); - static bool falseColorizeRandomOperation(VoxelNode* node, void* extraData); - static bool trueColorizeOperation(VoxelNode* node, void* extraData); - static bool falseColorizeInViewOperation(VoxelNode* node, void* extraData); - static bool falseColorizeDistanceFromViewOperation(VoxelNode* node, void* extraData); - static bool getDistanceFromViewRangeOperation(VoxelNode* node, void* extraData); - static bool removeOutOfViewOperation(VoxelNode* node, void* extraData); - static bool falseColorizeRandomEveryOtherOperation(VoxelNode* node, void* extraData); - static bool collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData); - static bool falseColorizeOccludedOperation(VoxelNode* node, void* extraData); - static bool falseColorizeSubTreeOperation(VoxelNode* node, void* extraData); - static bool falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData); + static bool randomColorOperation(VoxelNode* node, int level, void* extraData); + static bool falseColorizeRandomOperation(VoxelNode* node, int level, void* extraData); + static bool trueColorizeOperation(VoxelNode* node, int level, void* extraData); + static bool falseColorizeInViewOperation(VoxelNode* node, int level, void* extraData); + static bool falseColorizeDistanceFromViewOperation(VoxelNode* node, int level, void* extraData); + static bool getDistanceFromViewRangeOperation(VoxelNode* node, int level, void* extraData); + static bool removeOutOfViewOperation(VoxelNode* node, int level, void* extraData); + static bool falseColorizeRandomEveryOtherOperation(VoxelNode* node, int level, void* extraData); + static bool collectStatsForTreesAndVBOsOperation(VoxelNode* node, int level, void* extraData); + static bool falseColorizeOccludedOperation(VoxelNode* node, int level, void* extraData); + static bool falseColorizeSubTreeOperation(VoxelNode* node, int level, void* extraData); + static bool falseColorizeOccludedV2Operation(VoxelNode* node, int level, void* extraData); int updateNodeInArraysAsFullVBO(VoxelNode* node); diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index 7b7b0d2894..aaa03855d3 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -266,6 +266,7 @@ ViewFrustum::location ViewFrustum::sphereInFrustum(const glm::vec3& center, floa ViewFrustum::location ViewFrustum::boxInFrustum(const AABox& box) const { + ViewFrustum::location regularResult = INSIDE; ViewFrustum::location keyholeResult = OUTSIDE; @@ -274,15 +275,16 @@ ViewFrustum::location ViewFrustum::boxInFrustum(const AABox& box) const { keyholeResult = boxInKeyhole(box); } if (keyholeResult == INSIDE) { +//printLog("ViewFrustum::boxInFrustum() keyholeResult == INSIDE _keyholeRadius=%f\n",_keyholeRadius); return keyholeResult; } for(int i=0; i < 6; i++) { - glm::vec3 normal = _planes[i].getNormal(); - glm::vec3 boxVertexP = box.getVertexP(normal); + const glm::vec3& normal = _planes[i].getNormal(); + const glm::vec3& boxVertexP = box.getVertexP(normal); float planeToBoxVertexPDistance = _planes[i].distance(boxVertexP); - glm::vec3 boxVertexN = box.getVertexN(normal); + const glm::vec3& boxVertexN = box.getVertexN(normal); float planeToBoxVertexNDistance = _planes[i].distance(boxVertexN); if (planeToBoxVertexPDistance < 0) { diff --git a/libraries/voxels/src/ViewFrustum.h b/libraries/voxels/src/ViewFrustum.h index 6833eb6134..7308d5bb64 100644 --- a/libraries/voxels/src/ViewFrustum.h +++ b/libraries/voxels/src/ViewFrustum.h @@ -17,7 +17,7 @@ #include "AABox.h" #include "VoxelProjectedPolygon.h" -const float DEFAULT_KEYHOLE_RADIUS = 2.0f; +const float DEFAULT_KEYHOLE_RADIUS = 3.0f; class ViewFrustum { public: diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 083765aa0b..604e50f1d3 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -28,11 +28,16 @@ #include -int boundaryDistanceForRenderLevel(unsigned int renderLevel) { - float voxelSizeScale = 50000.0f; +float boundaryDistanceForRenderLevel(unsigned int renderLevel) { + const float voxelSizeScale = 50000.0f; return voxelSizeScale / powf(2, renderLevel); } +float boundaryDistanceSquaredForRenderLevel(unsigned int renderLevel) { + const float voxelSizeScale = (50000.0f/TREE_SCALE) * (50000.0f/TREE_SCALE); + return voxelSizeScale / powf(2, (2 * renderLevel)); +} + VoxelTree::VoxelTree(bool shouldReaverage) : voxelsCreated(0), voxelsColored(0), @@ -56,16 +61,19 @@ VoxelTree::~VoxelTree() { // Recurses voxel tree calling the RecurseVoxelTreeOperation function for each node. // stops recursion if operation function returns false. void VoxelTree::recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData) { - recurseNodeWithOperation(rootNode, operation, extraData); + int level = 0; + recurseNodeWithOperation(rootNode, level, operation, extraData); } // Recurses voxel node with an operation function -void VoxelTree::recurseNodeWithOperation(VoxelNode* node,RecurseVoxelTreeOperation operation, void* extraData) { - if (operation(node, extraData)) { +void VoxelTree::recurseNodeWithOperation(VoxelNode* node, int& level, RecurseVoxelTreeOperation operation, void* extraData) { + if (operation(node, level, extraData)) { for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { VoxelNode* child = node->getChildAtIndex(i); if (child) { - recurseNodeWithOperation(child, operation, extraData); + level++; + recurseNodeWithOperation(child, level, operation, extraData); + level--; } } } @@ -75,13 +83,15 @@ void VoxelTree::recurseNodeWithOperation(VoxelNode* node,RecurseVoxelTreeOperati // stops recursion if operation function returns false. void VoxelTree::recurseTreeWithOperationDistanceSorted(RecurseVoxelTreeOperation operation, const glm::vec3& point, void* extraData) { - recurseNodeWithOperationDistanceSorted(rootNode, operation, point, extraData); + + int level = 0; + recurseNodeWithOperationDistanceSorted(rootNode, level, operation, point, extraData); } // Recurses voxel node with an operation function -void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation, +void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, int& level, RecurseVoxelTreeOperation operation, const glm::vec3& point, void* extraData) { - if (operation(node, extraData)) { + if (operation(node, level, extraData)) { // determine the distance sorted order of our children VoxelNode* sortedChildren[NUMBER_OF_CHILDREN]; @@ -107,7 +117,9 @@ void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseV if (childNode) { //printLog("recurseNodeWithOperationDistanceSorted() PROCESSING child[%d] distance=%f...\n", i, distancesToChildren[i]); //childNode->printDebugDetails(""); - recurseNodeWithOperationDistanceSorted(childNode, operation, point, extraData); + level++; + recurseNodeWithOperationDistanceSorted(childNode, level, operation, point, extraData); + level--; } } } @@ -852,7 +864,7 @@ public: bool found; }; -bool findRayIntersectionOp(VoxelNode* node, void* extraData) { +bool findRayIntersectionOp(VoxelNode* node, int level, void* extraData) { RayArgs* args = static_cast(extraData); AABox box = node->getAABox(); float distance; @@ -888,7 +900,7 @@ public: bool found; }; -bool findSpherePenetrationOp(VoxelNode* node, void* extraData) { +bool findSpherePenetrationOp(VoxelNode* node, int level, void* extraData) { SphereArgs* args = static_cast(extraData); // coarse check against bounds @@ -925,7 +937,7 @@ public: bool found; }; -bool findCapsulePenetrationOp(VoxelNode* node, void* extraData) { +bool findCapsulePenetrationOp(VoxelNode* node, int level, void* extraData) { CapsuleArgs* args = static_cast(extraData); // coarse check against bounds @@ -1493,7 +1505,7 @@ unsigned long VoxelTree::getVoxelCount() { return nodeCount; } -bool VoxelTree::countVoxelsOperation(VoxelNode* node, void* extraData) { +bool VoxelTree::countVoxelsOperation(VoxelNode* node, int level, void* extraData) { (*(unsigned long*)extraData)++; return true; // keep going } diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 3d87fbc557..6d718d4cc9 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -16,7 +16,7 @@ #include "CoverageMap.h" // Callback function, for recuseTreeWithOperation -typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData); +typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, int level, void* extraData); typedef enum {GRADIENT, RANDOM, NATURAL} creationMode; #define NO_EXISTS_BITS false @@ -144,8 +144,8 @@ public: bool getShouldReaverage() const { return _shouldReaverage; } - void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData); - void recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation, + void recurseNodeWithOperation(VoxelNode* node, int& level, RecurseVoxelTreeOperation operation, void* extraData); + void recurseNodeWithOperationDistanceSorted(VoxelNode* node, int& level, RecurseVoxelTreeOperation operation, const glm::vec3& point, void* extraData); private: @@ -159,7 +159,7 @@ private: VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag, bool deltaViewFrustum, const ViewFrustum* lastViewFrustum); - static bool countVoxelsOperation(VoxelNode* node, void* extraData); + static bool countVoxelsOperation(VoxelNode* node, int level, void* extraData); VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const; VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate); @@ -171,6 +171,7 @@ private: bool _shouldReaverage; }; -int boundaryDistanceForRenderLevel(unsigned int renderLevel); +float boundaryDistanceForRenderLevel(unsigned int renderLevel); +float boundaryDistanceSquaredForRenderLevel(unsigned int renderLevel); #endif /* defined(__hifi__VoxelTree__) */ diff --git a/voxel-edit/src/main.cpp b/voxel-edit/src/main.cpp index 9571e4adf5..a2d9d80292 100644 --- a/voxel-edit/src/main.cpp +++ b/voxel-edit/src/main.cpp @@ -13,7 +13,7 @@ VoxelTree myTree; int _nodeCount=0; -bool countVoxelsOperation(VoxelNode* node, void* extraData) { +bool countVoxelsOperation(VoxelNode* node, int level, void* extraData) { if (node->isColored()){ _nodeCount++; } From f076eed9d24e0f6f172de59427edb37408441129 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 9 Jul 2013 13:17:38 -0700 Subject: [PATCH 22/55] more hacking on occlusion culling --- voxel-server/src/main.cpp | 180 ++++++++++++++++++++++++++++++++------ 1 file changed, 154 insertions(+), 26 deletions(-) diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 5bdab5b742..c96f11056b 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -65,7 +65,8 @@ bool wantSearchForColoredNodes = false; EnvironmentData environmentData[3]; -void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMap* coverageMap); +VoxelNode* leftOffAt = NULL; +void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMap* coverageMap, VoxelNode*& leftOffAt); void randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) { @@ -306,11 +307,6 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, } else { nodeData->nodeBag.insert(serverTree.rootNode); } - - - // hack, just test scanning the tree here! - //scanTreeWithOcclusion(&serverTree, &nodeData->getCurrentViewFrustum(), &nodeData->map); - } long long end = usecTimestampNow(); int elapsedmsec = (end - start)/1000; @@ -470,7 +466,11 @@ void *distributeVoxelsToListeners(void *args) { } if (nodeData->getWantResIn()) { - resInVoxelDistributor(nodeList, node, nodeData); + //resInVoxelDistributor(nodeList, node, nodeData); + + // hack, just test scanning the tree here! + scanTreeWithOcclusion(&serverTree, &nodeData->getCurrentViewFrustum(), &nodeData->map, ::leftOffAt); + } else { deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged); } @@ -762,9 +762,11 @@ int main(int argc, const char * argv[]) { } +const long MAX_SCAN = 100 * 1000; struct ScanTreeArgs { ViewFrustum* viewFrustum; + glm::vec3 cameraPosition; CoverageMap* map; // CoverageMapV2* mapV2; VoxelTree* tree; @@ -790,6 +792,16 @@ struct ScanTreeArgs { long stagedForDeletion; long doesntFit; + + long long startSearch; + long long startScan; + long long stopScan; + VoxelNode* leftOffAt; + + ViewFrustum::location subTreeLocation; + long subTreeInside; + + int level; }; struct CountSubTreeOperationArgs { @@ -797,29 +809,113 @@ struct CountSubTreeOperationArgs { }; -bool scanTreeWithOcclusionOperation(VoxelNode* node, void* extraData) { - +bool scanTreeWithOcclusionOperation(VoxelNode* node, int level, void* extraData) { + ScanTreeArgs* args = (ScanTreeArgs*) extraData; - args->totalVoxels++; + + // fastest, scan entire tree... + //args->totalVoxels++; + //return true; - // if this node is not in the view frustum, then just return, since we know none of it's children will be - // in the view. - if (!node->isInView(*args->viewFrustum)) { - args->outOfView++; - if (node->isLeaf()) { - args->leavesOutOfView++; - } else { - args->nonLeavesOutOfView++; + +/*** + + // If we had a previous leftOffAt, then keep going until we get find it + if (args->leftOffAt) { + // if this isn't our node, the just keep searching + if (node != args->leftOffAt) { + return true; } - return false; + // otherwise, stop searching and start processing + args->leftOffAt = NULL; + args->startScan = usecTimestampNow(); + } else { + // bail if we've taken too long + long long now = usecTimestampNow(); + if (now - args->startScan > MAX_SCAN) { + // if we just stopped, then record this stop time + if (args->stopScan == 0) { + args->stopScan = now; + args->leftOffAt = node; + } + return false; + } + } +**/ + + args->totalVoxels++; + + // SOME FACTS WE KNOW... + // If a node is INSIDE the view frustum, then ALL it's child nodes are also INSIDE, and so we + // shouldn't have to check that any more... use InFrustum() and store something in args to know + // that that part of the stack is in frustum. (do we also need to store a pointer to the parent node + // that is INSIDE so that when we unwind back to that point we start checking again?... probably) + // + // consider switching to stack data structure so we can break recursion on time slices.. + // /*** + // PointerStack stackOfNodes; + // + // // start case, stack empty, so start with root... + // if (stackOfNodes.empty()) { + // if (operation(rootNode, extraData)) { + // stackOfNodes.push(rootNode); + // } + // } + // while (!stackOfNodes.empty()) { + // VoxelNode* node = stackOfNodes.top(); + // stackOfNodes.pop(); + // sortChildren... + // for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + // VoxelNode* child = getSortedChild[i]... + // if (child && operation(child, extraData)) { + // stackOfNodes.push(child); + // } + // } + // // at this point, we can check to see if we should bail for timing reasons + // // because if we bail at this point, then reenter the while, we will basically + // // be back to processing the stack from same place we left off, and all can proceed normally + // if (elapsedTime > ALLOWED_TIME) { + // return; ... + // } + // } + // ***/ + + + if (args->subTreeLocation == ViewFrustum::INSIDE) { + args->subTreeInside++; + // continue without checking further + } else { + // if this node is not in the view frustum, then just return, since we know none of it's children will be + // in the view. + ViewFrustum::location location = node->inFrustum(*args->viewFrustum); + if (location == ViewFrustum::OUTSIDE) { + args->outOfView++; + if (node->isLeaf()) { + args->leavesOutOfView++; + } else { + args->nonLeavesOutOfView++; + } + return false; + } + // remember this for next recursion pass + args->subTreeLocation = location; } -/* // If this node is too far for LOD... - float distance = node->distanceToCamera(*args->viewFrustum); - float boundaryDistance = boundaryDistanceForRenderLevel(numberOfThreeBitSectionsInCode(node->getOctalCode())+1); + //float distance = node->distanceToCamera(*args->viewFrustum); + //float boundaryDistance = boundaryDistanceForRenderLevel(numberOfThreeBitSectionsInCode(node->getOctalCode())+1); - if (distance >= boundaryDistance) { + //int calcLevel = numberOfThreeBitSectionsInCode(node->getOctalCode())+1; + //printf("level=%d --- calcLevel=%d\n", level, calcLevel); + + float distanceSquared = node->distanceSquareToPoint(args->cameraPosition); + float boundaryDistanceSquared = boundaryDistanceSquaredForRenderLevel(level+1); + //float boundaryDistanceSquared = boundaryDistanceSquaredForRenderLevel(calcLevel); + + + //printf("distance=%f, boundaryDistance=%f ---- distanceSquared=%f, boundaryDistanceSquared=%f \n", distance, boundaryDistance, distanceSquared, boundaryDistanceSquared); + + if (distanceSquared >= boundaryDistanceSquared) { args->tooFar++; if (node->isLeaf()) { args->leavesTooFar++; @@ -828,8 +924,8 @@ bool scanTreeWithOcclusionOperation(VoxelNode* node, void* extraData) { } return false; } -*/ - + +return true; // so only counting in view... //return true; // just check inview or not @@ -905,13 +1001,14 @@ bool scanTreeWithOcclusionOperation(VoxelNode* node, void* extraData) { return true; // keep going! } -void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMap* coverageMap) { +void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMap* coverageMap, VoxelNode*& leftOffAt) { PerformanceWarning warn(true, "scanTreeWithOcclusion()",true); //CoverageMap::wantDebugging = false; //coverageMap->erase(); ScanTreeArgs args; args.viewFrustum = viewFrustum; + args.cameraPosition = viewFrustum->getPosition()/(float)TREE_SCALE; args.map = coverageMap; args.totalVoxels = 0; args.coloredVoxels = 0; @@ -931,6 +1028,20 @@ void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMa args.leavesTooFar = 0; args.nonLeavesTooFar = 0; args.tree = tree; + args.subTreeLocation = ViewFrustum::OUTSIDE; // assume the worst + args.subTreeInside = 0; + args.level = 0; + + args.leftOffAt = leftOffAt; + if (leftOffAt) { + args.startSearch = usecTimestampNow(); + args.startScan = 0; + args.stopScan = 0; + } else { + args.startSearch = usecTimestampNow(); + args.startScan = args.startSearch; + args.stopScan = 0; + } VoxelProjectedPolygon::pointInside_calls = 0; VoxelProjectedPolygon::occludes_calls = 0; @@ -939,6 +1050,9 @@ void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMa glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE); tree->recurseTreeWithOperationDistanceSorted(scanTreeWithOcclusionOperation, position, (void*)&args); + long long now = usecTimestampNow(); + leftOffAt = args.leftOffAt; + printf("scanTreeWithOcclusion()\n"); printf(" position=(%f,%f)\n", position.x, position.y); @@ -965,6 +1079,20 @@ void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMa printf(" pointInside_calls=%ld\n", VoxelProjectedPolygon::pointInside_calls); printf(" occludes_calls=%ld\n", VoxelProjectedPolygon::occludes_calls); printf(" intersects_calls=%ld\n", VoxelProjectedPolygon::intersects_calls); + + printf("\n"); + printf(" subTreeInside=%ld\n", args.subTreeInside); + + int searchmsec = (args.startScan - args.startSearch)/1000; + int elapsedmsec = (args.stopScan - args.startScan)/1000; + int unwindmsec = (now - args.stopScan)/1000; + + printf("\n"); + printf(" searchmsec=%d\n", searchmsec); + printf(" elapsedmsec=%d\n", elapsedmsec); + printf(" unwindmsec=%d\n", unwindmsec); + printf(" leftOffAt=%p\n", leftOffAt); + CoverageMap::wantDebugging = true; coverageMap->erase();// print debug results... From d89c3c80426928f9f311947b8e86681752bdd3ec Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 11:09:48 -0700 Subject: [PATCH 23/55] add version of recurse tree that uses pointer stack and allows timing breaks --- libraries/voxels/src/CoverageMap.cpp | 27 ++- libraries/voxels/src/CoverageMap.h | 1 + libraries/voxels/src/VoxelNode.h | 2 +- libraries/voxels/src/VoxelTree.cpp | 58 ++++- libraries/voxels/src/VoxelTree.h | 6 + voxel-server/src/main.cpp | 310 ++++++++++++--------------- 6 files changed, 220 insertions(+), 184 deletions(-) diff --git a/libraries/voxels/src/CoverageMap.cpp b/libraries/voxels/src/CoverageMap.cpp index 67e8387f5b..bd00179d9e 100644 --- a/libraries/voxels/src/CoverageMap.cpp +++ b/libraries/voxels/src/CoverageMap.cpp @@ -65,6 +65,21 @@ CoverageMap::~CoverageMap() { erase(); }; +void CoverageMap::printStats() { + printLog("CoverageMap::printStats()...\n"); + printLog("MINIMUM_POLYGON_AREA_TO_STORE=%f\n",MINIMUM_POLYGON_AREA_TO_STORE); + printLog("_mapCount=%d\n",_mapCount); + printLog("_checkMapRootCalls=%d\n",_checkMapRootCalls); + printLog("_notAllInView=%d\n",_notAllInView); + printLog("_maxPolygonsUsed=%d\n",CoverageRegion::_maxPolygonsUsed); + printLog("_totalPolygons=%d\n",CoverageRegion::_totalPolygons); + printLog("_occlusionTests=%d\n",CoverageRegion::_occlusionTests); + printLog("_regionSkips=%d\n",CoverageRegion::_regionSkips); + printLog("_tooSmallSkips=%d\n",CoverageRegion::_tooSmallSkips); + printLog("_outOfOrderPolygon=%d\n",CoverageRegion::_outOfOrderPolygon); + printLog("_clippedPolygons=%d\n",CoverageRegion::_clippedPolygons); +} + void CoverageMap::erase() { // tell our regions to erase() _topHalf.erase(); @@ -82,17 +97,7 @@ void CoverageMap::erase() { if (_isRoot && wantDebugging) { printLog("CoverageMap last to be deleted...\n"); - printLog("MINIMUM_POLYGON_AREA_TO_STORE=%f\n",MINIMUM_POLYGON_AREA_TO_STORE); - printLog("_mapCount=%d\n",_mapCount); - printLog("_checkMapRootCalls=%d\n",_checkMapRootCalls); - printLog("_notAllInView=%d\n",_notAllInView); - printLog("_maxPolygonsUsed=%d\n",CoverageRegion::_maxPolygonsUsed); - printLog("_totalPolygons=%d\n",CoverageRegion::_totalPolygons); - printLog("_occlusionTests=%d\n",CoverageRegion::_occlusionTests); - printLog("_regionSkips=%d\n",CoverageRegion::_regionSkips); - printLog("_tooSmallSkips=%d\n",CoverageRegion::_tooSmallSkips); - printLog("_outOfOrderPolygon=%d\n",CoverageRegion::_outOfOrderPolygon); - printLog("_clippedPolygons=%d\n",CoverageRegion::_clippedPolygons); + printStats(); CoverageRegion::_maxPolygonsUsed = 0; CoverageRegion::_totalPolygons = 0; diff --git a/libraries/voxels/src/CoverageMap.h b/libraries/voxels/src/CoverageMap.h index 6c0a1f86cb..c2e1a4193b 100644 --- a/libraries/voxels/src/CoverageMap.h +++ b/libraries/voxels/src/CoverageMap.h @@ -81,6 +81,7 @@ public: BoundingBox getChildBoundingBox(int childIndex); void erase(); // erase the coverage map + void printStats(); static bool wantDebugging; diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index 4aa64f019e..048612f6b8 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -62,7 +62,7 @@ public: const glm::vec3& getCenter() const { return _box.getCenter(); }; const glm::vec3& getCorner() const { return _box.getCorner(); }; float getScale() const { return _box.getSize().x; /* voxelScale = (1 / powf(2, *node->getOctalCode())); */ }; - int getLevel() const { return *_octalCode + 1; /* one based or zero based? */ }; + int getLevel() const { return *_octalCode + 1; /* one based or zero based? this doesn't correctly handle 2 byte case */ }; float getEnclosingRadius() const; diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 604e50f1d3..d911c69f4a 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -58,6 +58,63 @@ VoxelTree::~VoxelTree() { } } + +void VoxelTree::recurseTreeWithOperationDistanceSortedTimed(PointerStack* stackOfNodes, long allowedTime, + RecurseVoxelTreeOperation operation, + const glm::vec3& point, void* extraData) { + + int ignored = 0; + long long start = usecTimestampNow(); + + // start case, stack empty, so start with root... + if (stackOfNodes->empty()) { + stackOfNodes->push(rootNode); + } + while (!stackOfNodes->empty()) { + VoxelNode* node = (VoxelNode*)stackOfNodes->top(); + stackOfNodes->pop(); + + if (operation(node, ignored, extraData)) { + + //sortChildren... CLOSEST to FURTHEST + // determine the distance sorted order of our children + VoxelNode* sortedChildren[NUMBER_OF_CHILDREN]; + float distancesToChildren[NUMBER_OF_CHILDREN]; + int indexOfChildren[NUMBER_OF_CHILDREN]; // not really needed + int currentCount = 0; + + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + VoxelNode* childNode = node->getChildAtIndex(i); + if (childNode) { + // chance to optimize, doesn't need to be actual distance!! Could be distance squared + float distanceSquared = childNode->distanceSquareToPoint(point); + currentCount = insertIntoSortedArrays((void*)childNode, distanceSquared, i, + (void**)&sortedChildren, (float*)&distancesToChildren, + (int*)&indexOfChildren, currentCount, NUMBER_OF_CHILDREN); + } + } + + //iterate sorted children FURTHEST to CLOSEST + for (int i = currentCount-1; i >= 0; i--) { + VoxelNode* child = sortedChildren[i]; + stackOfNodes->push(child); +//printLog("stackOfNodes->size()=%d child->getLevel()=%d\n",stackOfNodes->size(), child->getLevel()); + } + } + + // at this point, we can check to see if we should bail for timing reasons + // because if we bail at this point, then reenter the while, we will basically + // be back to processing the stack from same place we left off, and all can proceed normally + long long now = usecTimestampNow(); + long elapsedTime = now - start; + + if (elapsedTime > allowedTime) { + return; // caller responsible for calling us again to finish the job! + } + } +} + + // Recurses voxel tree calling the RecurseVoxelTreeOperation function for each node. // stops recursion if operation function returns false. void VoxelTree::recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData) { @@ -93,7 +150,6 @@ void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, int& lev const glm::vec3& point, void* extraData) { if (operation(node, level, extraData)) { // determine the distance sorted order of our children - VoxelNode* sortedChildren[NUMBER_OF_CHILDREN]; float distancesToChildren[NUMBER_OF_CHILDREN]; int indexOfChildren[NUMBER_OF_CHILDREN]; // not really needed diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 6d718d4cc9..a6d1000301 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -14,6 +14,7 @@ #include "VoxelNode.h" #include "VoxelNodeBag.h" #include "CoverageMap.h" +#include "PointerStack.h" // Callback function, for recuseTreeWithOperation typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, int level, void* extraData); @@ -147,6 +148,11 @@ public: void recurseNodeWithOperation(VoxelNode* node, int& level, RecurseVoxelTreeOperation operation, void* extraData); void recurseNodeWithOperationDistanceSorted(VoxelNode* node, int& level, RecurseVoxelTreeOperation operation, const glm::vec3& point, void* extraData); + + + void recurseTreeWithOperationDistanceSortedTimed(PointerStack* stackOfNodes, long allowedTime, + RecurseVoxelTreeOperation operation, + const glm::vec3& point, void* extraData); private: void deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData); diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index c96f11056b..1ccf02ba8c 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -66,7 +66,7 @@ EnvironmentData environmentData[3]; VoxelNode* leftOffAt = NULL; -void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMap* coverageMap, VoxelNode*& leftOffAt); +void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMap* coverageMap, bool wantsOcclusionCulling); void randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) { @@ -365,8 +365,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, if (nodeData->getAvailable() >= bytesWritten) { nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); } else { - nodeList->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); + nodeList->getNodeSocket()->send(node->getActiveSocket(),nodeData->getPacket(), nodeData->getPacketLength()); trueBytesSent += nodeData->getPacketLength(); truePacketsSent++; packetsSentThisInterval++; @@ -375,8 +374,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, } } else { if (nodeData->isPacketWaiting()) { - nodeList->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); + nodeList->getNodeSocket()->send(node->getActiveSocket(),nodeData->getPacket(), nodeData->getPacketLength()); trueBytesSent += nodeData->getPacketLength(); truePacketsSent++; nodeData->resetVoxelPacket(); @@ -469,7 +467,12 @@ void *distributeVoxelsToListeners(void *args) { //resInVoxelDistributor(nodeList, node, nodeData); // hack, just test scanning the tree here! - scanTreeWithOcclusion(&serverTree, &nodeData->getCurrentViewFrustum(), &nodeData->map, ::leftOffAt); + scanTreeWithOcclusion(&serverTree, &nodeData->getCurrentViewFrustum(), &nodeData->map, + nodeData->getWantOcclusionCulling()); + printf("AFTER scanTreeWithOcclusion() map->printStats()\n"); + CoverageMap::wantDebugging = true; + nodeData->map.printStats(); + } else { deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged); @@ -792,12 +795,9 @@ struct ScanTreeArgs { long stagedForDeletion; long doesntFit; - - long long startSearch; - long long startScan; - long long stopScan; - VoxelNode* leftOffAt; - + + bool wantsOcclusionCulling; + ViewFrustum::location subTreeLocation; long subTreeInside; @@ -850,37 +850,7 @@ bool scanTreeWithOcclusionOperation(VoxelNode* node, int level, void* extraData) // shouldn't have to check that any more... use InFrustum() and store something in args to know // that that part of the stack is in frustum. (do we also need to store a pointer to the parent node // that is INSIDE so that when we unwind back to that point we start checking again?... probably) - // - // consider switching to stack data structure so we can break recursion on time slices.. - // /*** - // PointerStack stackOfNodes; - // - // // start case, stack empty, so start with root... - // if (stackOfNodes.empty()) { - // if (operation(rootNode, extraData)) { - // stackOfNodes.push(rootNode); - // } - // } - // while (!stackOfNodes.empty()) { - // VoxelNode* node = stackOfNodes.top(); - // stackOfNodes.pop(); - // sortChildren... - // for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - // VoxelNode* child = getSortedChild[i]... - // if (child && operation(child, extraData)) { - // stackOfNodes.push(child); - // } - // } - // // at this point, we can check to see if we should bail for timing reasons - // // because if we bail at this point, then reenter the while, we will basically - // // be back to processing the stack from same place we left off, and all can proceed normally - // if (elapsedTime > ALLOWED_TIME) { - // return; ... - // } - // } - // ***/ - - +/**/ if (args->subTreeLocation == ViewFrustum::INSIDE) { args->subTreeInside++; // continue without checking further @@ -898,23 +868,14 @@ bool scanTreeWithOcclusionOperation(VoxelNode* node, int level, void* extraData) return false; } // remember this for next recursion pass - args->subTreeLocation = location; + // NOTE: This doesn't actually work because we need a stack of these, and our code doesn't execute + // to pop the stack. Similar problem with level. + //args->subTreeLocation = location; } - +/**/ // If this node is too far for LOD... - //float distance = node->distanceToCamera(*args->viewFrustum); - //float boundaryDistance = boundaryDistanceForRenderLevel(numberOfThreeBitSectionsInCode(node->getOctalCode())+1); - - //int calcLevel = numberOfThreeBitSectionsInCode(node->getOctalCode())+1; - //printf("level=%d --- calcLevel=%d\n", level, calcLevel); - float distanceSquared = node->distanceSquareToPoint(args->cameraPosition); - float boundaryDistanceSquared = boundaryDistanceSquaredForRenderLevel(level+1); - //float boundaryDistanceSquared = boundaryDistanceSquaredForRenderLevel(calcLevel); - - - //printf("distance=%f, boundaryDistance=%f ---- distanceSquared=%f, boundaryDistanceSquared=%f \n", distance, boundaryDistance, distanceSquared, boundaryDistanceSquared); - + float boundaryDistanceSquared = boundaryDistanceSquaredForRenderLevel(node->getLevel()); //level+1); if (distanceSquared >= boundaryDistanceSquared) { args->tooFar++; if (node->isLeaf()) { @@ -925,133 +886,155 @@ bool scanTreeWithOcclusionOperation(VoxelNode* node, int level, void* extraData) return false; } -return true; // so only counting in view... - - //return true; // just check inview or not +//return true; // so only counting in view/LOD // If we are a parent, let's see if we're completely occluded. - if (!node->isLeaf()) { - args->nonLeaves++; - AABox voxelBox = node->getAABox(); - voxelBox.scale(TREE_SCALE); - VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(args->viewFrustum->getProjectedPolygon(voxelBox)); + if (args->wantsOcclusionCulling) { + if (!node->isLeaf()) { + args->nonLeaves++; + + AABox voxelBox = node->getAABox(); + voxelBox.scale(TREE_SCALE); + VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(args->viewFrustum->getProjectedPolygon(voxelBox)); + + // If we're not all in view, then ignore it, and just return. But keep searching... + if (!voxelPolygon->getAllInView()) { + args->nonLeavesNotAllInView++; + delete voxelPolygon; + + // we can't determine occlusion, so assume it's not + return true; + } + + CoverageMapStorageResult result = args->map->checkMap(voxelPolygon, false); + if (result == OCCLUDED) { + args->nonLeavesOccluded++; + delete voxelPolygon; + + /** Maybe we want to do this? But it's really only for debug accounting ****** + CountSubTreeOperationArgs subArgs; + subArgs.voxelsTouched = 0; + + args->tree->recurseNodeWithOperation(node, countSubTreeOperation, &subArgs ); + args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1); + args->totalVoxels += (subArgs.voxelsTouched - 1); + *****/ + + return false; + } - // If we're not all in view, then ignore it, and just return. But keep searching... - if (!voxelPolygon->getAllInView()) { - args->nonLeavesNotAllInView++; delete voxelPolygon; - - // we can't determine occlusion, so assume it's not - return true; + return true; // keep looking... } - CoverageMapStorageResult result = args->map->checkMap(voxelPolygon, false); - if (result == OCCLUDED) { - args->nonLeavesOccluded++; - delete voxelPolygon; + // we don't want to check shouldRender in the server... that's not used + if (node->isLeaf() && node->isColored() /*&& node->getShouldRender()*/) { + args->coloredVoxels++; - /** Maybe we want to do this? But it's really only for debug accounting ****** - CountSubTreeOperationArgs subArgs; - subArgs.voxelsTouched = 0; - - args->tree->recurseNodeWithOperation(node, countSubTreeOperation, &subArgs ); - args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1); - args->totalVoxels += (subArgs.voxelsTouched - 1); - *****/ - - return false; - } + AABox voxelBox = node->getAABox(); + voxelBox.scale(TREE_SCALE); + VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(args->viewFrustum->getProjectedPolygon(voxelBox)); - delete voxelPolygon; - return true; // keep looking... - } + // If we're not all in view, then ignore it, and just return. But keep searching... + if (!voxelPolygon->getAllInView()) { + args->notAllInView++; + delete voxelPolygon; - // we don't want to check shouldRender in the server... that's not used - if (node->isLeaf() && node->isColored() /*&& node->getShouldRender()*/) { - args->coloredVoxels++; + // But, this is one we do want to send... - AABox voxelBox = node->getAABox(); - voxelBox.scale(TREE_SCALE); - VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(args->viewFrustum->getProjectedPolygon(voxelBox)); + return true; + } - // If we're not all in view, then ignore it, and just return. But keep searching... - if (!voxelPolygon->getAllInView()) { - args->notAllInView++; - delete voxelPolygon; - - // But, this is one we do want to send... - - return true; - } - - CoverageMapStorageResult result = args->map->checkMap(voxelPolygon, true); - if (result == OCCLUDED) { - args->occludedVoxels++; - // This is one we don't want to send... - - } else if (result == STORED) { - args->notOccludedVoxels++; - // This is one we do want to send... - } else if (result == DOESNT_FIT) { - args->doesntFit++; - // Not sure what to do with this one... probably send it? + CoverageMapStorageResult result = args->map->checkMap(voxelPolygon, true); + if (result == OCCLUDED) { + args->occludedVoxels++; + // This is one we don't want to send... + + } else if (result == STORED) { + args->notOccludedVoxels++; + // This is one we do want to send... + } else if (result == DOESNT_FIT) { + args->doesntFit++; + // Not sure what to do with this one... probably send it? + } } } return true; // keep going! } -void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMap* coverageMap, VoxelNode*& leftOffAt) { +PointerStack stackOfNodes; +long long scanStart = 0; +ScanTreeArgs args; + +void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMap* coverageMap, bool wantsOcclusionCulling) { PerformanceWarning warn(true, "scanTreeWithOcclusion()",true); //CoverageMap::wantDebugging = false; //coverageMap->erase(); - ScanTreeArgs args; - args.viewFrustum = viewFrustum; - args.cameraPosition = viewFrustum->getPosition()/(float)TREE_SCALE; - args.map = coverageMap; - args.totalVoxels = 0; - args.coloredVoxels = 0; - args.occludedVoxels = 0; - args.notOccludedVoxels = 0; - args.notAllInView = 0; - args.outOfView = 0; - args.leavesOutOfView = 0; - args.nonLeavesOutOfView = 0; - args.subtreeVoxelsSkipped = 0; - args.nonLeaves = 0; - args.stagedForDeletion = 0; - args.nonLeavesNotAllInView = 0; - args.nonLeavesOccluded = 0; - args.doesntFit = 0; - args.tooFar = 0; - args.leavesTooFar = 0; - args.nonLeavesTooFar = 0; - args.tree = tree; - args.subTreeLocation = ViewFrustum::OUTSIDE; // assume the worst - args.subTreeInside = 0; - args.level = 0; + // if we're starting from scratch, remember our time + if (stackOfNodes.isEmpty()) { + printf("scanTreeWithOcclusion() -----> STARTING \n"); + ::scanStart = usecTimestampNow(); - args.leftOffAt = leftOffAt; - if (leftOffAt) { - args.startSearch = usecTimestampNow(); - args.startScan = 0; - args.stopScan = 0; + args.viewFrustum = viewFrustum; + args.cameraPosition = viewFrustum->getPosition()/(float)TREE_SCALE; + args.map = coverageMap; + args.totalVoxels = 0; + args.coloredVoxels = 0; + args.occludedVoxels = 0; + args.notOccludedVoxels = 0; + args.notAllInView = 0; + args.outOfView = 0; + args.leavesOutOfView = 0; + args.nonLeavesOutOfView = 0; + args.subtreeVoxelsSkipped = 0; + args.nonLeaves = 0; + args.stagedForDeletion = 0; + args.nonLeavesNotAllInView = 0; + args.nonLeavesOccluded = 0; + args.doesntFit = 0; + args.tooFar = 0; + args.leavesTooFar = 0; + args.nonLeavesTooFar = 0; + args.tree = tree; + args.subTreeLocation = ViewFrustum::OUTSIDE; // assume the worst + args.subTreeInside = 0; + args.level = 0; + args.wantsOcclusionCulling = wantsOcclusionCulling; + + VoxelProjectedPolygon::pointInside_calls = 0; + VoxelProjectedPolygon::occludes_calls = 0; + VoxelProjectedPolygon::intersects_calls = 0; } else { - args.startSearch = usecTimestampNow(); - args.startScan = args.startSearch; - args.stopScan = 0; + printf("scanTreeWithOcclusion() -----> still working\n"); } - VoxelProjectedPolygon::pointInside_calls = 0; - VoxelProjectedPolygon::occludes_calls = 0; - VoxelProjectedPolygon::intersects_calls = 0; - glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE); - tree->recurseTreeWithOperationDistanceSorted(scanTreeWithOcclusionOperation, position, (void*)&args); + long allowedTime = 80 * 1000; + tree->recurseTreeWithOperationDistanceSortedTimed(&stackOfNodes, allowedTime, + scanTreeWithOcclusionOperation, position, (void*)&args); + + + if (stackOfNodes.isEmpty()) { + printf("scanTreeWithOcclusion() -----> finished??? \n"); + long long endScan = usecTimestampNow(); + int totalScanmsecs = (endScan - scanStart)/1000; + float elapsedSceneSend = totalScanmsecs/1000.0f; + printf("scanTreeWithOcclusion() -----> totalScanmsecs=%d \n", totalScanmsecs); + printf("scanTreeWithOcclusion() --> elapsed time to send scene = %f seconds, args.totalVoxels=%ld ", elapsedSceneSend,args.totalVoxels); + printf(" [occlusionCulling: %s]\n", debug::valueOf(args.wantsOcclusionCulling)); + + coverageMap->erase(); + + + } else { + printf("scanTreeWithOcclusion() -----> still working\n"); + } + + //tree->recurseTreeWithOperationDistanceSorted(scanTreeWithOcclusionOperation, position, (void*)&args); long long now = usecTimestampNow(); - leftOffAt = args.leftOffAt; printf("scanTreeWithOcclusion()\n"); @@ -1082,19 +1065,4 @@ void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMa printf("\n"); printf(" subTreeInside=%ld\n", args.subTreeInside); - - int searchmsec = (args.startScan - args.startSearch)/1000; - int elapsedmsec = (args.stopScan - args.startScan)/1000; - int unwindmsec = (now - args.stopScan)/1000; - - printf("\n"); - printf(" searchmsec=%d\n", searchmsec); - printf(" elapsedmsec=%d\n", elapsedmsec); - printf(" unwindmsec=%d\n", unwindmsec); - printf(" leftOffAt=%p\n", leftOffAt); - - - CoverageMap::wantDebugging = true; - coverageMap->erase();// print debug results... - } From abd7dfde8bcf4355e794bf9a4539955069d3da69 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 11:10:03 -0700 Subject: [PATCH 24/55] added vector math timing test --- interface/src/Util.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 4bfdb1f587..d9813a231f 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -499,6 +499,19 @@ void runTimingTests() { gettimeofday(&endTime, NULL); elapsedMsecs = diffclock(&startTime, &endTime); printLog("powf(f, 0.5) usecs: %f\n", 1000.0f * elapsedMsecs / (float) numTests); + + // Vector Math + glm::vec3 pointA(randVector()), pointB(randVector()); + gettimeofday(&startTime, NULL); + for (int i = 1; i < numTests; i++) { + //glm::vec3 temp = pointA - pointB; + //float distanceSquared = glm::dot(temp, temp); + float distance = glm::distance(pointA, pointB); + } + gettimeofday(&endTime, NULL); + elapsedMsecs = diffclock(&startTime, &endTime); + printLog("vector math usecs: %f [%f msecs total for %d tests]\n", + 1000.0f * elapsedMsecs / (float) numTests, elapsedMsecs, numTests); } From a9b1aa727fbbe84c3bad3170059cc46cc78e7b21 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 11:11:21 -0700 Subject: [PATCH 25/55] added back stars file loading --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 66fc55c51c..c5ee474ae2 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1653,7 +1653,7 @@ void Application::init() { _headMouseX = _mouseX = _glWidget->width() / 2; _headMouseY = _mouseY = _glWidget->height() / 2; - //_stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0); + _stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0); _myAvatar.init(); _myAvatar.setPosition(START_LOCATION); From d8b72cb1268ab05dbcaccd4e133e173c82c34437 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 11:17:23 -0700 Subject: [PATCH 26/55] removed some comments --- libraries/voxels/src/CoverageMap.cpp | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/libraries/voxels/src/CoverageMap.cpp b/libraries/voxels/src/CoverageMap.cpp index bd00179d9e..230df4db69 100644 --- a/libraries/voxels/src/CoverageMap.cpp +++ b/libraries/voxels/src/CoverageMap.cpp @@ -543,24 +543,3 @@ CoverageMapStorageResult CoverageRegion::checkRegion(VoxelProjectedPolygon* poly } return result; } - - - - -///////////////////////////////////////////////////////////////// -// Notes on improvements. -// -// Let's say that we are going to combine polygon projects together if they intersect. How would we do that? -// -// On "check/insert"... -// We start at top of QuadTree, and we check to see if the checkpolygon's bounding box overlaps with any bounding boxes of -// polygons in the current quad level. -// If it overlaps, we check to see if the "in map" polygon occludes the checkPolygon. -// This operation could create side data that tells us: -// 1) checkPolygon is COMPLETELY outside of levelPolygon << If so, no occlusion, and can't be combined -// 2) checkPolygon is COMPLETELY INSIDE of levelPolygon << If so, it is occluded and does not need to be combined -// 3) checkPolygon has some points INSIDE some OUTSIDE -// 3a) which vertices are "inside" -// 3b) which vertices are "outside" -// 3c) for all pairs of vertices for which one is "inside" and the other "outside" we can determine an -// intersection point. This point will be used in our "union" From ed8c33ef4b2911a57b8bb81f331d6c64f1d6cf31 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 11:19:49 -0700 Subject: [PATCH 27/55] removed some comments --- libraries/voxels/src/GeometryUtil.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/libraries/voxels/src/GeometryUtil.cpp b/libraries/voxels/src/GeometryUtil.cpp index 69b566f857..af2a6dfc95 100644 --- a/libraries/voxels/src/GeometryUtil.cpp +++ b/libraries/voxels/src/GeometryUtil.cpp @@ -127,17 +127,11 @@ bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm int d3 = computeDirection(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p1.x, r2p1.y); int d4 = computeDirection(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p2.x, r2p2.y); return (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && - ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) - - // I think these following conditions are what handle the case of one end point - // being exactly on the second line. It seems like we sometimes want this to be - // considered "inside" and other times, maybe we don't... - || + ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) || (d1 == 0 && isOnSegment(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p1.x, r1p1.y)) || (d2 == 0 && isOnSegment(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p2.x, r1p2.y)) || (d3 == 0 && isOnSegment(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p1.x, r2p1.y)) || - (d4 == 0 && isOnSegment(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p2.x, r2p2.y)) - ; + (d4 == 0 && isOnSegment(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p2.x, r2p2.y)); } bool isOnSegment(float xi, float yi, float xj, float yj, float xk, float yk) { From 845b8924a568db677d651ba8582e64e83471da29 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 11:21:38 -0700 Subject: [PATCH 28/55] removed some dead code --- libraries/voxels/src/ViewFrustum.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index aaa03855d3..0245bdc0c1 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -275,7 +275,6 @@ ViewFrustum::location ViewFrustum::boxInFrustum(const AABox& box) const { keyholeResult = boxInKeyhole(box); } if (keyholeResult == INSIDE) { -//printLog("ViewFrustum::boxInFrustum() keyholeResult == INSIDE _keyholeRadius=%f\n",_keyholeRadius); return keyholeResult; } From de7cb9227a789af14ba985f742d2a5a4238b6b89 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 11:31:45 -0700 Subject: [PATCH 29/55] removed some dead code --- voxel-server/src/main.cpp | 47 ++++----------------------------------- 1 file changed, 4 insertions(+), 43 deletions(-) diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 1ccf02ba8c..a7d662fdeb 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -65,7 +65,6 @@ bool wantSearchForColoredNodes = false; EnvironmentData environmentData[3]; -VoxelNode* leftOffAt = NULL; void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMap* coverageMap, bool wantsOcclusionCulling); @@ -818,31 +817,6 @@ bool scanTreeWithOcclusionOperation(VoxelNode* node, int level, void* extraData) //return true; -/*** - - // If we had a previous leftOffAt, then keep going until we get find it - if (args->leftOffAt) { - // if this isn't our node, the just keep searching - if (node != args->leftOffAt) { - return true; - } - // otherwise, stop searching and start processing - args->leftOffAt = NULL; - args->startScan = usecTimestampNow(); - } else { - // bail if we've taken too long - long long now = usecTimestampNow(); - if (now - args->startScan > MAX_SCAN) { - // if we just stopped, then record this stop time - if (args->stopScan == 0) { - args->stopScan = now; - args->leftOffAt = node; - } - return false; - } - } -**/ - args->totalVoxels++; // SOME FACTS WE KNOW... @@ -850,7 +824,6 @@ bool scanTreeWithOcclusionOperation(VoxelNode* node, int level, void* extraData) // shouldn't have to check that any more... use InFrustum() and store something in args to know // that that part of the stack is in frustum. (do we also need to store a pointer to the parent node // that is INSIDE so that when we unwind back to that point we start checking again?... probably) -/**/ if (args->subTreeLocation == ViewFrustum::INSIDE) { args->subTreeInside++; // continue without checking further @@ -869,10 +842,11 @@ bool scanTreeWithOcclusionOperation(VoxelNode* node, int level, void* extraData) } // remember this for next recursion pass // NOTE: This doesn't actually work because we need a stack of these, and our code doesn't execute - // to pop the stack. Similar problem with level. + // to pop the stack. Similar problem with level. So we need a solution to allow operation functions to + // store information on a stack. //args->subTreeLocation = location; } -/**/ + // If this node is too far for LOD... float distanceSquared = node->distanceSquareToPoint(args->cameraPosition); float boundaryDistanceSquared = boundaryDistanceSquaredForRenderLevel(node->getLevel()); //level+1); @@ -886,10 +860,7 @@ bool scanTreeWithOcclusionOperation(VoxelNode* node, int level, void* extraData) return false; } -//return true; // so only counting in view/LOD - - // If we are a parent, let's see if we're completely occluded. - + // Handle occlusion culling tests. if (args->wantsOcclusionCulling) { if (!node->isLeaf()) { args->nonLeaves++; @@ -911,16 +882,6 @@ bool scanTreeWithOcclusionOperation(VoxelNode* node, int level, void* extraData) if (result == OCCLUDED) { args->nonLeavesOccluded++; delete voxelPolygon; - - /** Maybe we want to do this? But it's really only for debug accounting ****** - CountSubTreeOperationArgs subArgs; - subArgs.voxelsTouched = 0; - - args->tree->recurseNodeWithOperation(node, countSubTreeOperation, &subArgs ); - args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1); - args->totalVoxels += (subArgs.voxelsTouched - 1); - *****/ - return false; } From c1080bca1aa21342f286d0b553dbe31f49280ea3 Mon Sep 17 00:00:00 2001 From: Eric Johnston Date: Wed, 10 Jul 2013 11:32:18 -0700 Subject: [PATCH 30/55] Added a read me with instructions on installing the Leap libraries. --- interface/external/Leap/readme.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 interface/external/Leap/readme.txt diff --git a/interface/external/Leap/readme.txt b/interface/external/Leap/readme.txt new file mode 100644 index 0000000000..e69de29bb2 From ca8f25955a923affe42a19bfce89015b07fd7f12 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 10 Jul 2013 11:46:48 -0700 Subject: [PATCH 31/55] switch usec timestamps to uint64_t --- animation-server/src/main.cpp | 6 ++--- audio-mixer/src/main.cpp | 4 +-- domain-server/src/main.cpp | 2 +- eve/src/main.cpp | 2 +- interface/src/Application.cpp | 4 +-- interface/src/VoxelSystem.cpp | 12 ++++----- interface/src/Webcam.cpp | 4 +-- interface/src/Webcam.h | 4 +-- libraries/audio/src/AudioInjector.cpp | 2 +- libraries/audio/src/AudioInjector.h | 2 +- libraries/shared/src/Node.h | 12 ++++----- libraries/shared/src/NodeList.cpp | 7 ++--- libraries/shared/src/PerfStat.cpp | 2 +- libraries/shared/src/PerfStat.h | 2 +- libraries/shared/src/SharedUtil.cpp | 4 +-- libraries/shared/src/SharedUtil.h | 4 +-- libraries/shared/src/SimpleMovingAverage.h | 2 +- libraries/voxels/src/VoxelNode.h | 4 +-- voxel-server/src/VoxelNodeData.h | 6 ++--- voxel-server/src/main.cpp | 30 +++++++++++----------- 20 files changed, 58 insertions(+), 57 deletions(-) diff --git a/animation-server/src/main.cpp b/animation-server/src/main.cpp index 8a3d70d83d..963d8f73b8 100644 --- a/animation-server/src/main.cpp +++ b/animation-server/src/main.cpp @@ -643,14 +643,14 @@ void* animateVoxels(void* args) { sendDanceFloor(); } - long long end = usecTimestampNow(); - long long elapsedSeconds = (end - ::start) / 1000000; + uint64_t end = usecTimestampNow(); + int elapsedSeconds = (end - ::start) / 1000000; if (::shouldShowPacketsPerSecond) { printf("packetsSent=%ld, bytesSent=%ld pps=%f bps=%f\n",packetsSent,bytesSent, (float)(packetsSent/elapsedSeconds),(float)(bytesSent/elapsedSeconds)); } // dynamically sleep until we need to fire off the next set of voxels - long long usecToSleep = ANIMATE_VOXELS_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); + uint64_t usecToSleep = ANIMATE_VOXELS_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); if (usecToSleep > 0) { usleep(usecToSleep); diff --git a/audio-mixer/src/main.cpp b/audio-mixer/src/main.cpp index 26f4bb23f8..68d66ac7ac 100644 --- a/audio-mixer/src/main.cpp +++ b/audio-mixer/src/main.cpp @@ -47,7 +47,7 @@ const unsigned short MIXER_LISTEN_PORT = 55443; const short JITTER_BUFFER_MSECS = 12; const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0); -const long long BUFFER_SEND_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000); +const unsigned int BUFFER_SEND_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000); const long MAX_SAMPLE_VALUE = std::numeric_limits::max(); const long MIN_SAMPLE_VALUE = std::numeric_limits::min(); @@ -412,7 +412,7 @@ int main(int argc, const char* argv[]) { numStatCollections++; } - long long usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow(); + int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow(); if (usecToSleep > 0) { usleep(usecToSleep); diff --git a/domain-server/src/main.cpp b/domain-server/src/main.cpp index 6fd5fae887..b4174d986f 100644 --- a/domain-server/src/main.cpp +++ b/domain-server/src/main.cpp @@ -159,7 +159,7 @@ int main(int argc, const char * argv[]) } // update last receive to now - long long timeNow = usecTimestampNow(); + uint64_t timeNow = usecTimestampNow(); newNode->setLastHeardMicrostamp(timeNow); if (packetData[0] == PACKET_HEADER_DOMAIN_REPORT_FOR_DUTY diff --git a/eve/src/main.cpp b/eve/src/main.cpp index 8a03dc453b..5e7eb5d541 100644 --- a/eve/src/main.cpp +++ b/eve/src/main.cpp @@ -128,7 +128,7 @@ int main(int argc, const char* argv[]) { broadcastPacket[0] = PACKET_HEADER_HEAD_DATA; timeval thisSend; - long long numMicrosecondsSleep = 0; + int numMicrosecondsSleep = 0; int handStateTimer = 0; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5a0b45960e..5368e43e6f 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -903,7 +903,7 @@ void Application::wheelEvent(QWheelEvent* event) { void Application::sendPingPackets() { char nodeTypesOfInterest[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER}; - long long currentTime = usecTimestampNow(); + uint64_t currentTime = usecTimestampNow(); unsigned char pingPacket[1 + sizeof(currentTime)]; pingPacket[0] = PACKET_HEADER_PING; @@ -2429,7 +2429,7 @@ void Application::displayOverlay() { // Show on-screen msec timer if (_renderFrameTimerOn->isChecked()) { char frameTimer[10]; - long long mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5); + uint64_t mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5); sprintf(frameTimer, "%d\n", (int)(mSecsNow % 1000)); drawtext(_glWidget->width() - 100, _glWidget->height() - 20, 0.30, 0, 1.0, 0, frameTimer, 0, 0, 0); drawtext(_glWidget->width() - 102, _glWidget->height() - 22, 0.30, 0, 1.0, 0, frameTimer, 1, 1, 1); diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 97c3e4bcd5..00b8e7486c 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -168,15 +168,15 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { void VoxelSystem::setupNewVoxelsForDrawing() { PerformanceWarning warn(_renderWarningsOn, "setupNewVoxelsForDrawing()"); // would like to include _voxelsInArrays, _voxelsUpdated - long long start = usecTimestampNow(); - long long sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000; + uint64_t start = usecTimestampNow(); + int sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000; bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging if (!iAmDebugging && sinceLastTime <= std::max(_setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) { return; // bail early, it hasn't been long enough since the last time we ran } - long long sinceLastViewCulling = (start - _lastViewCulling) / 1000; + int sinceLastViewCulling = (start - _lastViewCulling) / 1000; // If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view if ((sinceLastViewCulling >= std::max(_lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)) && !isViewChanging() && hasViewChanged()) { @@ -192,7 +192,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { // VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary. cleanupRemovedVoxels(); - long long endViewCulling = usecTimestampNow(); + uint64_t endViewCulling = usecTimestampNow(); _lastViewCullingElapsed = (endViewCulling - start) / 1000; } @@ -229,8 +229,8 @@ void VoxelSystem::setupNewVoxelsForDrawing() { pthread_mutex_unlock(&_bufferWriteLock); - long long end = usecTimestampNow(); - long long elapsedmsec = (end - start) / 1000; + uint64_t end = usecTimestampNow(); + int elapsedmsec = (end - start) / 1000; _setupNewVoxelsForDrawingLastFinished = end; _setupNewVoxelsForDrawingLastElapsed = elapsedmsec; } diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 6c7006c5f4..1a5aa9fa7c 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -191,8 +191,8 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Rota const int MAX_FPS = 60; const int MIN_FRAME_DELAY = 1000000 / MAX_FPS; - long long now = usecTimestampNow(); - long long remaining = MIN_FRAME_DELAY; + uint64_t now = usecTimestampNow(); + int remaining = MIN_FRAME_DELAY; if (_startTimestamp == 0) { _startTimestamp = now; } else { diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index f0910c7bce..c39f8660da 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -73,10 +73,10 @@ private: cv::RotatedRect _initialFaceRect; JointVector _joints; - long long _startTimestamp; + uint64_t _startTimestamp; int _frameCount; - long long _lastFrameTimestamp; + uint64_t _lastFrameTimestamp; glm::vec3 _estimatedPosition; glm::vec3 _estimatedRotation; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 16c381a036..d97ea8abdd 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -115,7 +115,7 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination injectorSocket->send(destinationSocket, dataPacket, sizeof(dataPacket)); - long long usecToSleep = usecTimestamp(&startTime) + (++nextFrame * INJECT_INTERVAL_USECS) - usecTimestampNow(); + int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * INJECT_INTERVAL_USECS) - usecTimestampNow(); if (usecToSleep > 0) { usleep(usecToSleep); } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 29ff920317..875bc815ce 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -21,7 +21,7 @@ const int STREAM_IDENTIFIER_NUM_BYTES = 8; const int MAX_INJECTOR_VOLUME = 0xFF; -const long long INJECT_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000); +const int INJECT_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000); class AudioInjector { public: diff --git a/libraries/shared/src/Node.h b/libraries/shared/src/Node.h index 7bafbd5b58..de43558b7a 100644 --- a/libraries/shared/src/Node.h +++ b/libraries/shared/src/Node.h @@ -37,11 +37,11 @@ public: uint16_t getNodeID() const { return _nodeID; } void setNodeID(uint16_t nodeID) { _nodeID = nodeID;} - long long getWakeMicrostamp() const { return _wakeMicrostamp; } - void setWakeMicrostamp(long long wakeMicrostamp) { _wakeMicrostamp = wakeMicrostamp; } + uint64_t getWakeMicrostamp() const { return _wakeMicrostamp; } + void setWakeMicrostamp(uint64_t wakeMicrostamp) { _wakeMicrostamp = wakeMicrostamp; } - long long getLastHeardMicrostamp() const { return _lastHeardMicrostamp; } - void setLastHeardMicrostamp(long long lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; } + uint64_t getLastHeardMicrostamp() const { return _lastHeardMicrostamp; } + void setLastHeardMicrostamp(uint64_t lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; } sockaddr* getPublicSocket() const { return _publicSocket; } void setPublicSocket(sockaddr* publicSocket) { _publicSocket = publicSocket; } @@ -74,8 +74,8 @@ private: char _type; uint16_t _nodeID; - long long _wakeMicrostamp; - long long _lastHeardMicrostamp; + uint64_t _wakeMicrostamp; + uint64_t _lastHeardMicrostamp; sockaddr* _publicSocket; sockaddr* _localSocket; sockaddr* _activeSocket; diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 832c81cd24..75fa0ed1d9 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -81,7 +81,7 @@ void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) { for(NodeList::iterator node = begin(); node != end(); node++) { if (socketMatch(node->getPublicSocket(), nodeAddress) || socketMatch(node->getLocalSocket(), nodeAddress)) { - int pingTime = usecTimestampNow() - *(long long *)(packetData + 1); + int pingTime = usecTimestampNow() - *(uint64_t *)(packetData + 1); node->setPingMs(pingTime / 1000); break; } @@ -422,7 +422,7 @@ void *pingUnknownNodes(void *args) { } } - long long usecToSleep = PING_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSend)); + int usecToSleep = PING_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSend)); if (usecToSleep > 0) { usleep(usecToSleep); @@ -443,7 +443,8 @@ void NodeList::stopPingUnknownNodesThread() { void *removeSilentNodes(void *args) { NodeList* nodeList = (NodeList*) args; - long long checkTimeUSecs, sleepTime; + uint64_t checkTimeUSecs; + int sleepTime; while (!silentNodeThreadStopFlag) { checkTimeUSecs = usecTimestampNow(); diff --git a/libraries/shared/src/PerfStat.cpp b/libraries/shared/src/PerfStat.cpp index c376cfcb3e..3cf272ecf8 100644 --- a/libraries/shared/src/PerfStat.cpp +++ b/libraries/shared/src/PerfStat.cpp @@ -105,7 +105,7 @@ int PerfStat::DumpStats(char** array) { // Destructor handles recording all of our stats PerformanceWarning::~PerformanceWarning() { - long long end = usecTimestampNow(); + uint64_t end = usecTimestampNow(); double elapsedmsec = (end - _start) / 1000.0; if ((_alwaysDisplay || _renderWarningsOn) && elapsedmsec > 1) { if (elapsedmsec > 1000) { diff --git a/libraries/shared/src/PerfStat.h b/libraries/shared/src/PerfStat.h index d39c1f31fb..22eb5d38b5 100644 --- a/libraries/shared/src/PerfStat.h +++ b/libraries/shared/src/PerfStat.h @@ -85,7 +85,7 @@ typedef std::map >::iterator class PerformanceWarning { private: - long long _start; + uint64_t _start; const char* _message; bool _renderWarningsOn; bool _alwaysDisplay; diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index bdc35ff3f5..4aef5d8a6a 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -22,11 +22,11 @@ #include #endif -long long usecTimestamp(timeval *time) { +uint64_t usecTimestamp(timeval *time) { return (time->tv_sec * 1000000 + time->tv_usec); } -long long usecTimestampNow() { +uint64_t usecTimestampNow() { timeval now; gettimeofday(&now, NULL); return (now.tv_sec * 1000000 + now.tv_usec); diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 2c9e1e6317..dd9e0c1446 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -36,8 +36,8 @@ static const float DECIMETER = 0.1f; static const float CENTIMETER = 0.01f; static const float MILLIIMETER = 0.001f; -long long usecTimestamp(timeval *time); -long long usecTimestampNow(); +uint64_t usecTimestamp(timeval *time); +uint64_t usecTimestampNow(); float randFloat(); int randIntInRange (int min, int max); diff --git a/libraries/shared/src/SimpleMovingAverage.h b/libraries/shared/src/SimpleMovingAverage.h index 7c8605db5d..2d23400f7b 100644 --- a/libraries/shared/src/SimpleMovingAverage.h +++ b/libraries/shared/src/SimpleMovingAverage.h @@ -26,7 +26,7 @@ public: float getAverageSampleValuePerSecond(); private: int _numSamples; - long long _lastEventTimestamp; + uint64_t _lastEventTimestamp; float _average; float _eventDeltaAverage; diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index 4aa64f019e..284821c3bd 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -29,7 +29,7 @@ private: #endif glBufferIndex _glBufferIndex; bool _isDirty; - long long _lastChanged; + uint64_t _lastChanged; bool _shouldRender; bool _isStagedForDeletion; AABox _box; @@ -80,7 +80,7 @@ public: void printDebugDetails(const char* label) const; bool isDirty() const { return _isDirty; }; void clearDirtyBit() { _isDirty = false; }; - bool hasChangedSince(long long time) const { return (_lastChanged > time); }; + bool hasChangedSince(uint64_t time) const { return (_lastChanged > time); }; void markWithChangedTime() { _lastChanged = usecTimestampNow(); }; void handleSubtreeChanged(VoxelTree* myTree); diff --git a/voxel-server/src/VoxelNodeData.h b/voxel-server/src/VoxelNodeData.h index ded4093f41..48ddee8aa4 100644 --- a/voxel-server/src/VoxelNodeData.h +++ b/voxel-server/src/VoxelNodeData.h @@ -50,8 +50,8 @@ public: bool getViewSent() const { return _viewSent; }; void setViewSent(bool viewSent) { _viewSent = viewSent; } - long long getLastTimeBagEmpty() const { return _lastTimeBagEmpty; }; - void setLastTimeBagEmpty(long long lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; }; + uint64_t getLastTimeBagEmpty() const { return _lastTimeBagEmpty; }; + void setLastTimeBagEmpty(uint64_t lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; }; private: @@ -67,7 +67,7 @@ private: int _maxLevelReachedInLastSearch; ViewFrustum _currentViewFrustum; ViewFrustum _lastKnownViewFrustum; - long long _lastTimeBagEmpty; + uint64_t _lastTimeBagEmpty; }; diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 64a4565b6c..4901ea27d3 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -32,7 +32,7 @@ const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo"; const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo"; -const long long VOXEL_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds +const int VOXEL_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds const int VOXEL_LISTEN_PORT = 40106; @@ -119,7 +119,7 @@ void resInVoxelDistributor(NodeList* nodeList, bool searchReset = false; int searchLoops = 0; int searchLevelWas = nodeData->getMaxSearchLevel(); - long long start = usecTimestampNow(); + uint64_t start = usecTimestampNow(); while (!searchReset && nodeData->nodeBag.isEmpty()) { searchLoops++; @@ -138,7 +138,7 @@ void resInVoxelDistributor(NodeList* nodeList, } } } - long long end = usecTimestampNow(); + uint64_t end = usecTimestampNow(); int elapsedmsec = (end - start)/1000; if (elapsedmsec > 100) { if (elapsedmsec > 1000) { @@ -162,7 +162,7 @@ void resInVoxelDistributor(NodeList* nodeList, int packetsSentThisInterval = 0; int truePacketsSent = 0; int trueBytesSent = 0; - long long start = usecTimestampNow(); + uint64_t start = usecTimestampNow(); bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { @@ -209,7 +209,7 @@ void resInVoxelDistributor(NodeList* nodeList, trueBytesSent += envPacketLength; truePacketsSent++; } - long long end = usecTimestampNow(); + uint64_t end = usecTimestampNow(); int elapsedmsec = (end - start)/1000; if (elapsedmsec > 100) { if (elapsedmsec > 1000) { @@ -248,7 +248,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, pthread_mutex_lock(&::treeLock); int maxLevelReached = 0; - long long start = usecTimestampNow(); + uint64_t start = usecTimestampNow(); // FOR NOW... node tells us if it wants to receive only view frustum deltas bool wantDelta = nodeData->getWantDelta(); @@ -267,7 +267,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, if (::debugVoxelSending) { printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); - long long now = usecTimestampNow(); + uint64_t now = usecTimestampNow(); if (nodeData->getLastTimeBagEmpty() > 0) { float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f; @@ -306,7 +306,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, } } - long long end = usecTimestampNow(); + uint64_t end = usecTimestampNow(); int elapsedmsec = (end - start)/1000; if (elapsedmsec > 100) { if (elapsedmsec > 1000) { @@ -329,12 +329,12 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, int packetsSentThisInterval = 0; int truePacketsSent = 0; int trueBytesSent = 0; - long long start = usecTimestampNow(); + uint64_t start = usecTimestampNow(); bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { // Check to see if we're taking too long, and if so bail early... - long long now = usecTimestampNow(); + uint64_t now = usecTimestampNow(); long elapsedUsec = (now - start); long elapsedUsecPerPacket = (truePacketsSent == 0) ? 0 : (elapsedUsec / truePacketsSent); long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec); @@ -396,7 +396,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, truePacketsSent++; } - long long end = usecTimestampNow(); + uint64_t end = usecTimestampNow(); int elapsedmsec = (end - start)/1000; if (elapsedmsec > 100) { if (elapsedmsec > 1000) { @@ -425,10 +425,10 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, pthread_mutex_unlock(&::treeLock); } -long long lastPersistVoxels = 0; +uint64_t lastPersistVoxels = 0; void persistVoxelsWhenDirty() { - long long now = usecTimestampNow(); - long long sinceLastTime = (now - ::lastPersistVoxels) / 1000; + uint64_t now = usecTimestampNow(); + int sinceLastTime = (now - ::lastPersistVoxels) / 1000; // check the dirty bit and persist here... if (::wantVoxelPersist && ::serverTree.isDirty() && sinceLastTime > VOXEL_PERSIST_INTERVAL) { @@ -473,7 +473,7 @@ void *distributeVoxelsToListeners(void *args) { } // dynamically sleep until we need to fire off the next set of voxels - long long usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); + int usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); if (usecToSleep > 0) { usleep(usecToSleep); From 02437c6f39cfbd132b39ca89b78fafb69f62a19b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 13:14:23 -0700 Subject: [PATCH 32/55] fix random big average voxels slightly in view bug --- libraries/voxels/src/VoxelTree.cpp | 34 ++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index d911c69f4a..2f0f702795 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1340,9 +1340,39 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp bool childWasInView = (childNode && params.deltaViewFrustum && (params.lastViewFrustum && ViewFrustum::INSIDE == childNode->inFrustum(*params.lastViewFrustum))); - + + // 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 and beyond our LOD. + // NOTE: This code works, but it's pretty expensive, because we're calculating distances for all the grand + // children, which we'll end up doing again later in the next level of recursion. We need to optimize this + // in the future. + bool isLeafOrLOD = childNode->isLeaf(); + if (params.viewFrustum && childNode->isColored() && !childNode->isLeaf()) { + int grandChildrenInView = 0; + int grandChildrenInLOD = 0; + for (int grandChildIndex = 0; grandChildIndex < NUMBER_OF_CHILDREN; grandChildIndex++) { + VoxelNode* grandChild = childNode->getChildAtIndex(grandChildIndex); + + if (grandChild && grandChild->isColored() && grandChild->isInView(*params.viewFrustum)) { + grandChildrenInView++; + + float grandChildDistance = grandChild->distanceToCamera(*params.viewFrustum); + float grandChildBoundaryDistance = boundaryDistanceForRenderLevel(grandChild->getLevel() + 1); + if (grandChildDistance < grandChildBoundaryDistance) { + grandChildrenInLOD++; + } + } + } + // if any of our grandchildren ARE in view, then we don't want to include our color. If none are, then + // we do want to include our color + if (grandChildrenInView > 0 && grandChildrenInLOD==0) { + isLeafOrLOD = true; + } + } + // track children with actual color, only if the child wasn't previously in view! - if (childNode && childNode->isColored() && !childWasInView && !childIsOccluded) { + if (childNode && isLeafOrLOD && childNode->isColored() && !childWasInView && !childIsOccluded) { childrenColoredBits += (1 << (7 - originalIndex)); inViewWithColorCount++; } From 09f77279e2d95544896ac5364229bced37d868ac Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 13:42:43 -0700 Subject: [PATCH 33/55] add shortcut key to enable wants occlusion culling --- interface/src/Application.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c5ee474ae2..f06b22f40c 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1586,7 +1586,8 @@ void Application::initMenu() { debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true); debugMenu->addAction("Wants View Delta Sending", this, SLOT(setWantsDelta(bool)))->setCheckable(true); (_shouldLowPassFilter = debugMenu->addAction("Test: LowPass filter"))->setCheckable(true); - debugMenu->addAction("Wants Occlusion Culling", this, SLOT(setWantsOcclusionCulling(bool)))->setCheckable(true); + debugMenu->addAction("Wants Occlusion Culling", this, SLOT(setWantsOcclusionCulling(bool)), + Qt::SHIFT | Qt::Key_C)->setCheckable(true); (_renderCoverageMap = debugMenu->addAction("Render Coverage Map"))->setCheckable(true); _renderCoverageMap->setShortcut(Qt::SHIFT | Qt::CTRL | Qt::Key_O); From 38fe6201a858a7e1c6c33cc26940c2e6bc09f411 Mon Sep 17 00:00:00 2001 From: Eric Johnston Date: Wed, 10 Jul 2013 15:33:28 -0700 Subject: [PATCH 34/55] Added content to read me --- interface/external/Leap/readme.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/interface/external/Leap/readme.txt b/interface/external/Leap/readme.txt index e69de29bb2..24a79299c0 100644 --- a/interface/external/Leap/readme.txt +++ b/interface/external/Leap/readme.txt @@ -0,0 +1,11 @@ + +Instructions for adding the Leap driver to Interface +Eric Johnston, July 10, 2013 + +NOTE: Without doing step 2, you will crash at program start time. + +1. Copy the Leap sdk folders (lib, include, etc.) into the interface/external/Leap folder. There should be a folder already there called "stub", and this read me.txt should be there as well. + +2. IMPORTANT: Copy the file interface/external/Leap/lib/libc++/libLeap.dylib to /usr/lib + +3. Delete your build directory, run cmake and build, and you should be all set. From d8f368a50c1aa6a23fe71f1bc1d693e8f1b7f83a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 16:06:23 -0700 Subject: [PATCH 35/55] lazy load stars file only if stars render enabled --- interface/src/Application.cpp | 6 +++--- interface/src/Stars.cpp | 5 +++-- interface/src/Stars.h | 3 +++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6480a39539..10fc5c4cfd 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1682,8 +1682,6 @@ void Application::init() { _headMouseX = _mouseX = _glWidget->width() / 2; _headMouseY = _mouseY = _glWidget->height() / 2; - _stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0); - _myAvatar.init(); _myAvatar.setPosition(START_LOCATION); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); @@ -1709,7 +1707,6 @@ void Application::init() { printLog("Loaded settings.\n"); - sendAvatarVoxelURLMessage(_myAvatar.getVoxels()->getVoxelURL()); _palette.init(_glWidget->width(), _glWidget->height()); @@ -2275,6 +2272,9 @@ void Application::displaySide(Camera& whichCamera) { glMateriali(GL_FRONT, GL_SHININESS, 96); if (_renderStarsOn->isChecked()) { + if (!_stars.getFileLoaded()) { + _stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0); + } // should be the first rendering pass - w/o depth buffer / lighting // compute starfield alpha based on distance from atmosphere diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp index 7934190e4c..e663ef33bd 100644 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -14,7 +14,7 @@ #undef __interface__Starfield_impl__ Stars::Stars() : - _controller(0l) { + _controller(0l), _fileLoaded(false) { _controller = new starfield::Controller; } @@ -23,7 +23,8 @@ Stars::~Stars() { } bool Stars::readInput(const char* url, const char* cacheFile, unsigned limit) { - return _controller->readInput(url, cacheFile, limit); + _fileLoaded = _controller->readInput(url, cacheFile, limit); + return _fileLoaded; } bool Stars::setResolution(unsigned k) { diff --git a/interface/src/Stars.h b/interface/src/Stars.h index ac2abcde42..83d55d3766 100644 --- a/interface/src/Stars.h +++ b/interface/src/Stars.h @@ -65,6 +65,7 @@ class Stars { float changeLOD(float factor, float overalloc = 0.25, float realloc = 0.15); + bool getFileLoaded() const { return _fileLoaded; }; private: // don't copy/assign Stars(Stars const&); // = delete; @@ -73,6 +74,8 @@ class Stars { // variables starfield::Controller* _controller; + + bool _fileLoaded; }; From 1439b47e846052079da993d9907100e464193e1e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 16:09:45 -0700 Subject: [PATCH 36/55] only get boundary distance once, since its the same for all grandchildren --- libraries/voxels/src/VoxelTree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 207573b860..0df9ff0885 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1284,6 +1284,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp if (params.viewFrustum && childNode->isColored() && !childNode->isLeaf()) { int grandChildrenInView = 0; int grandChildrenInLOD = 0; + float grandChildBoundaryDistance = boundaryDistanceForRenderLevel(childNode->getLevel() + 2); for (int grandChildIndex = 0; grandChildIndex < NUMBER_OF_CHILDREN; grandChildIndex++) { VoxelNode* grandChild = childNode->getChildAtIndex(grandChildIndex); @@ -1291,7 +1292,6 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp grandChildrenInView++; float grandChildDistance = grandChild->distanceToCamera(*params.viewFrustum); - float grandChildBoundaryDistance = boundaryDistanceForRenderLevel(grandChild->getLevel() + 1); if (grandChildDistance < grandChildBoundaryDistance) { grandChildrenInLOD++; } From 3c150c3ec648cb15bb6b76dee2c1fd3696412acf Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 16:09:45 -0700 Subject: [PATCH 37/55] only get boundary distance once, since its the same for all grandchildren --- libraries/voxels/src/VoxelTree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 2f0f702795..70e88bd7b5 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1351,6 +1351,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp if (params.viewFrustum && childNode->isColored() && !childNode->isLeaf()) { int grandChildrenInView = 0; int grandChildrenInLOD = 0; + float grandChildBoundaryDistance = boundaryDistanceForRenderLevel(childNode->getLevel() + 2); for (int grandChildIndex = 0; grandChildIndex < NUMBER_OF_CHILDREN; grandChildIndex++) { VoxelNode* grandChild = childNode->getChildAtIndex(grandChildIndex); @@ -1358,7 +1359,6 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp grandChildrenInView++; float grandChildDistance = grandChild->distanceToCamera(*params.viewFrustum); - float grandChildBoundaryDistance = boundaryDistanceForRenderLevel(grandChild->getLevel() + 1); if (grandChildDistance < grandChildBoundaryDistance) { grandChildrenInLOD++; } From 78c62f29c3983ef7dd39c0da1267ce8c20b1e482 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 16:06:23 -0700 Subject: [PATCH 38/55] lazy load stars file only if stars render enabled --- interface/src/Application.cpp | 6 +++--- interface/src/Stars.cpp | 5 +++-- interface/src/Stars.h | 3 +++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f06b22f40c..dc97742ea9 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1654,8 +1654,6 @@ void Application::init() { _headMouseX = _mouseX = _glWidget->width() / 2; _headMouseY = _mouseY = _glWidget->height() / 2; - _stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0); - _myAvatar.init(); _myAvatar.setPosition(START_LOCATION); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); @@ -1681,7 +1679,6 @@ void Application::init() { printLog("Loaded settings.\n"); - sendAvatarVoxelURLMessage(_myAvatar.getVoxels()->getVoxelURL()); } @@ -2238,6 +2235,9 @@ void Application::displaySide(Camera& whichCamera) { glMateriali(GL_FRONT, GL_SHININESS, 96); if (_renderStarsOn->isChecked()) { + if (!_stars.getFileLoaded()) { + _stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0); + } // should be the first rendering pass - w/o depth buffer / lighting // compute starfield alpha based on distance from atmosphere diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp index 7934190e4c..e663ef33bd 100644 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -14,7 +14,7 @@ #undef __interface__Starfield_impl__ Stars::Stars() : - _controller(0l) { + _controller(0l), _fileLoaded(false) { _controller = new starfield::Controller; } @@ -23,7 +23,8 @@ Stars::~Stars() { } bool Stars::readInput(const char* url, const char* cacheFile, unsigned limit) { - return _controller->readInput(url, cacheFile, limit); + _fileLoaded = _controller->readInput(url, cacheFile, limit); + return _fileLoaded; } bool Stars::setResolution(unsigned k) { diff --git a/interface/src/Stars.h b/interface/src/Stars.h index ac2abcde42..83d55d3766 100644 --- a/interface/src/Stars.h +++ b/interface/src/Stars.h @@ -65,6 +65,7 @@ class Stars { float changeLOD(float factor, float overalloc = 0.25, float realloc = 0.15); + bool getFileLoaded() const { return _fileLoaded; }; private: // don't copy/assign Stars(Stars const&); // = delete; @@ -73,6 +74,8 @@ class Stars { // variables starfield::Controller* _controller; + + bool _fileLoaded; }; From 6694ef3869f9ef9cc664d76170dbf3f1be912ce0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 16:33:27 -0700 Subject: [PATCH 39/55] fixed some compiler warnings --- interface/src/Util.cpp | 3 ++- libraries/voxels/src/CoverageMap.cpp | 2 +- libraries/voxels/src/Tags.cpp | 4 ++-- libraries/voxels/src/ViewFrustum.cpp | 2 +- voxel-server/src/main.cpp | 2 -- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index d9813a231f..8d22570275 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -501,12 +501,13 @@ void runTimingTests() { printLog("powf(f, 0.5) usecs: %f\n", 1000.0f * elapsedMsecs / (float) numTests); // Vector Math + float distance; glm::vec3 pointA(randVector()), pointB(randVector()); gettimeofday(&startTime, NULL); for (int i = 1; i < numTests; i++) { //glm::vec3 temp = pointA - pointB; //float distanceSquared = glm::dot(temp, temp); - float distance = glm::distance(pointA, pointB); + distance = glm::distance(pointA, pointB); } gettimeofday(&endTime, NULL); elapsedMsecs = diffclock(&startTime, &endTime); diff --git a/libraries/voxels/src/CoverageMap.cpp b/libraries/voxels/src/CoverageMap.cpp index 230df4db69..c1c53ca750 100644 --- a/libraries/voxels/src/CoverageMap.cpp +++ b/libraries/voxels/src/CoverageMap.cpp @@ -336,7 +336,7 @@ void CoverageRegion::erase() { // _polygons[i]->getBoundingBox().printDebugDetails(); //} } -/**/ +**/ // If we're in charge of managing the polygons, then clean them up first if (_managePolygons) { for (int i = 0; i < _polygonCount; i++) { diff --git a/libraries/voxels/src/Tags.cpp b/libraries/voxels/src/Tags.cpp index 0cbfa1a37c..8f7dab5390 100644 --- a/libraries/voxels/src/Tags.cpp +++ b/libraries/voxels/src/Tags.cpp @@ -115,8 +115,8 @@ TagCompound::TagCompound(std::stringstream &ss) : _width(0), _length(0), _height(0), - _blocksId(NULL), - _blocksData(NULL) + _blocksData(NULL), + _blocksId(NULL) { int tagId; diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index 0245bdc0c1..166a2f2126 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -583,7 +583,7 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { lookUp += PROJECTION_CLIPPED; } - /***/ + ***/ } // set the distance from our camera position, to the closest vertex float distance = glm::distance(getPosition(), box.getCenter()); diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index a7d662fdeb..a5234bcd94 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -995,8 +995,6 @@ void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMa } //tree->recurseTreeWithOperationDistanceSorted(scanTreeWithOcclusionOperation, position, (void*)&args); - long long now = usecTimestampNow(); - printf("scanTreeWithOcclusion()\n"); printf(" position=(%f,%f)\n", position.x, position.y); From 62c933140dd97b9e0ee9984226f7556d29523d5b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 16:57:42 -0700 Subject: [PATCH 40/55] implement max number of polygons in the coverage regions to keep them from getting slow --- libraries/voxels/src/CoverageMap.cpp | 31 +++++++++++++++++++++------- libraries/voxels/src/CoverageMap.h | 1 + 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/libraries/voxels/src/CoverageMap.cpp b/libraries/voxels/src/CoverageMap.cpp index c1c53ca750..d30e0a4290 100644 --- a/libraries/voxels/src/CoverageMap.cpp +++ b/libraries/voxels/src/CoverageMap.cpp @@ -16,6 +16,8 @@ int CoverageMap::_checkMapRootCalls = 0; int CoverageMap::_notAllInView = 0; bool CoverageMap::wantDebugging = false; +const int MAX_POLYGONS_PER_REGION = 50; + const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-1.f,-1.f), glm::vec2(2.f,2.f)); // Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space. @@ -76,6 +78,7 @@ void CoverageMap::printStats() { printLog("_occlusionTests=%d\n",CoverageRegion::_occlusionTests); printLog("_regionSkips=%d\n",CoverageRegion::_regionSkips); printLog("_tooSmallSkips=%d\n",CoverageRegion::_tooSmallSkips); + printLog("_regionFullSkips=%d\n",CoverageRegion::_regionFullSkips); printLog("_outOfOrderPolygon=%d\n",CoverageRegion::_outOfOrderPolygon); printLog("_clippedPolygons=%d\n",CoverageRegion::_clippedPolygons); } @@ -104,6 +107,7 @@ void CoverageMap::erase() { CoverageRegion::_occlusionTests = 0; CoverageRegion::_regionSkips = 0; CoverageRegion::_tooSmallSkips = 0; + CoverageRegion::_regionFullSkips = 0; CoverageRegion::_outOfOrderPolygon = 0; CoverageRegion::_clippedPolygons = 0; _mapCount = 0; @@ -283,10 +287,16 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b // any of our child bounding boxes, so we should add it here. if (storeIt) { if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) { - //printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area()); - storeIn->storeInArray(polygon); - //printLog("CoverageMap2::checkMap()... STORED\n"); - return STORED; + //printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area()); + if (storeIn->getPolygonCount() < MAX_POLYGONS_PER_REGION) { + storeIn->storeInArray(polygon); + //printLog("CoverageMap2::checkMap()... STORED\n"); + return STORED; + } else { + CoverageRegion::_regionFullSkips++; + //printLog("CoverageMap2::checkMap()... NOT_STORED\n"); + return NOT_STORED; + } } else { CoverageRegion::_tooSmallSkips++; //printLog("CoverageMap2::checkMap()... NOT_STORED\n"); @@ -405,6 +415,7 @@ int CoverageRegion::_totalPolygons = 0; int CoverageRegion::_occlusionTests = 0; int CoverageRegion::_regionSkips = 0; int CoverageRegion::_tooSmallSkips = 0; +int CoverageRegion::_regionFullSkips = 0; int CoverageRegion::_outOfOrderPolygon = 0; int CoverageRegion::_clippedPolygons = 0; @@ -523,10 +534,14 @@ CoverageMapStorageResult CoverageRegion::checkRegion(VoxelProjectedPolygon* poly if (polygonAtThisLevel->getDistance() >= polygon->getDistance()) { _outOfOrderPolygon++; if (storeIt) { - if (true || polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) { - //printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area()); - storeInArray(polygon); - return STORED; + if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) { + if (getPolygonCount() < MAX_POLYGONS_PER_REGION) { + storeInArray(polygon); + return STORED; + } else { + CoverageRegion::_regionFullSkips++; + return NOT_STORED; + } } else { _tooSmallSkips++; return NOT_STORED; diff --git a/libraries/voxels/src/CoverageMap.h b/libraries/voxels/src/CoverageMap.h index c2e1a4193b..46daf646c9 100644 --- a/libraries/voxels/src/CoverageMap.h +++ b/libraries/voxels/src/CoverageMap.h @@ -33,6 +33,7 @@ public: static int _occlusionTests; static int _regionSkips; static int _tooSmallSkips; + static int _regionFullSkips; static int _outOfOrderPolygon; static int _clippedPolygons; From 1831cc0de3dc1d7467d6b4709382b5eff49e1fc4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 17:06:22 -0700 Subject: [PATCH 41/55] removed level param from recursion --- interface/src/Application.cpp | 2 +- interface/src/Application.h | 2 +- interface/src/VoxelSystem.cpp | 28 +++++++++++++------------- interface/src/VoxelSystem.h | 24 +++++++++++----------- libraries/voxels/src/VoxelTree.cpp | 32 ++++++++++++------------------ libraries/voxels/src/VoxelTree.h | 8 ++++---- voxel-server/src/main.cpp | 5 ++++- 7 files changed, 49 insertions(+), 52 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index be93d25af9..8950b04339 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1298,7 +1298,7 @@ struct SendVoxelsOperationArgs { }; -bool Application::sendVoxelsOperation(VoxelNode* node, int level, void* extraData) { +bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) { SendVoxelsOperationArgs* args = (SendVoxelsOperationArgs*)extraData; if (node->isColored()) { unsigned char* nodeOctalCode = node->getOctalCode(); diff --git a/interface/src/Application.h b/interface/src/Application.h index b9ca8919b5..8ab6994981 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -170,7 +170,7 @@ private: const char* nodeTypes, int numNodeTypes); static void sendVoxelServerAddScene(); - static bool sendVoxelsOperation(VoxelNode* node, int level, void* extraData); + static bool sendVoxelsOperation(VoxelNode* node, void* extraData); static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail); static void sendAvatarVoxelURLMessage(const QUrl& url); static void processAvatarVoxelURLMessage(unsigned char *packetData, size_t dataBytes); diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 0bc9b104be..791729a442 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -701,7 +701,7 @@ void VoxelSystem::killLocalVoxels() { } -bool VoxelSystem::randomColorOperation(VoxelNode* node, int level, void* extraData) { +bool VoxelSystem::randomColorOperation(VoxelNode* node, void* extraData) { _nodeCount++; if (node->isColored()) { nodeColor newColor = { 255, randomColorValue(150), randomColorValue(150), 1 }; @@ -717,7 +717,7 @@ void VoxelSystem::randomizeVoxelColors() { setupNewVoxelsForDrawing(); } -bool VoxelSystem::falseColorizeRandomOperation(VoxelNode* node, int level, void* extraData) { +bool VoxelSystem::falseColorizeRandomOperation(VoxelNode* node, void* extraData) { _nodeCount++; // always false colorize node->setFalseColor(255, randomColorValue(150), randomColorValue(150)); @@ -731,7 +731,7 @@ void VoxelSystem::falseColorizeRandom() { setupNewVoxelsForDrawing(); } -bool VoxelSystem::trueColorizeOperation(VoxelNode* node, int level, void* extraData) { +bool VoxelSystem::trueColorizeOperation(VoxelNode* node, void* extraData) { _nodeCount++; node->setFalseColored(false); return true; @@ -746,7 +746,7 @@ void VoxelSystem::trueColorize() { } // Will false colorize voxels that are not in view -bool VoxelSystem::falseColorizeInViewOperation(VoxelNode* node, int level, void* extraData) { +bool VoxelSystem::falseColorizeInViewOperation(VoxelNode* node, void* extraData) { const ViewFrustum* viewFrustum = (const ViewFrustum*) extraData; _nodeCount++; if (node->isColored()) { @@ -766,7 +766,7 @@ void VoxelSystem::falseColorizeInView(ViewFrustum* viewFrustum) { } // Will false colorize voxels based on distance from view -bool VoxelSystem::falseColorizeDistanceFromViewOperation(VoxelNode* node, int level, void* extraData) { +bool VoxelSystem::falseColorizeDistanceFromViewOperation(VoxelNode* node, void* extraData) { ViewFrustum* viewFrustum = (ViewFrustum*) extraData; if (node->isColored()) { float distance = node->distanceToCamera(*viewFrustum); @@ -789,7 +789,7 @@ float VoxelSystem::_minDistance = FLT_MAX; // Helper function will get the distance from view range, would be nice if you could just keep track // of this as voxels are created and/or colored... seems like some transform math could do that so // we wouldn't need to do two passes of the tree -bool VoxelSystem::getDistanceFromViewRangeOperation(VoxelNode* node, int level, void* extraData) { +bool VoxelSystem::getDistanceFromViewRangeOperation(VoxelNode* node, void* extraData) { ViewFrustum* viewFrustum = (ViewFrustum*) extraData; // only do this for truly colored voxels... if (node->isColored()) { @@ -842,7 +842,7 @@ public: // "Remove" voxels from the tree that are not in view. We don't actually delete them, // we remove them from the tree and place them into a holding area for later deletion -bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, int level, void* extraData) { +bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) { removeOutOfViewArgs* args = (removeOutOfViewArgs*)extraData; // If our node was previously added to the don't recurse bag, then return false to @@ -977,7 +977,7 @@ public: bool colorThis; }; -bool VoxelSystem::falseColorizeRandomEveryOtherOperation(VoxelNode* node, int level, void* extraData) { +bool VoxelSystem::falseColorizeRandomEveryOtherOperation(VoxelNode* node, void* extraData) { falseColorizeRandomEveryOtherArgs* args = (falseColorizeRandomEveryOtherArgs*)extraData; args->totalNodes++; if (node->isColored()) { @@ -1030,7 +1030,7 @@ public: bool hasIndexFound[MAX_VOXELS_PER_SYSTEM]; }; -bool VoxelSystem::collectStatsForTreesAndVBOsOperation(VoxelNode* node, int level, void* extraData) { +bool VoxelSystem::collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData) { collectStatsForTreesAndVBOsArgs* args = (collectStatsForTreesAndVBOsArgs*)extraData; args->totalNodes++; @@ -1182,7 +1182,7 @@ struct FalseColorizeSubTreeOperationArgs { long voxelsTouched; }; -bool VoxelSystem::falseColorizeSubTreeOperation(VoxelNode* node, int level, void* extraData) { +bool VoxelSystem::falseColorizeSubTreeOperation(VoxelNode* node, void* extraData) { if (node->getShouldRender()) { FalseColorizeSubTreeOperationArgs* args = (FalseColorizeSubTreeOperationArgs*) extraData; node->setFalseColor(args->color[0], args->color[1], args->color[2]); @@ -1191,7 +1191,7 @@ bool VoxelSystem::falseColorizeSubTreeOperation(VoxelNode* node, int level, void return true; } -bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, int level, void* extraData) { +bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraData) { FalseColorizeOccludedArgs* args = (FalseColorizeOccludedArgs*) extraData; args->totalVoxels++; @@ -1228,7 +1228,7 @@ bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, int level, voi subArgs.color[2] = 0; subArgs.voxelsTouched = 0; - args->tree->recurseNodeWithOperation(node, level, falseColorizeSubTreeOperation, &subArgs ); + args->tree->recurseNodeWithOperation(node, falseColorizeSubTreeOperation, &subArgs ); args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1); args->totalVoxels += (subArgs.voxelsTouched - 1); @@ -1312,7 +1312,7 @@ void VoxelSystem::falseColorizeOccluded() { setupNewVoxelsForDrawing(); } -bool VoxelSystem::falseColorizeOccludedV2Operation(VoxelNode* node, int level, void* extraData) { +bool VoxelSystem::falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData) { FalseColorizeOccludedArgs* args = (FalseColorizeOccludedArgs*) extraData; args->totalVoxels++; @@ -1349,7 +1349,7 @@ bool VoxelSystem::falseColorizeOccludedV2Operation(VoxelNode* node, int level, v subArgs.color[2] = 0; subArgs.voxelsTouched = 0; - args->tree->recurseNodeWithOperation(node, level, falseColorizeSubTreeOperation, &subArgs ); + args->tree->recurseNodeWithOperation(node, falseColorizeSubTreeOperation, &subArgs ); args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1); args->totalVoxels += (subArgs.voxelsTouched - 1); diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 0b7b271411..470ec79aa2 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -120,18 +120,18 @@ private: bool _renderWarningsOn; // Operation functions for tree recursion methods static int _nodeCount; - static bool randomColorOperation(VoxelNode* node, int level, void* extraData); - static bool falseColorizeRandomOperation(VoxelNode* node, int level, void* extraData); - static bool trueColorizeOperation(VoxelNode* node, int level, void* extraData); - static bool falseColorizeInViewOperation(VoxelNode* node, int level, void* extraData); - static bool falseColorizeDistanceFromViewOperation(VoxelNode* node, int level, void* extraData); - static bool getDistanceFromViewRangeOperation(VoxelNode* node, int level, void* extraData); - static bool removeOutOfViewOperation(VoxelNode* node, int level, void* extraData); - static bool falseColorizeRandomEveryOtherOperation(VoxelNode* node, int level, void* extraData); - static bool collectStatsForTreesAndVBOsOperation(VoxelNode* node, int level, void* extraData); - static bool falseColorizeOccludedOperation(VoxelNode* node, int level, void* extraData); - static bool falseColorizeSubTreeOperation(VoxelNode* node, int level, void* extraData); - static bool falseColorizeOccludedV2Operation(VoxelNode* node, int level, void* extraData); + static bool randomColorOperation(VoxelNode* node, void* extraData); + static bool falseColorizeRandomOperation(VoxelNode* node, void* extraData); + static bool trueColorizeOperation(VoxelNode* node, void* extraData); + static bool falseColorizeInViewOperation(VoxelNode* node, void* extraData); + static bool falseColorizeDistanceFromViewOperation(VoxelNode* node, void* extraData); + static bool getDistanceFromViewRangeOperation(VoxelNode* node, void* extraData); + static bool removeOutOfViewOperation(VoxelNode* node, void* extraData); + static bool falseColorizeRandomEveryOtherOperation(VoxelNode* node, void* extraData); + static bool collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData); + static bool falseColorizeOccludedOperation(VoxelNode* node, void* extraData); + static bool falseColorizeSubTreeOperation(VoxelNode* node, void* extraData); + static bool falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData); int updateNodeInArraysAsFullVBO(VoxelNode* node); diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index dbd13e236a..2fa086c76f 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -64,7 +64,6 @@ void VoxelTree::recurseTreeWithOperationDistanceSortedTimed(PointerStack* stackO RecurseVoxelTreeOperation operation, const glm::vec3& point, void* extraData) { - int ignored = 0; long long start = usecTimestampNow(); // start case, stack empty, so start with root... @@ -75,7 +74,7 @@ void VoxelTree::recurseTreeWithOperationDistanceSortedTimed(PointerStack* stackO VoxelNode* node = (VoxelNode*)stackOfNodes->top(); stackOfNodes->pop(); - if (operation(node, ignored, extraData)) { + if (operation(node, extraData)) { //sortChildren... CLOSEST to FURTHEST // determine the distance sorted order of our children @@ -120,18 +119,16 @@ void VoxelTree::recurseTreeWithOperationDistanceSortedTimed(PointerStack* stackO // stops recursion if operation function returns false. void VoxelTree::recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData) { int level = 0; - recurseNodeWithOperation(rootNode, level, operation, extraData); + recurseNodeWithOperation(rootNode, operation, extraData); } // Recurses voxel node with an operation function -void VoxelTree::recurseNodeWithOperation(VoxelNode* node, int& level, RecurseVoxelTreeOperation operation, void* extraData) { - if (operation(node, level, extraData)) { +void VoxelTree::recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData) { + if (operation(node, extraData)) { for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { VoxelNode* child = node->getChildAtIndex(i); if (child) { - level++; - recurseNodeWithOperation(child, level, operation, extraData); - level--; + recurseNodeWithOperation(child, operation, extraData); } } } @@ -142,14 +139,13 @@ void VoxelTree::recurseNodeWithOperation(VoxelNode* node, int& level, RecurseVox void VoxelTree::recurseTreeWithOperationDistanceSorted(RecurseVoxelTreeOperation operation, const glm::vec3& point, void* extraData) { - int level = 0; - recurseNodeWithOperationDistanceSorted(rootNode, level, operation, point, extraData); + recurseNodeWithOperationDistanceSorted(rootNode, operation, point, extraData); } // Recurses voxel node with an operation function -void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, int& level, RecurseVoxelTreeOperation operation, +void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation, const glm::vec3& point, void* extraData) { - if (operation(node, level, extraData)) { + if (operation(node, extraData)) { // determine the distance sorted order of our children VoxelNode* sortedChildren[NUMBER_OF_CHILDREN]; float distancesToChildren[NUMBER_OF_CHILDREN]; @@ -174,9 +170,7 @@ void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, int& lev if (childNode) { //printLog("recurseNodeWithOperationDistanceSorted() PROCESSING child[%d] distance=%f...\n", i, distancesToChildren[i]); //childNode->printDebugDetails(""); - level++; - recurseNodeWithOperationDistanceSorted(childNode, level, operation, point, extraData); - level--; + recurseNodeWithOperationDistanceSorted(childNode, operation, point, extraData); } } } @@ -921,7 +915,7 @@ public: bool found; }; -bool findRayIntersectionOp(VoxelNode* node, int level, void* extraData) { +bool findRayIntersectionOp(VoxelNode* node, void* extraData) { RayArgs* args = static_cast(extraData); AABox box = node->getAABox(); float distance; @@ -957,7 +951,7 @@ public: bool found; }; -bool findSpherePenetrationOp(VoxelNode* node, int level, void* extraData) { +bool findSpherePenetrationOp(VoxelNode* node, void* extraData) { SphereArgs* args = static_cast(extraData); // coarse check against bounds @@ -994,7 +988,7 @@ public: bool found; }; -bool findCapsulePenetrationOp(VoxelNode* node, int level, void* extraData) { +bool findCapsulePenetrationOp(VoxelNode* node, void* extraData) { CapsuleArgs* args = static_cast(extraData); // coarse check against bounds @@ -1672,7 +1666,7 @@ unsigned long VoxelTree::getVoxelCount() { return nodeCount; } -bool VoxelTree::countVoxelsOperation(VoxelNode* node, int level, void* extraData) { +bool VoxelTree::countVoxelsOperation(VoxelNode* node, void* extraData) { (*(unsigned long*)extraData)++; return true; // keep going } diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index a52e8cf491..4c0e7d33c4 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -17,7 +17,7 @@ #include "PointerStack.h" // Callback function, for recuseTreeWithOperation -typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, int level, void* extraData); +typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData); typedef enum {GRADIENT, RANDOM, NATURAL} creationMode; #define NO_EXISTS_BITS false @@ -147,8 +147,8 @@ public: bool getShouldReaverage() const { return _shouldReaverage; } - void recurseNodeWithOperation(VoxelNode* node, int& level, RecurseVoxelTreeOperation operation, void* extraData); - void recurseNodeWithOperationDistanceSorted(VoxelNode* node, int& level, RecurseVoxelTreeOperation operation, + void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData); + void recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation, const glm::vec3& point, void* extraData); @@ -167,7 +167,7 @@ private: VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag, bool deltaViewFrustum, const ViewFrustum* lastViewFrustum); - static bool countVoxelsOperation(VoxelNode* node, int level, void* extraData); + static bool countVoxelsOperation(VoxelNode* node, void* extraData); VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const; VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate); diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index a5234bcd94..05d834c7df 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -415,6 +415,9 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, if (nodeData->nodeBag.isEmpty()) { nodeData->updateLastKnownViewFrustum(); nodeData->setViewSent(true); + if (::debugVoxelSending) { + nodeData->map.printStats(); + } nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes } @@ -808,7 +811,7 @@ struct CountSubTreeOperationArgs { }; -bool scanTreeWithOcclusionOperation(VoxelNode* node, int level, void* extraData) { +bool scanTreeWithOcclusionOperation(VoxelNode* node, void* extraData) { ScanTreeArgs* args = (ScanTreeArgs*) extraData; From a48f353f2f8a7571a3c87d11ef953e9a43663204 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 17:07:55 -0700 Subject: [PATCH 42/55] whitespace cleaned up --- interface/src/Application.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8950b04339..8706443a65 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2610,10 +2610,6 @@ void Application::displayStats() { } delete []perfStatLinesArray; // we're responsible for cleanup } - - - - } void Application::renderThrustAtVoxel(const glm::vec3& thrust) { @@ -2626,8 +2622,8 @@ void Application::renderThrustAtVoxel(const glm::vec3& thrust) { glVertex3f(voxelTouched.x + thrust.x, voxelTouched.y + thrust.y, voxelTouched.z + thrust.z); glEnd(); } - } + void Application::renderLineToTouchedVoxel() { // Draw a teal line to the voxel I am currently dragging on if (_mousePressed) { From 2bb2fc736c89964fa5277dacc9744708bc5f3ac5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 17:14:34 -0700 Subject: [PATCH 43/55] remove dead code --- .../voxels/src/VoxelProjectedPolygon.cpp | 52 ------------------- 1 file changed, 52 deletions(-) diff --git a/libraries/voxels/src/VoxelProjectedPolygon.cpp b/libraries/voxels/src/VoxelProjectedPolygon.cpp index 61cbe6574e..95c66b6821 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.cpp +++ b/libraries/voxels/src/VoxelProjectedPolygon.cpp @@ -259,57 +259,6 @@ bool VoxelProjectedPolygon::pointInside(const glm::vec2& point, bool* matchesVer return true; } -/* - -// This old version of pointInside works on concave or convex polygons. But it's slower, and since we don't need -// to support concave polygons, we don't need to do this extra heavy lifting -bool VoxelProjectedPolygon::pointInside(const glm::vec2& point, bool* matchesVertex) const { - // first check the bounding boxes, the point must be fully within the boounding box of this shadow - if ((point.x > getMaxX()) || - (point.y > getMaxY()) || - (point.x < getMinX()) || - (point.y < getMinY())) { - return false; - } - - //float e = (getMaxX() - getMinX()) / 100.0f; // some epsilon - float e = 1.0f; // some epsilon - - // We need to have one ray that goes from a known outside position to the point in question. We'll pick a - // start point outside of our min X, min Y - glm::vec2 r1p1(getMinX() - e, point.y); - glm::vec2 r1p2(point); - - glm::vec2 r2p1(getVertex(getVertexCount()-1)); // start with last vertex to first vertex - glm::vec2 r2p2; - - // Test the ray against all sides - int intersections = 0; - bool someVertexMatches = false; - for (int i = 0; i < getVertexCount(); i++) { - r2p2 = getVertex(i); - - // if the point in question matches one of our vetices, then we consider it to NOT be inside. - if (point.x == r2p2.x && point.y == r2p2.y) { - // caller wants to know if we match any of the vertices - if (matchesVertex) { - *matchesVertex = true; - } - return false; - } - - // if we're still processing, then check for intersections - if (doLineSegmentsIntersect(r1p1, r1p2, r2p1, r2p2)) { - intersections++; - } - r2p1 = r2p2; // set up for next side - } - - // If odd number of intersections and we got here, we're inside - return ((intersections & 1) == 1); -} -*/ - void VoxelProjectedPolygon::printDebugDetails() const { printf("VoxelProjectedPolygon..."); printf(" minX=%f maxX=%f minY=%f maxY=%f\n", getMinX(), getMaxX(), getMinY(), getMaxY()); @@ -325,7 +274,6 @@ bool VoxelProjectedPolygon::intersects(const BoundingBox& box) const { return intersects(testee); } - bool VoxelProjectedPolygon::intersects(const VoxelProjectedPolygon& testee) const { VoxelProjectedPolygon::intersects_calls++; return intersectsOnAxes(testee) && testee.intersectsOnAxes(*this); From 78611e30e7bcd650d7110849c93564f69eac280b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 17:21:40 -0700 Subject: [PATCH 44/55] make occlusion culling default --- interface/src/Application.cpp | 6 +++--- interface/src/Application.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8706443a65..8348c693c8 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1232,8 +1232,8 @@ void Application::setWantsDelta(bool wantsDelta) { _myAvatar.setWantDelta(wantsDelta); } -void Application::setWantsOcclusionCulling(bool wantsOcclusionCulling) { - _myAvatar.setWantOcclusionCulling(wantsOcclusionCulling); +void Application::disableOcclusionCulling(bool disableOcclusionCulling) { + _myAvatar.setWantOcclusionCulling(!disableOcclusionCulling); } void Application::updateVoxelModeActions() { @@ -1626,7 +1626,7 @@ void Application::initMenu() { debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true); debugMenu->addAction("Wants View Delta Sending", this, SLOT(setWantsDelta(bool)))->setCheckable(true); (_shouldLowPassFilter = debugMenu->addAction("Test: LowPass filter"))->setCheckable(true); - debugMenu->addAction("Wants Occlusion Culling", this, SLOT(setWantsOcclusionCulling(bool)), + debugMenu->addAction("Disable Occlusion Culling", this, SLOT(disableOcclusionCulling(bool)), Qt::SHIFT | Qt::Key_C)->setCheckable(true); (_renderCoverageMap = debugMenu->addAction("Render Coverage Map"))->setCheckable(true); diff --git a/interface/src/Application.h b/interface/src/Application.h index 8ab6994981..530ec2e76f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -138,7 +138,7 @@ private slots: void setWantsMonochrome(bool wantsMonochrome); void setWantsResIn(bool wantsResIn); void setWantsDelta(bool wantsDelta); - void setWantsOcclusionCulling(bool wantsOcclusionCulling); + void disableOcclusionCulling(bool disableOcclusionCulling); void updateVoxelModeActions(); void decreaseVoxelSize(); void increaseVoxelSize(); From 17e70271ffc1207f68a9519f6cf37a8fc1416129 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 17:21:51 -0700 Subject: [PATCH 45/55] make occlusion culling default --- libraries/avatars/src/AvatarData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 1bef9026e7..a21e502b09 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -35,7 +35,7 @@ AvatarData::AvatarData(Node* owningNode) : _wantResIn(false), _wantColor(true), _wantDelta(false), - _wantOcclusionCulling(false), + _wantOcclusionCulling(true), _headData(NULL), _handData(NULL) { From 371bf7d181c7a1faa207b51d106d3c4dcafab600 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 17:22:30 -0700 Subject: [PATCH 46/55] removed dead code, fixed compiler warning --- libraries/voxels/src/VoxelTree.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 2fa086c76f..9aac2b7455 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -118,7 +118,6 @@ void VoxelTree::recurseTreeWithOperationDistanceSortedTimed(PointerStack* stackO // Recurses voxel tree calling the RecurseVoxelTreeOperation function for each node. // stops recursion if operation function returns false. void VoxelTree::recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData) { - int level = 0; recurseNodeWithOperation(rootNode, operation, extraData); } From f720513affc145ebce1835f98085e6d15a3389b9 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 17:30:40 -0700 Subject: [PATCH 47/55] removed scanTreeWithOcclusion() experimental code --- voxel-server/src/main.cpp | 275 +------------------------------------- 1 file changed, 1 insertion(+), 274 deletions(-) diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 05d834c7df..87ee020bde 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -65,9 +65,6 @@ bool wantSearchForColoredNodes = false; EnvironmentData environmentData[3]; -void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMap* coverageMap, bool wantsOcclusionCulling); - - void randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) { // randomly generate children for this node // the first level of the tree (where levelsToGo = MAX_VOXEL_TREE_DEPTH_LEVELS) has all 8 @@ -466,16 +463,7 @@ void *distributeVoxelsToListeners(void *args) { } if (nodeData->getWantResIn()) { - //resInVoxelDistributor(nodeList, node, nodeData); - - // hack, just test scanning the tree here! - scanTreeWithOcclusion(&serverTree, &nodeData->getCurrentViewFrustum(), &nodeData->map, - nodeData->getWantOcclusionCulling()); - printf("AFTER scanTreeWithOcclusion() map->printStats()\n"); - CoverageMap::wantDebugging = true; - nodeData->map.printStats(); - - + resInVoxelDistributor(nodeList, node, nodeData); } else { deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged); } @@ -767,264 +755,3 @@ int main(int argc, const char * argv[]) { } -const long MAX_SCAN = 100 * 1000; - -struct ScanTreeArgs { - ViewFrustum* viewFrustum; - glm::vec3 cameraPosition; - CoverageMap* map; -// CoverageMapV2* mapV2; - VoxelTree* tree; - long totalVoxels; - long coloredVoxels; - long occludedVoxels; - long notOccludedVoxels; - - long outOfView; // not in the view frustum - long leavesOutOfView; // not in the view frustum - - long tooFar; // out of LOD - long leavesTooFar; // out of LOD - - long notAllInView; // in the view frustum, but the projection can't be calculated, assume it needs to be sent - long subtreeVoxelsSkipped; - - long nonLeaves; - long nonLeavesNotAllInView; - long nonLeavesOutOfView; // not in the view frustum - long nonLeavesOccluded; - long nonLeavesTooFar; - - long stagedForDeletion; - long doesntFit; - - bool wantsOcclusionCulling; - - ViewFrustum::location subTreeLocation; - long subTreeInside; - - int level; -}; - -struct CountSubTreeOperationArgs { - long voxelsTouched; -}; - - -bool scanTreeWithOcclusionOperation(VoxelNode* node, void* extraData) { - - ScanTreeArgs* args = (ScanTreeArgs*) extraData; - - // fastest, scan entire tree... - //args->totalVoxels++; - //return true; - - - args->totalVoxels++; - - // SOME FACTS WE KNOW... - // If a node is INSIDE the view frustum, then ALL it's child nodes are also INSIDE, and so we - // shouldn't have to check that any more... use InFrustum() and store something in args to know - // that that part of the stack is in frustum. (do we also need to store a pointer to the parent node - // that is INSIDE so that when we unwind back to that point we start checking again?... probably) - if (args->subTreeLocation == ViewFrustum::INSIDE) { - args->subTreeInside++; - // continue without checking further - } else { - // if this node is not in the view frustum, then just return, since we know none of it's children will be - // in the view. - ViewFrustum::location location = node->inFrustum(*args->viewFrustum); - if (location == ViewFrustum::OUTSIDE) { - args->outOfView++; - if (node->isLeaf()) { - args->leavesOutOfView++; - } else { - args->nonLeavesOutOfView++; - } - return false; - } - // remember this for next recursion pass - // NOTE: This doesn't actually work because we need a stack of these, and our code doesn't execute - // to pop the stack. Similar problem with level. So we need a solution to allow operation functions to - // store information on a stack. - //args->subTreeLocation = location; - } - - // If this node is too far for LOD... - float distanceSquared = node->distanceSquareToPoint(args->cameraPosition); - float boundaryDistanceSquared = boundaryDistanceSquaredForRenderLevel(node->getLevel()); //level+1); - if (distanceSquared >= boundaryDistanceSquared) { - args->tooFar++; - if (node->isLeaf()) { - args->leavesTooFar++; - } else { - args->nonLeavesTooFar++; - } - return false; - } - - // Handle occlusion culling tests. - if (args->wantsOcclusionCulling) { - if (!node->isLeaf()) { - args->nonLeaves++; - - AABox voxelBox = node->getAABox(); - voxelBox.scale(TREE_SCALE); - VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(args->viewFrustum->getProjectedPolygon(voxelBox)); - - // If we're not all in view, then ignore it, and just return. But keep searching... - if (!voxelPolygon->getAllInView()) { - args->nonLeavesNotAllInView++; - delete voxelPolygon; - - // we can't determine occlusion, so assume it's not - return true; - } - - CoverageMapStorageResult result = args->map->checkMap(voxelPolygon, false); - if (result == OCCLUDED) { - args->nonLeavesOccluded++; - delete voxelPolygon; - return false; - } - - delete voxelPolygon; - return true; // keep looking... - } - - // we don't want to check shouldRender in the server... that's not used - if (node->isLeaf() && node->isColored() /*&& node->getShouldRender()*/) { - args->coloredVoxels++; - - AABox voxelBox = node->getAABox(); - voxelBox.scale(TREE_SCALE); - VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(args->viewFrustum->getProjectedPolygon(voxelBox)); - - // If we're not all in view, then ignore it, and just return. But keep searching... - if (!voxelPolygon->getAllInView()) { - args->notAllInView++; - delete voxelPolygon; - - // But, this is one we do want to send... - - return true; - } - - CoverageMapStorageResult result = args->map->checkMap(voxelPolygon, true); - if (result == OCCLUDED) { - args->occludedVoxels++; - // This is one we don't want to send... - - } else if (result == STORED) { - args->notOccludedVoxels++; - // This is one we do want to send... - } else if (result == DOESNT_FIT) { - args->doesntFit++; - // Not sure what to do with this one... probably send it? - } - } - } - return true; // keep going! -} - -PointerStack stackOfNodes; -long long scanStart = 0; -ScanTreeArgs args; - -void scanTreeWithOcclusion(VoxelTree* tree, ViewFrustum* viewFrustum, CoverageMap* coverageMap, bool wantsOcclusionCulling) { - PerformanceWarning warn(true, "scanTreeWithOcclusion()",true); - //CoverageMap::wantDebugging = false; - //coverageMap->erase(); - - // if we're starting from scratch, remember our time - if (stackOfNodes.isEmpty()) { - printf("scanTreeWithOcclusion() -----> STARTING \n"); - ::scanStart = usecTimestampNow(); - - args.viewFrustum = viewFrustum; - args.cameraPosition = viewFrustum->getPosition()/(float)TREE_SCALE; - args.map = coverageMap; - args.totalVoxels = 0; - args.coloredVoxels = 0; - args.occludedVoxels = 0; - args.notOccludedVoxels = 0; - args.notAllInView = 0; - args.outOfView = 0; - args.leavesOutOfView = 0; - args.nonLeavesOutOfView = 0; - args.subtreeVoxelsSkipped = 0; - args.nonLeaves = 0; - args.stagedForDeletion = 0; - args.nonLeavesNotAllInView = 0; - args.nonLeavesOccluded = 0; - args.doesntFit = 0; - args.tooFar = 0; - args.leavesTooFar = 0; - args.nonLeavesTooFar = 0; - args.tree = tree; - args.subTreeLocation = ViewFrustum::OUTSIDE; // assume the worst - args.subTreeInside = 0; - args.level = 0; - args.wantsOcclusionCulling = wantsOcclusionCulling; - - VoxelProjectedPolygon::pointInside_calls = 0; - VoxelProjectedPolygon::occludes_calls = 0; - VoxelProjectedPolygon::intersects_calls = 0; - } else { - printf("scanTreeWithOcclusion() -----> still working\n"); - } - - glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE); - - long allowedTime = 80 * 1000; - tree->recurseTreeWithOperationDistanceSortedTimed(&stackOfNodes, allowedTime, - scanTreeWithOcclusionOperation, position, (void*)&args); - - - if (stackOfNodes.isEmpty()) { - printf("scanTreeWithOcclusion() -----> finished??? \n"); - long long endScan = usecTimestampNow(); - int totalScanmsecs = (endScan - scanStart)/1000; - float elapsedSceneSend = totalScanmsecs/1000.0f; - printf("scanTreeWithOcclusion() -----> totalScanmsecs=%d \n", totalScanmsecs); - printf("scanTreeWithOcclusion() --> elapsed time to send scene = %f seconds, args.totalVoxels=%ld ", elapsedSceneSend,args.totalVoxels); - printf(" [occlusionCulling: %s]\n", debug::valueOf(args.wantsOcclusionCulling)); - - coverageMap->erase(); - - - } else { - printf("scanTreeWithOcclusion() -----> still working\n"); - } - - //tree->recurseTreeWithOperationDistanceSorted(scanTreeWithOcclusionOperation, position, (void*)&args); - - printf("scanTreeWithOcclusion()\n"); - printf(" position=(%f,%f)\n", position.x, position.y); - printf(" total=%ld\n", args.totalVoxels); - printf(" colored=%ld\n", args.coloredVoxels); - printf("\n"); - printf(" occluded=%ld\n", args.occludedVoxels); - printf(" notOccluded=%ld\n", args.notOccludedVoxels); - printf(" nonLeavesOccluded=%ld\n", args.nonLeavesOccluded); - printf("\n"); - printf(" outOfView=%ld\n", args.outOfView); - printf(" leavesOutOfView=%ld\n", args.leavesOutOfView); - printf(" nonLeavesOutOfView=%ld\n", args.nonLeavesOutOfView); - printf("\n"); - printf(" tooFar=%ld\n", args.tooFar); - printf(" leavesTooFar=%ld\n", args.leavesTooFar); - printf(" nonLeavesTooFar=%ld\n", args.nonLeavesTooFar); - printf("\n"); - printf(" nonLeaves=%ld\n", args.nonLeaves); - printf(" notAllInView=%ld\n", args.notAllInView); - printf(" nonLeavesNotAllInView=%ld\n", args.nonLeavesNotAllInView); - printf(" subtreeVoxelsSkipped=%ld\n", args.subtreeVoxelsSkipped); - printf(" stagedForDeletion=%ld\n", args.stagedForDeletion); - printf(" pointInside_calls=%ld\n", VoxelProjectedPolygon::pointInside_calls); - printf(" occludes_calls=%ld\n", VoxelProjectedPolygon::occludes_calls); - printf(" intersects_calls=%ld\n", VoxelProjectedPolygon::intersects_calls); - - printf("\n"); - printf(" subTreeInside=%ld\n", args.subTreeInside); -} From d59b9ce955303b78a140c2bb38d46672adab7e42 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 10 Jul 2013 17:31:21 -0700 Subject: [PATCH 48/55] Flip x when in mirror mode, rather than special-casing all the head rotations. Also fixed sideways leaning (was correct in mirror mode, incorrect in world). --- interface/src/Application.cpp | 9 +++++++++ interface/src/Avatar.cpp | 4 ++-- interface/src/Head.cpp | 13 +++++-------- interface/src/Head.h | 3 +-- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ae9466dabe..3b44799e69 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2246,6 +2246,15 @@ void Application::displayOculus(Camera& whichCamera) { void Application::displaySide(Camera& whichCamera) { // transform by eye offset + // flip x if in mirror mode (also requires reversing winding order for backface culling) + if (_lookingInMirror->isChecked()) { + glScalef(-1.0f, 1.0f, 1.0f); + glFrontFace(GL_CW); + + } else { + glFrontFace(GL_CCW); + } + glm::vec3 eyeOffsetPos = whichCamera.getEyeOffsetPosition(); glm::quat eyeOffsetOrient = whichCamera.getEyeOffsetOrientation(); glm::vec3 eyeOffsetAxis = glm::axis(eyeOffsetOrient); diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 00bf65fd7d..426aad57b3 100755 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -324,7 +324,7 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyA // Update torso lean distance based on accelerometer data const float TORSO_LENGTH = 0.5f; const float MAX_LEAN = 45.0f; - _head.setLeanSideways(glm::clamp(glm::degrees(atanf(-estimatedPosition.x * _leanScale / TORSO_LENGTH)), + _head.setLeanSideways(glm::clamp(glm::degrees(atanf(estimatedPosition.x * _leanScale / TORSO_LENGTH)), -MAX_LEAN, MAX_LEAN)); _head.setLeanForward(glm::clamp(glm::degrees(atanf(estimatedPosition.z * _leanScale / TORSO_LENGTH)), -MAX_LEAN, MAX_LEAN)); @@ -1222,7 +1222,7 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { // Always render other people, and render myself when beyond threshold distance if (b == BODY_BALL_HEAD_BASE) { // the head is rendered as a special if (alpha > 0.0f) { - _head.render(lookingInMirror, alpha); + _head.render(alpha); } } else if (alpha > 0.0f) { // Render the body ball sphere diff --git a/interface/src/Head.cpp b/interface/src/Head.cpp index 39f0da3ca9..8b116b48ff 100644 --- a/interface/src/Head.cpp +++ b/interface/src/Head.cpp @@ -67,7 +67,6 @@ Head::Head(Avatar* owningAvatar) : _audioAttack(0.0f), _returnSpringScale(1.0f), _bodyRotation(0.0f, 0.0f, 0.0f), - _lookingInMirror(false), _renderLookatVectors(false), _mohawkTriangleFan(NULL), _mohawkColors(NULL), @@ -283,11 +282,10 @@ void Head::calculateGeometry() { } -void Head::render(bool lookingInMirror, float alpha) { +void Head::render(float alpha) { _renderAlpha = alpha; - _lookingInMirror = lookingInMirror; - + calculateGeometry(); glEnable(GL_DEPTH_TEST); @@ -375,8 +373,8 @@ void Head::renderMohawk() { } else { glPushMatrix(); glTranslatef(_position.x, _position.y, _position.z); - glRotatef((_lookingInMirror ? (_bodyRotation.y - _yaw) : (_bodyRotation.y + _yaw)), 0, 1, 0); - glRotatef(_lookingInMirror ? _roll: -_roll, 0, 0, 1); + glRotatef(_bodyRotation.y + _yaw, 0, 1, 0); + glRotatef(-_roll, 0, 0, 1); glRotatef(-_pitch - _bodyRotation.x, 1, 0, 0); glBegin(GL_TRIANGLE_FAN); @@ -391,8 +389,7 @@ void Head::renderMohawk() { } glm::quat Head::getOrientation() const { - return glm::quat(glm::radians(_bodyRotation)) * glm::quat(glm::radians(_lookingInMirror ? - glm::vec3(_pitch, -_yaw, -_roll) : glm::vec3(_pitch, _yaw, _roll))); + return glm::quat(glm::radians(_bodyRotation)) * glm::quat(glm::radians(glm::vec3(_pitch, _yaw, _roll))); } glm::quat Head::getCameraOrientation () const { diff --git a/interface/src/Head.h b/interface/src/Head.h index e8bcfb5277..2b1b7aabfb 100644 --- a/interface/src/Head.h +++ b/interface/src/Head.h @@ -35,7 +35,7 @@ public: void init(); void reset(); void simulate(float deltaTime, bool isMine); - void render(bool lookingInMirror, float alpha); + void render(float alpha); void renderMohawk(); void setScale (float scale ) { _scale = scale; } @@ -102,7 +102,6 @@ private: float _audioAttack; float _returnSpringScale; //strength of return springs glm::vec3 _bodyRotation; - bool _lookingInMirror; bool _renderLookatVectors; HairTuft _hairTuft[NUM_HAIR_TUFTS]; glm::vec3* _mohawkTriangleFan; From 66f0813981825ab29c8cc238319a910916fd8481 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 17:39:27 -0700 Subject: [PATCH 49/55] removed level form recursion calls --- voxel-edit/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/voxel-edit/src/main.cpp b/voxel-edit/src/main.cpp index a2d9d80292..9571e4adf5 100644 --- a/voxel-edit/src/main.cpp +++ b/voxel-edit/src/main.cpp @@ -13,7 +13,7 @@ VoxelTree myTree; int _nodeCount=0; -bool countVoxelsOperation(VoxelNode* node, int level, void* extraData) { +bool countVoxelsOperation(VoxelNode* node, void* extraData) { if (node->isColored()){ _nodeCount++; } From 50a5b60c55a77c0acf269c26b7edcbbd798b954b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 17:41:30 -0700 Subject: [PATCH 50/55] spacing cleanup --- voxel-server/src/main.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 87ee020bde..d9f8bcc244 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -361,7 +361,8 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, if (nodeData->getAvailable() >= bytesWritten) { nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); } else { - nodeList->getNodeSocket()->send(node->getActiveSocket(),nodeData->getPacket(), nodeData->getPacketLength()); + nodeList->getNodeSocket()->send(node->getActiveSocket(), + nodeData->getPacket(), nodeData->getPacketLength()); trueBytesSent += nodeData->getPacketLength(); truePacketsSent++; packetsSentThisInterval++; @@ -370,7 +371,8 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, } } else { if (nodeData->isPacketWaiting()) { - nodeList->getNodeSocket()->send(node->getActiveSocket(),nodeData->getPacket(), nodeData->getPacketLength()); + nodeList->getNodeSocket()->send(node->getActiveSocket(), + nodeData->getPacket(), nodeData->getPacketLength()); trueBytesSent += nodeData->getPacketLength(); truePacketsSent++; nodeData->resetVoxelPacket(); From dc70675c22fde0796cfb3260a69fcd86454e5c02 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 19:04:28 -0700 Subject: [PATCH 51/55] removed obsolete ResIn mode --- interface/src/Application.cpp | 5 - interface/src/Application.h | 1 - libraries/avatars/src/AvatarData.cpp | 3 - libraries/avatars/src/AvatarData.h | 5 +- libraries/voxels/src/VoxelTree.cpp | 1 - voxel-server/src/main.cpp | 131 +-------------------------- 6 files changed, 2 insertions(+), 144 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 658dd865fe..2e903a6464 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1235,10 +1235,6 @@ void Application::setWantsMonochrome(bool wantsMonochrome) { _myAvatar.setWantColor(!wantsMonochrome); } -void Application::setWantsResIn(bool wantsResIn) { - _myAvatar.setWantResIn(wantsResIn); -} - void Application::setWantsDelta(bool wantsDelta) { _myAvatar.setWantDelta(wantsDelta); } @@ -1629,7 +1625,6 @@ void Application::initMenu() { renderDebugMenu->addAction("FALSE Color Occluded V2 Voxels", this, SLOT(doFalseColorizeOccludedV2()), Qt::CTRL | Qt::Key_P); renderDebugMenu->addAction("Show TRUE Colors", this, SLOT(doTrueVoxelColors()), Qt::CTRL | Qt::Key_T); - debugMenu->addAction("Wants Res-In", this, SLOT(setWantsResIn(bool)))->setCheckable(true); debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true); debugMenu->addAction("Wants View Delta Sending", this, SLOT(setWantsDelta(bool)))->setCheckable(true); (_shouldLowPassFilter = debugMenu->addAction("Test: LowPass filter"))->setCheckable(true); diff --git a/interface/src/Application.h b/interface/src/Application.h index f85b4af159..eede6d8cb2 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -136,7 +136,6 @@ private slots: void doTrueVoxelColors(); void doTreeStats(); void setWantsMonochrome(bool wantsMonochrome); - void setWantsResIn(bool wantsResIn); void setWantsDelta(bool wantsDelta); void disableOcclusionCulling(bool disableOcclusionCulling); void updateVoxelModeActions(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a21e502b09..997d528a5e 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -32,7 +32,6 @@ AvatarData::AvatarData(Node* owningNode) : _cameraNearClip(0.0f), _cameraFarClip(0.0f), _keyState(NO_KEY_DOWN), - _wantResIn(false), _wantColor(true), _wantDelta(false), _wantOcclusionCulling(true), @@ -113,7 +112,6 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) { // bitMask of less than byte wide items unsigned char bitItems = 0; - if (_wantResIn) { setAtBit(bitItems, WANT_RESIN_AT_BIT); } if (_wantColor) { setAtBit(bitItems, WANT_COLOR_AT_BIT); } if (_wantDelta) { setAtBit(bitItems, WANT_DELTA_AT_BIT); } if (_wantOcclusionCulling) { setAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT); } @@ -240,7 +238,6 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { // voxel sending features... unsigned char bitItems = 0; bitItems = (unsigned char)*sourceBuffer++; - _wantResIn = oneAtBit(bitItems, WANT_RESIN_AT_BIT); _wantColor = oneAtBit(bitItems, WANT_COLOR_AT_BIT); _wantDelta = oneAtBit(bitItems, WANT_DELTA_AT_BIT); _wantOcclusionCulling = oneAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index f65cf7bcb5..218283999e 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -20,7 +20,7 @@ #include "HeadData.h" #include "HandData.h" -const int WANT_RESIN_AT_BIT = 0; +const int UNUSED_BIT = 0; // this bit is available to use const int WANT_COLOR_AT_BIT = 1; const int WANT_DELTA_AT_BIT = 2; const int KEY_STATE_START_BIT = 3; // 4th and 5th bits @@ -91,11 +91,9 @@ public: const std::string& chatMessage () const { return _chatMessage; } // related to Voxel Sending strategies - bool getWantResIn() const { return _wantResIn; } bool getWantColor() const { return _wantColor; } bool getWantDelta() const { return _wantDelta; } bool getWantOcclusionCulling() const { return _wantOcclusionCulling; } - void setWantResIn(bool wantResIn) { _wantResIn = wantResIn; } void setWantColor(bool wantColor) { _wantColor = wantColor; } void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; } void setWantOcclusionCulling(bool wantOcclusionCulling) { _wantOcclusionCulling = wantOcclusionCulling; } @@ -130,7 +128,6 @@ protected: std::string _chatMessage; // voxel server sending items - bool _wantResIn; bool _wantColor; bool _wantDelta; bool _wantOcclusionCulling; diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 9aac2b7455..2f0ec4727c 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -98,7 +98,6 @@ void VoxelTree::recurseTreeWithOperationDistanceSortedTimed(PointerStack* stackO for (int i = currentCount-1; i >= 0; i--) { VoxelNode* child = sortedChildren[i]; stackOfNodes->push(child); -//printLog("stackOfNodes->size()=%d child->getLevel()=%d\n",stackOfNodes->size(), child->getLevel()); } } diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index d9f8bcc244..74780ed52a 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -111,130 +111,6 @@ void eraseVoxelTreeAndCleanupNodeVisitData() { } -// Version of voxel distributor that sends each LOD level at a time -void resInVoxelDistributor(NodeList* nodeList, - NodeList::iterator& node, - VoxelNodeData* nodeData) { - ViewFrustum viewFrustum = nodeData->getCurrentViewFrustum(); - bool searchReset = false; - int searchLoops = 0; - int searchLevelWas = nodeData->getMaxSearchLevel(); - long long start = usecTimestampNow(); - while (!searchReset && nodeData->nodeBag.isEmpty()) { - searchLoops++; - - searchLevelWas = nodeData->getMaxSearchLevel(); - int maxLevelReached = serverTree.searchForColoredNodes(nodeData->getMaxSearchLevel(), serverTree.rootNode, - viewFrustum, nodeData->nodeBag); - nodeData->setMaxLevelReached(maxLevelReached); - - // If nothing got added, then we bump our levels. - if (nodeData->nodeBag.isEmpty()) { - if (nodeData->getMaxLevelReached() < nodeData->getMaxSearchLevel()) { - nodeData->resetMaxSearchLevel(); - searchReset = true; - } else { - nodeData->incrementMaxSearchLevel(); - } - } - } - long long 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 in %d loops\n", - elapsedsec, nodeData->nodeBag.count(), searchLevelWas, searchLoops); - } else { - printf("WARNING! searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d in %d loops\n", - elapsedmsec, nodeData->nodeBag.count(), searchLevelWas, searchLoops); - } - } else if (::debugVoxelSending) { - printf("searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d in %d loops\n", - elapsedmsec, nodeData->nodeBag.count(), searchLevelWas, searchLoops); - } - - - // If we have something in our nodeBag, then turn them into packets and send them out... - if (!nodeData->nodeBag.isEmpty()) { - static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static - int bytesWritten = 0; - int packetsSentThisInterval = 0; - int truePacketsSent = 0; - int trueBytesSent = 0; - long long start = usecTimestampNow(); - - bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); - while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { - if (!nodeData->nodeBag.isEmpty()) { - VoxelNode* subTree = nodeData->nodeBag.extract(); - - EncodeBitstreamParams params(nodeData->getMaxSearchLevel(), &viewFrustum, - nodeData->getWantColor(), WANT_EXISTS_BITS); - - bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, - nodeData->nodeBag, params); - - if (nodeData->getAvailable() >= bytesWritten) { - nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); - } else { - nodeList->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); - trueBytesSent += nodeData->getPacketLength(); - truePacketsSent++; - packetsSentThisInterval++; - nodeData->resetVoxelPacket(); - nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); - } - } else { - if (nodeData->isPacketWaiting()) { - nodeList->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); - trueBytesSent += nodeData->getPacketLength(); - truePacketsSent++; - nodeData->resetVoxelPacket(); - - } - packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left - } - } - // send the environment packets - if (shouldSendEnvironments) { - int envPacketLength = 1; - *tempOutputBuffer = PACKET_HEADER_ENVIRONMENT_DATA; - for (int i = 0; i < sizeof(environmentData) / sizeof(environmentData[0]); i++) { - envPacketLength += environmentData[i].getBroadcastData(tempOutputBuffer + envPacketLength); - } - nodeList->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength); - trueBytesSent += envPacketLength; - truePacketsSent++; - } - long long end = usecTimestampNow(); - int elapsedmsec = (end - start)/1000; - if (elapsedmsec > 100) { - if (elapsedmsec > 1000) { - int elapsedsec = (end - start)/1000000; - printf("WARNING! packetLoop() took %d seconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n", - elapsedsec, trueBytesSent, truePacketsSent, searchLevelWas, nodeData->nodeBag.count()); - } else { - printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n", - elapsedmsec, trueBytesSent, truePacketsSent, searchLevelWas, nodeData->nodeBag.count()); - } - } else if (::debugVoxelSending) { - printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n", - elapsedmsec, trueBytesSent, truePacketsSent, searchLevelWas, nodeData->nodeBag.count()); - } - - // if during this last pass, we emptied our bag, then we want to move to the next level. - if (nodeData->nodeBag.isEmpty()) { - if (nodeData->getMaxLevelReached() < nodeData->getMaxSearchLevel()) { - nodeData->resetMaxSearchLevel(); - } else { - nodeData->incrementMaxSearchLevel(); - } - } - } -} pthread_mutex_t treeLock; @@ -463,12 +339,7 @@ void *distributeVoxelsToListeners(void *args) { if (::debugVoxelSending) { printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); } - - if (nodeData->getWantResIn()) { - resInVoxelDistributor(nodeList, node, nodeData); - } else { - deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged); - } + deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged); } } From ab8e2916d32aca4bcc8ed84e60e990e73fff15c1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 19:49:50 -0700 Subject: [PATCH 52/55] improvements to delta sending --- libraries/voxels/src/VoxelTree.cpp | 31 ++++++++++++++++++++++-------- libraries/voxels/src/VoxelTree.h | 3 +++ voxel-server/src/main.cpp | 6 +++++- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 2f0ec4727c..70bca504cb 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1331,10 +1331,6 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp } // wants occlusion culling & isLeaf() - bool childWasInView = (childNode && params.deltaViewFrustum && - - (params.lastViewFrustum && ViewFrustum::INSIDE == childNode->inFrustum(*params.lastViewFrustum))); - // 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 and beyond our LOD. @@ -1360,15 +1356,34 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp } // if any of our grandchildren ARE in view, then we don't want to include our color. If none are, then // we do want to include our color - if (grandChildrenInView > 0 && grandChildrenInLOD==0) { + if (grandChildrenInView > 0 && grandChildrenInLOD == 0) { isLeafOrLOD = true; } } // track children with actual color, only if the child wasn't previously in view! - if (childNode && isLeafOrLOD && childNode->isColored() && !childWasInView && !childIsOccluded) { - childrenColoredBits += (1 << (7 - originalIndex)); - inViewWithColorCount++; + if (childNode && isLeafOrLOD && childNode->isColored() && !childIsOccluded) { + bool childWasInView = false; + + if (childNode && params.deltaViewFrustum && params.lastViewFrustum) { + ViewFrustum::location location = childNode->inFrustum(*params.lastViewFrustum); + + // If we're a leaf, then either intersect or inside is considered "formerly in view" + if (childNode->isLeaf()) { + childWasInView = location != ViewFrustum::OUTSIDE; + } 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) { + childrenColoredBits += (1 << (7 - originalIndex)); + inViewWithColorCount++; + } else { + // otherwise just track stats of the items we discarded + params.childWasInViewDiscarded++; + } } } } diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 4c0e7d33c4..a94afe0d79 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -45,6 +45,8 @@ public: bool deltaViewFrustum; const ViewFrustum* lastViewFrustum; bool wantOcclusionCulling; + long childWasInViewDiscarded; + CoverageMap* map; EncodeBitstreamParams( @@ -66,6 +68,7 @@ public: deltaViewFrustum (deltaViewFrustum), lastViewFrustum (lastViewFrustum), wantOcclusionCulling(wantOcclusionCulling), + childWasInViewDiscarded(0), map (map) {} }; diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 74780ed52a..e71e456359 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -127,7 +127,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, long long start = usecTimestampNow(); // FOR NOW... node tells us if it wants to receive only view frustum deltas - bool wantDelta = nodeData->getWantDelta(); + bool wantDelta = viewFrustumChanged && nodeData->getWantDelta(); const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; if (::debugVoxelSending) { @@ -233,6 +233,10 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeData->nodeBag, params); + + if (::debugVoxelSending && wantDelta) { + printf("encodeTreeBitstream() childWasInViewDiscarded=%ld\n", params.childWasInViewDiscarded); + } if (nodeData->getAvailable() >= bytesWritten) { nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); From cbf18160cb040cab53cbaa4ad330f15d42d6ab63 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 11 Jul 2013 09:22:08 -0700 Subject: [PATCH 53/55] include stdint for uint64_t in SimpleMovingAverage --- libraries/shared/src/SimpleMovingAverage.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/SimpleMovingAverage.h b/libraries/shared/src/SimpleMovingAverage.h index 2d23400f7b..e8a6bc22d0 100644 --- a/libraries/shared/src/SimpleMovingAverage.h +++ b/libraries/shared/src/SimpleMovingAverage.h @@ -11,7 +11,7 @@ #ifndef __hifi__Stats__ #define __hifi__Stats__ -#include +#include class SimpleMovingAverage { public: From ca0f6a4d105a1b66491a3674a4a07ff2bf8e2004 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 11 Jul 2013 09:30:45 -0700 Subject: [PATCH 54/55] header re-arranging and changing --- animation-server/src/main.cpp | 10 ++++++---- libraries/voxels/src/SceneUtils.cpp | 7 +++++-- libraries/voxels/src/SquarePixelMap.cpp | 9 ++++++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/animation-server/src/main.cpp b/animation-server/src/main.cpp index 963d8f73b8..8123fa439c 100644 --- a/animation-server/src/main.cpp +++ b/animation-server/src/main.cpp @@ -10,14 +10,16 @@ #include #include #include -#include +#include + +#include #include #include -#include -#include -#include +#include #include #include +#include +#include #ifdef _WIN32 #include "Syssocket.h" diff --git a/libraries/voxels/src/SceneUtils.cpp b/libraries/voxels/src/SceneUtils.cpp index da92734350..bd126ebf31 100644 --- a/libraries/voxels/src/SceneUtils.cpp +++ b/libraries/voxels/src/SceneUtils.cpp @@ -3,12 +3,15 @@ // hifi // // Created by Brad Hefta-Gaub on 5/7/2013. -// +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // -#include "SceneUtils.h" +#include + #include +#include "SceneUtils.h" + void addCornersAndAxisLines(VoxelTree* tree) { // We want our corner voxels to be about 1/2 meter high, and our TREE_SCALE is in meters, so... float voxelSize = 0.5f / TREE_SCALE; diff --git a/libraries/voxels/src/SquarePixelMap.cpp b/libraries/voxels/src/SquarePixelMap.cpp index bdc97624a7..f37bee4757 100644 --- a/libraries/voxels/src/SquarePixelMap.cpp +++ b/libraries/voxels/src/SquarePixelMap.cpp @@ -3,12 +3,15 @@ // hifi // // Created by Tomáš Horáček on 6/25/13. -// +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // -#include "SquarePixelMap.h" -#include +#include +#include #include +#include + +#include "SquarePixelMap.h" #define CHILD_COORD_X_IS_1 0x1 #define CHILD_COORD_Y_IS_1 0x2 From dc264c98ec4d3cb59f829f244f9d148c499fcc8d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 11 Jul 2013 09:38:55 -0700 Subject: [PATCH 55/55] delete a function accidentally added on merge --- voxel-server/src/main.cpp | 126 -------------------------------------- 1 file changed, 126 deletions(-) diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 4e90df2646..f38b82886d 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -110,132 +110,6 @@ void eraseVoxelTreeAndCleanupNodeVisitData() { } } - -// Version of voxel distributor that sends each LOD level at a time -void resInVoxelDistributor(NodeList* nodeList, - NodeList::iterator& node, - VoxelNodeData* nodeData) { - ViewFrustum viewFrustum = nodeData->getCurrentViewFrustum(); - bool searchReset = false; - int searchLoops = 0; - int searchLevelWas = nodeData->getMaxSearchLevel(); - uint64_t start = usecTimestampNow(); - while (!searchReset && nodeData->nodeBag.isEmpty()) { - searchLoops++; - - searchLevelWas = nodeData->getMaxSearchLevel(); - int maxLevelReached = serverTree.searchForColoredNodes(nodeData->getMaxSearchLevel(), serverTree.rootNode, - viewFrustum, nodeData->nodeBag); - nodeData->setMaxLevelReached(maxLevelReached); - - // If nothing got added, then we bump our levels. - if (nodeData->nodeBag.isEmpty()) { - if (nodeData->getMaxLevelReached() < nodeData->getMaxSearchLevel()) { - nodeData->resetMaxSearchLevel(); - searchReset = true; - } else { - nodeData->incrementMaxSearchLevel(); - } - } - } - 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 in %d loops\n", - elapsedsec, nodeData->nodeBag.count(), searchLevelWas, searchLoops); - } else { - printf("WARNING! searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d in %d loops\n", - elapsedmsec, nodeData->nodeBag.count(), searchLevelWas, searchLoops); - } - } else if (::debugVoxelSending) { - printf("searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d in %d loops\n", - elapsedmsec, nodeData->nodeBag.count(), searchLevelWas, searchLoops); - } - - - // If we have something in our nodeBag, then turn them into packets and send them out... - if (!nodeData->nodeBag.isEmpty()) { - static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static - int bytesWritten = 0; - int packetsSentThisInterval = 0; - int truePacketsSent = 0; - int trueBytesSent = 0; - uint64_t start = usecTimestampNow(); - - bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); - while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { - if (!nodeData->nodeBag.isEmpty()) { - VoxelNode* subTree = nodeData->nodeBag.extract(); - - EncodeBitstreamParams params(nodeData->getMaxSearchLevel(), &viewFrustum, - nodeData->getWantColor(), WANT_EXISTS_BITS); - - bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, - nodeData->nodeBag, params); - - if (nodeData->getAvailable() >= bytesWritten) { - nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); - } else { - nodeList->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); - trueBytesSent += nodeData->getPacketLength(); - truePacketsSent++; - packetsSentThisInterval++; - nodeData->resetVoxelPacket(); - nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); - } - } else { - if (nodeData->isPacketWaiting()) { - nodeList->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); - trueBytesSent += nodeData->getPacketLength(); - truePacketsSent++; - nodeData->resetVoxelPacket(); - - } - packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left - } - } - // send the environment packets - if (shouldSendEnvironments) { - int envPacketLength = 1; - *tempOutputBuffer = PACKET_HEADER_ENVIRONMENT_DATA; - for (int i = 0; i < sizeof(environmentData) / sizeof(environmentData[0]); i++) { - envPacketLength += environmentData[i].getBroadcastData(tempOutputBuffer + envPacketLength); - } - nodeList->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength); - trueBytesSent += envPacketLength; - truePacketsSent++; - } - uint64_t end = usecTimestampNow(); - int elapsedmsec = (end - start)/1000; - if (elapsedmsec > 100) { - if (elapsedmsec > 1000) { - int elapsedsec = (end - start)/1000000; - printf("WARNING! packetLoop() took %d seconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n", - elapsedsec, trueBytesSent, truePacketsSent, searchLevelWas, nodeData->nodeBag.count()); - } else { - printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n", - elapsedmsec, trueBytesSent, truePacketsSent, searchLevelWas, nodeData->nodeBag.count()); - } - } else if (::debugVoxelSending) { - printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n", - elapsedmsec, trueBytesSent, truePacketsSent, searchLevelWas, nodeData->nodeBag.count()); - } - - // if during this last pass, we emptied our bag, then we want to move to the next level. - if (nodeData->nodeBag.isEmpty()) { - if (nodeData->getMaxLevelReached() < nodeData->getMaxSearchLevel()) { - nodeData->resetMaxSearchLevel(); - } else { - nodeData->incrementMaxSearchLevel(); - } - } - } -} - pthread_mutex_t treeLock; // Version of voxel distributor that sends the deepest LOD level at once