From 6755dfd8e4dddb52f851c65ec1f5d73c3ddac0b2 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 19 Jun 2013 11:46:22 -0700 Subject: [PATCH 1/4] Occlusion culling optimizations, added minimum polygon area, and only calculate projection matrix once --- libraries/voxels/src/CoverageMap.cpp | 58 +++++++++++++++++-- libraries/voxels/src/CoverageMap.h | 1 + libraries/voxels/src/ViewFrustum.cpp | 20 +++---- libraries/voxels/src/ViewFrustum.h | 2 + .../voxels/src/VoxelProjectedPolygon.cpp | 4 +- libraries/voxels/src/VoxelProjectedPolygon.h | 3 +- 6 files changed, 69 insertions(+), 19 deletions(-) diff --git a/libraries/voxels/src/CoverageMap.cpp b/libraries/voxels/src/CoverageMap.cpp index 76a04b2468..48a5b74923 100644 --- a/libraries/voxels/src/CoverageMap.cpp +++ b/libraries/voxels/src/CoverageMap.cpp @@ -13,6 +13,35 @@ int CoverageMap::_mapCount = 0; const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-2.f,-2.f), glm::vec2(4.f,4.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 CoverageMap::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); + + CoverageMap::CoverageMap(BoundingBox boundingBox, bool isRoot, bool managePolygons) : _isRoot(isRoot), _myBoundingBox(boundingBox), _managePolygons(managePolygons) { _mapCount++; @@ -51,9 +80,10 @@ void CoverageMap::erase() { } } -/** +/** Kee this debugging code for now.... if (_isRoot) { 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("_maxPolygonsUsed=%d\n",_maxPolygonsUsed); printLog("_totalPolygons=%d\n",_totalPolygons); @@ -63,7 +93,6 @@ void CoverageMap::erase() { _mapCount = 0; } **/ - } void CoverageMap::init() { @@ -143,6 +172,13 @@ void CoverageMap::storeInArray(VoxelProjectedPolygon* polygon) { // possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT CoverageMap::StorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, bool storeIt) { + + // 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()) { + return DOESNT_FIT; + } + if (_isRoot || _myBoundingBox.contains(polygon->getBoundingBox())) { // check to make sure this polygon isn't occluded by something at this level for (int i = 0; i < _polygonCount; i++) { @@ -157,8 +193,13 @@ CoverageMap::StorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, // want to report our inserted one as occluded, but we do want to add our inserted one. if (polygonAtThisLevel->getDistance() >= polygon->getDistance()) { if (storeIt) { - storeInArray(polygon); - return STORED; + if (polygon->getBoundingBox().area() > MINIMUM_POLYGON_AREA_TO_STORE) { + //printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area()); + storeInArray(polygon); + return STORED; + } else { + return NOT_STORED; + } } else { return NOT_STORED; } @@ -183,8 +224,13 @@ CoverageMap::StorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, // 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) { - storeInArray(polygon); - return STORED; + if (polygon->getBoundingBox().area() > MINIMUM_POLYGON_AREA_TO_STORE) { + //printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area()); + storeInArray(polygon); + return STORED; + } else { + return NOT_STORED; + } } else { return NOT_STORED; } diff --git a/libraries/voxels/src/CoverageMap.h b/libraries/voxels/src/CoverageMap.h index 38c7dc6145..282a001cb0 100644 --- a/libraries/voxels/src/CoverageMap.h +++ b/libraries/voxels/src/CoverageMap.h @@ -18,6 +18,7 @@ public: 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; CoverageMap(BoundingBox boundingBox = ROOT_BOUNDING_BOX, bool isRoot = IS_ROOT, bool managePolygons = true); ~CoverageMap(); diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index 786e5a1af0..f231688c77 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -115,6 +115,14 @@ void ViewFrustum::calculate() { _planes[NEAR_PLANE ].set3Points(_nearBottomRight,_nearBottomLeft,_nearTopLeft); _planes[FAR_PLANE ].set3Points(_farBottomLeft,_farBottomRight,_farTopRight); + // Also calculate our projection matrix in case people want to project points... + // Projection matrix : Field of View, ratio, display range : near to far + glm::mat4 projection = glm::perspective(_fieldOfView, _aspectRatio, _nearClip, _farClip); + glm::vec3 lookAt = _position + _direction; + glm::mat4 view = glm::lookAt(_position, lookAt, _up); + + // Our ModelViewProjection : multiplication of our 3 matrices (note: model is identity, so we can drop it) + _ourModelViewProjectionMatrix = projection * view; // Remember, matrix multiplication is the other way around } //enum { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE }; @@ -425,18 +433,10 @@ void ViewFrustum::printDebugDetails() const { _eyeOffsetOrientation.w ); } - glm::vec2 ViewFrustum::projectPoint(glm::vec3 point, bool& pointInView) const { - // Projection matrix : Field of View, ratio, display range : near to far - glm::mat4 projection = glm::perspective(_fieldOfView, _aspectRatio, _nearClip, _farClip); - glm::vec3 lookAt = _position + _direction; - glm::mat4 view = glm::lookAt(_position, lookAt, _up); - // Our ModelViewProjection : multiplication of our 3 matrices (note: model is identity, so we can drop it) - glm::mat4 VP = projection * view; // Remember, matrix multiplication is the other way around - - glm::vec4 pointVec4 = glm::vec4(point,1); - glm::vec4 projectedPointVec4 = VP * pointVec4; + glm::vec4 pointVec4 = glm::vec4(point,1) ; + glm::vec4 projectedPointVec4 = _ourModelViewProjectionMatrix * pointVec4; pointInView = (projectedPointVec4.w > 0); // math! If the w result is negative then the point is behind the viewer // what happens with w is 0??? diff --git a/libraries/voxels/src/ViewFrustum.h b/libraries/voxels/src/ViewFrustum.h index f2b329ee30..8ad18f6246 100644 --- a/libraries/voxels/src/ViewFrustum.h +++ b/libraries/voxels/src/ViewFrustum.h @@ -137,6 +137,8 @@ private: const char* debugPlaneName (int plane) const; + // Used to project points + glm::mat4 _ourModelViewProjectionMatrix; }; diff --git a/libraries/voxels/src/VoxelProjectedPolygon.cpp b/libraries/voxels/src/VoxelProjectedPolygon.cpp index baef436d08..000758860a 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.cpp +++ b/libraries/voxels/src/VoxelProjectedPolygon.cpp @@ -48,7 +48,7 @@ void VoxelProjectedPolygon::setVertex(int vertex, const glm::vec2& point) { }; -bool VoxelProjectedPolygon::occludes(const VoxelProjectedPolygon& occludee) const { +bool VoxelProjectedPolygon::occludes(const VoxelProjectedPolygon& occludee, bool checkAllInView) const { // 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 @@ -56,7 +56,7 @@ bool VoxelProjectedPolygon::occludes(const VoxelProjectedPolygon& occludee) cons // this is true, but unfortunately, we're not quite handling projects in the // case when SOME points are in view and others are not. So, we will not consider // occlusion for any shadows that are partially in view. - if (!getAllInView() || !occludee.getAllInView() ) { + if (checkAllInView && (!getAllInView() || !occludee.getAllInView())) { return false; } diff --git a/libraries/voxels/src/VoxelProjectedPolygon.h b/libraries/voxels/src/VoxelProjectedPolygon.h index c6e4b665fa..227e1895a0 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.h +++ b/libraries/voxels/src/VoxelProjectedPolygon.h @@ -20,6 +20,7 @@ public: glm::vec2 corner; glm::vec2 size; bool contains(const BoundingBox& box) const; + float area() const { return size.x * size.y; }; void printDebugDetails(const char* label=NULL) const; }; @@ -48,7 +49,7 @@ public: bool getAllInView() const { return _allInView; }; void setAllInView(bool allInView) { _allInView = allInView; }; - bool occludes(const VoxelProjectedPolygon& occludee) const; + bool occludes(const VoxelProjectedPolygon& occludee, bool checkAllInView = false) const; bool pointInside(const glm::vec2& point) const; float getMaxX() const { return _maxX; } From 1284f9d09aaa9c42613404a23a55a0b1ade3fbfe Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 19 Jun 2013 13:45:13 -0700 Subject: [PATCH 2/4] naming cleanup and some optimizations --- interface/src/VoxelSystem.cpp | 20 ++++---- libraries/voxels/src/AABox.cpp | 11 ++++ libraries/voxels/src/AABox.h | 1 + libraries/voxels/src/ViewFrustum.cpp | 75 +++++++++++++--------------- libraries/voxels/src/ViewFrustum.h | 9 ++-- libraries/voxels/src/VoxelTree.cpp | 23 +++++---- 6 files changed, 74 insertions(+), 65 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 4a81d3ec2d..25ed13b338 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -1202,19 +1202,19 @@ bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraDat AABox voxelBox = node->getAABox(); voxelBox.scale(TREE_SCALE); - VoxelProjectedPolygon* voxelShadow = new VoxelProjectedPolygon(args->viewFrustum->getProjectedShadow(voxelBox)); + 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 (!voxelShadow->getAllInView()) { + if (!voxelPolygon->getAllInView()) { args->nonLeavesOutOfView++; - delete voxelShadow; + delete voxelPolygon; return true; } - CoverageMap::StorageResult result = args->map->checkMap(voxelShadow, false); + CoverageMap::StorageResult result = args->map->checkMap(voxelPolygon, false); if (result == CoverageMap::OCCLUDED) { args->nonLeavesOccluded++; - delete voxelShadow; + delete voxelPolygon; FalseColorizeSubTreeOperationArgs subArgs; subArgs.color[0] = 0; @@ -1230,7 +1230,7 @@ bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraDat return false; } - delete voxelShadow; + delete voxelPolygon; return true; // keep looking... } @@ -1239,16 +1239,16 @@ bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraDat AABox voxelBox = node->getAABox(); voxelBox.scale(TREE_SCALE); - VoxelProjectedPolygon* voxelShadow = new VoxelProjectedPolygon(args->viewFrustum->getProjectedShadow(voxelBox)); + 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 (!voxelShadow->getAllInView()) { + if (!voxelPolygon->getAllInView()) { args->outOfView++; - delete voxelShadow; + delete voxelPolygon; return true; } - CoverageMap::StorageResult result = args->map->checkMap(voxelShadow, true); + CoverageMap::StorageResult result = args->map->checkMap(voxelPolygon, true); if (result == CoverageMap::OCCLUDED) { node->setFalseColor(255, 0, 0); args->occludedVoxels++; diff --git a/libraries/voxels/src/AABox.cpp b/libraries/voxels/src/AABox.cpp index d4b7e1383c..36e65561c3 100644 --- a/libraries/voxels/src/AABox.cpp +++ b/libraries/voxels/src/AABox.cpp @@ -105,6 +105,17 @@ bool AABox::contains(const glm::vec3& point) const { isWithin(point.z, _corner.z, _size.z); } +bool AABox::contains(const AABox& otherBox) const { + for (int v = BOTTOM_LEFT_NEAR; v < TOP_LEFT_FAR; v++) { + glm::vec3 vertex = otherBox.getVertex((BoxVertex)v); + if (!contains(vertex)) { + return false; + } + } + return true; +} + + // determines whether a value is within the expanded extents static bool isWithinExpanded(float value, float corner, float size, float expansion) { return value >= corner - expansion && value <= corner + size + expansion; diff --git a/libraries/voxels/src/AABox.h b/libraries/voxels/src/AABox.h index d295f24aea..735799cd05 100644 --- a/libraries/voxels/src/AABox.h +++ b/libraries/voxels/src/AABox.h @@ -63,6 +63,7 @@ public: glm::vec3 getVertex(BoxVertex vertex) const; bool contains(const glm::vec3& point) const; + bool contains(const AABox& otherBox) const; bool expandedContains(const glm::vec3& point, float expansion) const; bool expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const; bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const; diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index f231688c77..4ded457d44 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -123,6 +123,10 @@ void ViewFrustum::calculate() { // Our ModelViewProjection : multiplication of our 3 matrices (note: model is identity, so we can drop it) _ourModelViewProjectionMatrix = projection * view; // Remember, matrix multiplication is the other way around + + // Set up our keyhole bounding box... + glm::vec3 corner = _position - _keyholeRadius; + _keyholeBoundingBox = AABox(corner,(_keyholeRadius * 2.0f)); } //enum { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE }; @@ -138,14 +142,14 @@ const char* ViewFrustum::debugPlaneName (int plane) const { return "Unknown"; } -ViewFrustum::location ViewFrustum::pointInSphere(const glm::vec3& point, const glm::vec3& center, float radius ) const { +ViewFrustum::location ViewFrustum::pointInKeyhole(const glm::vec3& point) const { ViewFrustum::location result = INTERSECT; - float distance = glm::distance(point, center); - if (distance > radius) { + float distance = glm::distance(point, _position); + if (distance > _keyholeRadius) { result = OUTSIDE; - } else if (distance < radius) { + } else if (distance < _keyholeRadius) { result = INSIDE; } @@ -155,15 +159,13 @@ ViewFrustum::location ViewFrustum::pointInSphere(const glm::vec3& point, const g // To determine if two spheres intersect, simply calculate the distance between the centers of the two spheres. // If the distance is greater than the sum of the two sphere radii, they don’t intersect. Otherwise they intersect. // If the distance plus the radius of sphere A is less than the radius of sphere B then, sphere A is inside of sphere B -ViewFrustum::location ViewFrustum::sphereInSphere(const glm::vec3& centerA, float radiusA, - const glm::vec3& centerB, float radiusB ) const { - +ViewFrustum::location ViewFrustum::sphereInKeyhole(const glm::vec3& center, float radius) const { ViewFrustum::location result = INTERSECT; - float distanceFromAtoB = glm::distance(centerA, centerB); - if (distanceFromAtoB > (radiusA + radiusB)) { + float distance = glm::distance(center, _position); + if (distance > (radius + _keyholeRadius)) { result = OUTSIDE; - } else if ((distanceFromAtoB + radiusA) < radiusB) { + } else if ((distance + radius) < _keyholeRadius) { result = INSIDE; } @@ -174,9 +176,16 @@ ViewFrustum::location ViewFrustum::sphereInSphere(const glm::vec3& centerA, floa // A box is inside a sphere if all of its corners are inside the sphere // A box intersects a sphere if any of its edges (as rays) interesect the sphere // A box is outside a sphere if none of its edges (as rays) interesect the sphere -ViewFrustum::location ViewFrustum::boxInSphere(const AABox& box, const glm::vec3& center, float radius) const { +ViewFrustum::location ViewFrustum::boxInKeyhole(const AABox& box) const { + + // First check to see if the box is in the bounding box for the sphere, if it's not, then we can short circuit + // this and not check with sphere penetration which is more expensive + if (!_keyholeBoundingBox.contains(box)) { + return OUTSIDE; + } + glm::vec3 penetration; - bool intersects = box.findSpherePenetration(center, radius, penetration); + bool intersects = box.findSpherePenetration(_position, _keyholeRadius, penetration); ViewFrustum::location result = OUTSIDE; @@ -185,32 +194,18 @@ ViewFrustum::location ViewFrustum::boxInSphere(const AABox& box, const glm::vec3 result = INTERSECT; // test all the corners, if they are all inside the sphere, the entire box is in the sphere - glm::vec3 testPoint = box.getCorner(); - glm::vec3 size = box.getSize(); - if (pointInSphere(testPoint, center, radius)) { - testPoint = box.getCorner() + glm::vec3(size.x, 0.0f, 0.0f); - if (pointInSphere(testPoint, center, radius)) { - testPoint = box.getCorner() + glm::vec3(0.0f, 0.0f, size.z); - if (pointInSphere(testPoint, center, radius)) { - testPoint = box.getCorner() + glm::vec3(size.x, 0.0f, size.z); - if (pointInSphere(testPoint, center, radius)) { - testPoint = box.getCorner() + glm::vec3(0.0f, size.y, 0.0f); - if (pointInSphere(testPoint, center, radius)) { - testPoint = box.getCorner() + glm::vec3(size.x, size.y, 0.0f); - if (pointInSphere(testPoint, center, radius)) { - testPoint = box.getCorner() + glm::vec3(0.0f, size.y, size.z); - if (pointInSphere(testPoint, center, radius)) { - testPoint = box.getCorner() + glm::vec3(size.x, size.y, size.z); - if (pointInSphere(testPoint, center, radius)) { - result = INSIDE; - } - } - } - } - } - } + bool allPointsInside = true; // assume the best + for (int v = BOTTOM_LEFT_NEAR; v < TOP_LEFT_FAR; v++) { + glm::vec3 vertex = box.getVertex((BoxVertex)v); + if (!pointInKeyhole(vertex)) { + allPointsInside = false; + break; } } + + if (allPointsInside) { + result = INSIDE; + } } return result; @@ -222,7 +217,7 @@ ViewFrustum::location ViewFrustum::pointInFrustum(const glm::vec3& point) const // If we have a keyholeRadius, check that first, since it's cheaper if (_keyholeRadius >= 0.0f) { - keyholeResult = pointInSphere(point, _position, _keyholeRadius); + keyholeResult = pointInKeyhole(point); } if (keyholeResult == INSIDE) { return keyholeResult; @@ -245,7 +240,7 @@ ViewFrustum::location ViewFrustum::sphereInFrustum(const glm::vec3& center, floa // If we have a keyholeRadius, check that first, since it's cheaper if (_keyholeRadius >= 0.0f) { - keyholeResult = sphereInSphere(center, radius, _position, _keyholeRadius); + keyholeResult = sphereInKeyhole(center, radius); } if (keyholeResult == INSIDE) { return keyholeResult; @@ -272,7 +267,7 @@ ViewFrustum::location ViewFrustum::boxInFrustum(const AABox& box) const { // If we have a keyholeRadius, check that first, since it's cheaper if (_keyholeRadius >= 0.0f) { - keyholeResult = boxInSphere(box, _position, _keyholeRadius); + keyholeResult = boxInKeyhole(box); } if (keyholeResult == INSIDE) { return keyholeResult; @@ -503,7 +498,7 @@ const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_SHADOW_VERTEX_COUNT+1] {6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // back, top, left }; -VoxelProjectedPolygon ViewFrustum::getProjectedShadow(const AABox& box) const { +VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { glm::vec3 bottomNearRight = box.getCorner(); glm::vec3 topFarLeft = box.getCorner() + box.getSize(); int lookUp = ((_position.x < bottomNearRight.x) ) // 1 = right | compute 6-bit diff --git a/libraries/voxels/src/ViewFrustum.h b/libraries/voxels/src/ViewFrustum.h index 8ad18f6246..6833eb6134 100644 --- a/libraries/voxels/src/ViewFrustum.h +++ b/libraries/voxels/src/ViewFrustum.h @@ -89,14 +89,14 @@ public: void printDebugDetails() const; glm::vec2 projectPoint(glm::vec3 point, bool& pointInView) const; - VoxelProjectedPolygon getProjectedShadow(const AABox& box) const; + VoxelProjectedPolygon getProjectedPolygon(const AABox& box) const; private: // Used for keyhole calculations - ViewFrustum::location pointInSphere(const glm::vec3& point, const glm::vec3& center, float radius) const; - ViewFrustum::location sphereInSphere(const glm::vec3& centerA, float radiusA, const glm::vec3& centerB, float radiusB) const; - ViewFrustum::location boxInSphere(const AABox& box, const glm::vec3& center, float radius) const; + ViewFrustum::location pointInKeyhole(const glm::vec3& point) const; + ViewFrustum::location sphereInKeyhole(const glm::vec3& center, float radius) const; + ViewFrustum::location boxInKeyhole(const AABox& box) const; // camera location/orientation attributes glm::vec3 _position; @@ -117,6 +117,7 @@ private: // keyhole attributes float _keyholeRadius; + AABox _keyholeBoundingBox; // Calculated values diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index ec9ae008e6..06554abba6 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1125,15 +1125,15 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp //node->printDebugDetails("upper section, params.wantOcclusionCulling... node="); AABox voxelBox = node->getAABox(); voxelBox.scale(TREE_SCALE); - VoxelProjectedPolygon* voxelShadow = new VoxelProjectedPolygon(params.viewFrustum->getProjectedShadow(voxelBox)); + VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(params.viewFrustum->getProjectedPolygon(voxelBox)); // In order to check occlusion culling, the shadow has to be "all in view" otherwise, we will ignore occlusion // culling and proceed as normal - if (voxelShadow->getAllInView()) { - //node->printDebugDetails("upper section, voxelShadow->getAllInView() node="); + if (voxelPolygon->getAllInView()) { + //node->printDebugDetails("upper section, voxelPolygon->getAllInView() node="); - CoverageMap::StorageResult result = params.map->checkMap(voxelShadow, false); - delete voxelShadow; // cleanup + CoverageMap::StorageResult result = params.map->checkMap(voxelPolygon, false); + delete voxelPolygon; // cleanup if (result == CoverageMap::OCCLUDED) { //node->printDebugDetails("upper section, non-Leaf is occluded!! node="); //args->nonLeavesOccluded++; @@ -1147,7 +1147,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp //node->printDebugDetails("upper section, shadow Not in view node="); // If this shadow wasn't "all in view" then we ignored it for occlusion culling, but // we do need to clean up memory and proceed as normal... - delete voxelShadow; + delete voxelPolygon; } } } @@ -1241,17 +1241,18 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp AABox voxelBox = childNode->getAABox(); voxelBox.scale(TREE_SCALE); - VoxelProjectedPolygon* voxelShadow = new VoxelProjectedPolygon(params.viewFrustum->getProjectedShadow(voxelBox)); + VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon( + params.viewFrustum->getProjectedPolygon(voxelBox)); // In order to check occlusion culling, the shadow has to be "all in view" otherwise, we will ignore occlusion // culling and proceed as normal - if (voxelShadow->getAllInView()) { - CoverageMap::StorageResult result = params.map->checkMap(voxelShadow, true); + if (voxelPolygon->getAllInView()) { + CoverageMap::StorageResult result = params.map->checkMap(voxelPolygon, true); // In all cases where the shadow wasn't stored, we need to free our own memory. // In the case where it is stored, the CoverageMap will free memory for us later. if (result != CoverageMap::STORED) { - delete voxelShadow; + delete voxelPolygon; } // If while attempting to add this voxel's shadow, we determined it was occluded, then @@ -1260,7 +1261,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp childIsOccluded = true; } } else { - delete voxelShadow; + delete voxelPolygon; } } // wants occlusion culling & isLeaf() From 91f6d0fde5d27fa1d0f1f45b083453b1604c1f49 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 19 Jun 2013 15:29:50 -0700 Subject: [PATCH 3/4] added copyright message --- libraries/voxels/src/CoverageMap.cpp | 1 + libraries/voxels/src/CoverageMap.h | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/voxels/src/CoverageMap.cpp b/libraries/voxels/src/CoverageMap.cpp index 48a5b74923..79b9b58dfb 100644 --- a/libraries/voxels/src/CoverageMap.cpp +++ b/libraries/voxels/src/CoverageMap.cpp @@ -3,6 +3,7 @@ // hifi // // Added by Brad Hefta-Gaub on 06/11/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // #include "CoverageMap.h" diff --git a/libraries/voxels/src/CoverageMap.h b/libraries/voxels/src/CoverageMap.h index 282a001cb0..0def65140c 100644 --- a/libraries/voxels/src/CoverageMap.h +++ b/libraries/voxels/src/CoverageMap.h @@ -3,6 +3,7 @@ // hifi // // Added by Brad Hefta-Gaub on 06/11/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // #ifndef _COVERAGE_MAP_ From cadd596508e99be734b6e2263130c81b786d6f1e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 19 Jun 2013 17:52:01 -0700 Subject: [PATCH 4/4] implement partitioned CoverageRegions in CoverageMap to further optimize polygon compares --- interface/src/Application.cpp | 1 + interface/src/VoxelSystem.cpp | 12 +- libraries/voxels/src/CoverageMap.cpp | 355 ++++++++++++------ libraries/voxels/src/CoverageMap.h | 67 +++- .../voxels/src/VoxelProjectedPolygon.cpp | 24 ++ libraries/voxels/src/VoxelProjectedPolygon.h | 5 + libraries/voxels/src/VoxelTree.cpp | 10 +- 7 files changed, 338 insertions(+), 136 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a7d5b63d98..4b2842ea0d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -980,6 +980,7 @@ void Application::doFalseColorizeInView() { } void Application::doFalseColorizeOccluded() { + CoverageMap::wantDebugging = true; _voxels.falseColorizeOccluded(); } diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 25ed13b338..69edf376ee 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -1211,8 +1211,8 @@ bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraDat return true; } - CoverageMap::StorageResult result = args->map->checkMap(voxelPolygon, false); - if (result == CoverageMap::OCCLUDED) { + CoverageMapStorageResult result = args->map->checkMap(voxelPolygon, false); + if (result == OCCLUDED) { args->nonLeavesOccluded++; delete voxelPolygon; @@ -1248,14 +1248,14 @@ bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraDat return true; } - CoverageMap::StorageResult result = args->map->checkMap(voxelPolygon, true); - if (result == CoverageMap::OCCLUDED) { + CoverageMapStorageResult result = args->map->checkMap(voxelPolygon, true); + if (result == OCCLUDED) { node->setFalseColor(255, 0, 0); args->occludedVoxels++; - } else if (result == CoverageMap::STORED) { + } else if (result == STORED) { args->notOccludedVoxels++; //printLog("***** falseColorizeOccludedOperation() NODE is STORED *****\n"); - } else if (result == CoverageMap::DOESNT_FIT) { + } else if (result == DOESNT_FIT) { //printLog("***** falseColorizeOccludedOperation() NODE DOESNT_FIT???? *****\n"); } } diff --git a/libraries/voxels/src/CoverageMap.cpp b/libraries/voxels/src/CoverageMap.cpp index 79b9b58dfb..eae1262a59 100644 --- a/libraries/voxels/src/CoverageMap.cpp +++ b/libraries/voxels/src/CoverageMap.cpp @@ -12,6 +12,9 @@ #include "Log.h" int CoverageMap::_mapCount = 0; +int CoverageMap::_checkMapRootCalls = 0; +bool CoverageMap::wantDebugging = false; + const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-2.f,-2.f), glm::vec2(4.f,4.f)); // Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space. @@ -42,9 +45,16 @@ const float TYPICAL_SCREEN_PIXEL_WIDTH = (2.0f / TYPICAL_SCREEN_WIDTH_IN_PIXELS) const float CoverageMap::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); - CoverageMap::CoverageMap(BoundingBox boundingBox, bool isRoot, bool managePolygons) : - _isRoot(isRoot), _myBoundingBox(boundingBox), _managePolygons(managePolygons) { + _isRoot(isRoot), + _myBoundingBox(boundingBox), + _managePolygons(managePolygons), + _topHalf (boundingBox.topHalf() , false, managePolygons, TOP_HALF ), + _bottomHalf (boundingBox.bottomHalf(), false, managePolygons, BOTTOM_HALF ), + _leftHalf (boundingBox.leftHalf() , false, managePolygons, LEFT_HALF ), + _rightHalf (boundingBox.rightHalf() , false, managePolygons, RIGHT_HALF ), + _remainder (boundingBox, isRoot, managePolygons, REMAINDER ) +{ _mapCount++; init(); //printLog("CoverageMap created... _mapCount=%d\n",_mapCount); @@ -55,25 +65,13 @@ CoverageMap::~CoverageMap() { }; void CoverageMap::erase() { - // If we're in charge of managing the polygons, then clean them up first - if (_managePolygons) { - for (int i = 0; i < _polygonCount; i++) { - delete _polygons[i]; - _polygons[i] = NULL; // do we need to do this? - } - } - - // Now, clean up our local storage - _polygonCount = 0; - _polygonArraySize = 0; - if (_polygons) { - delete[] _polygons; - _polygons = NULL; - } - if (_polygonDistances) { - delete[] _polygonDistances; - _polygonDistances = NULL; - } + // tell our regions to erase() + _topHalf.erase(); + _bottomHalf.erase(); + _leftHalf.erase(); + _rightHalf.erase(); + _remainder.erase(); + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { if (_childMaps[i]) { delete _childMaps[i]; @@ -81,26 +79,25 @@ void CoverageMap::erase() { } } -/** Kee this debugging code for now.... - if (_isRoot) { + 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("_maxPolygonsUsed=%d\n",_maxPolygonsUsed); - printLog("_totalPolygons=%d\n",_totalPolygons); - - _maxPolygonsUsed = 0; - _totalPolygons = 0; + printLog("_checkMapRootCalls=%d\n",_checkMapRootCalls); + printLog("_maxPolygonsUsed=%d\n",CoverageRegion::_maxPolygonsUsed); + printLog("_totalPolygons=%d\n",CoverageRegion::_totalPolygons); + printLog("_occlusionTests=%d\n",CoverageRegion::_occlusionTests); + printLog("_outOfOrderPolygon=%d\n",CoverageRegion::_outOfOrderPolygon); + CoverageRegion::_maxPolygonsUsed = 0; + CoverageRegion::_totalPolygons = 0; + CoverageRegion::_occlusionTests = 0; + CoverageRegion::_outOfOrderPolygon = 0; _mapCount = 0; + _checkMapRootCalls = 0; } -**/ } void CoverageMap::init() { - _polygonCount = 0; - _polygonArraySize = 0; - _polygons = NULL; - _polygonDistances = NULL; memset(_childMaps,0,sizeof(_childMaps)); } @@ -124,55 +121,12 @@ BoundingBox CoverageMap::getChildBoundingBox(int childIndex) { return result; } - -void CoverageMap::growPolygonArray() { - VoxelProjectedPolygon** newPolygons = new VoxelProjectedPolygon*[_polygonArraySize + DEFAULT_GROW_SIZE]; - float* newDistances = new float[_polygonArraySize + DEFAULT_GROW_SIZE]; - - - if (_polygons) { - memcpy(newPolygons, _polygons, sizeof(VoxelProjectedPolygon*) * _polygonCount); - delete[] _polygons; - memcpy(newDistances, _polygonDistances, sizeof(float) * _polygonCount); - delete[] _polygonDistances; - } - _polygons = newPolygons; - _polygonDistances = newDistances; - _polygonArraySize = _polygonArraySize + DEFAULT_GROW_SIZE; - //printLog("CoverageMap::growPolygonArray() _polygonArraySize=%d...\n",_polygonArraySize); -} - -int CoverageMap::_maxPolygonsUsed = 0; -int CoverageMap::_totalPolygons = 0; - -// just handles storage in the array, doesn't test for occlusion or -// determining if this is the correct map to store in! -void CoverageMap::storeInArray(VoxelProjectedPolygon* polygon) { - - _totalPolygons++; - - if (_polygonArraySize < _polygonCount + 1) { - 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); - - if (_polygonCount > _maxPolygonsUsed) { - _maxPolygonsUsed = _polygonCount; - //printLog("CoverageMap new _maxPolygonsUsed reached=%d\n",_maxPolygonsUsed); - //_myBoundingBox.printDebugDetails("map._myBoundingBox"); - } -} - - // possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT -CoverageMap::StorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, bool storeIt) { +CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, bool storeIt) { + + if (_isRoot) { + _checkMapRootCalls++; + } // 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. @@ -180,35 +134,46 @@ CoverageMap::StorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, return DOESNT_FIT; } - if (_isRoot || _myBoundingBox.contains(polygon->getBoundingBox())) { - // 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. - 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()) { - if (storeIt) { - if (polygon->getBoundingBox().area() > MINIMUM_POLYGON_AREA_TO_STORE) { - //printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area()); - storeInArray(polygon); - return 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; - } + BoundingBox polygonBox(polygon->getBoundingBox()); + if (_isRoot || _myBoundingBox.contains(polygonBox)) { + + CoverageMapStorageResult result = NOT_STORED; + CoverageRegion* storeIn = &_remainder; + 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; } + + // 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 + // half. In either of these cases, we want to check our remainder region to see if its + // occluded by anything there + if (!(result == STORED || result == OCCLUDED)) { + result = _remainder.checkRegion(polygon, polygonBox, storeIt); + } + + // 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) { + return result; + } + // 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 @@ -225,9 +190,9 @@ CoverageMap::StorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, // 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) { - if (polygon->getBoundingBox().area() > MINIMUM_POLYGON_AREA_TO_STORE) { + if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) { //printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area()); - storeInArray(polygon); + storeIn->storeInArray(polygon); return STORED; } else { return NOT_STORED; @@ -238,3 +203,171 @@ CoverageMap::StorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, } return DOESNT_FIT; } + + +CoverageRegion::CoverageRegion(BoundingBox boundingBox, bool isRoot, bool managePolygons, RegionName regionName) : + _isRoot(isRoot), + _myBoundingBox(boundingBox), + _managePolygons(managePolygons), + _regionName(regionName) +{ + init(); +}; + +CoverageRegion::~CoverageRegion() { + erase(); +}; + +void CoverageRegion::init() { + _polygonCount = 0; + _polygonArraySize = 0; + _polygons = NULL; + _polygonDistances = NULL; +} + + +void CoverageRegion::erase() { + +/* + if (_polygonCount) { + printLog("CoverageRegion::erase()...\n"); + printLog("_polygonCount=%d\n",_polygonCount); + _myBoundingBox.printDebugDetails(getRegionName()); + //for (int i = 0; i < _polygonCount; i++) { + // printLog("_polygons[%d]=",i); + // _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++) { + delete _polygons[i]; + _polygons[i] = NULL; // do we need to do this? + } + } + + // Now, clean up our local storage + _polygonCount = 0; + _polygonArraySize = 0; + if (_polygons) { + delete[] _polygons; + _polygons = NULL; + } + if (_polygonDistances) { + delete[] _polygonDistances; + _polygonDistances = NULL; + } +} + +void CoverageRegion::growPolygonArray() { + VoxelProjectedPolygon** newPolygons = new VoxelProjectedPolygon*[_polygonArraySize + DEFAULT_GROW_SIZE]; + float* newDistances = new float[_polygonArraySize + DEFAULT_GROW_SIZE]; + + + if (_polygons) { + memcpy(newPolygons, _polygons, sizeof(VoxelProjectedPolygon*) * _polygonCount); + delete[] _polygons; + memcpy(newDistances, _polygonDistances, sizeof(float) * _polygonCount); + delete[] _polygonDistances; + } + _polygons = newPolygons; + _polygonDistances = newDistances; + _polygonArraySize = _polygonArraySize + DEFAULT_GROW_SIZE; + //printLog("CoverageMap::growPolygonArray() _polygonArraySize=%d...\n",_polygonArraySize); +} + +const char* CoverageRegion::getRegionName() const { + switch (_regionName) { + case TOP_HALF: + return "TOP_HALF"; + case BOTTOM_HALF: + return "BOTTOM_HALF"; + case LEFT_HALF: + return "LEFT_HALF"; + case RIGHT_HALF: + return "RIGHT_HALF"; + default: + case REMAINDER: + return "REMAINDER"; + } + return "REMAINDER"; +} + +int CoverageRegion::_maxPolygonsUsed = 0; +int CoverageRegion::_totalPolygons = 0; +int CoverageRegion::_occlusionTests = 0; +int CoverageRegion::_outOfOrderPolygon = 0; + +// 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++; + + if (_polygonArraySize < _polygonCount + 1) { + 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); + + // Debugging and Optimization Tuning code. + if (_polygonCount > _maxPolygonsUsed) { + _maxPolygonsUsed = _polygonCount; + //printLog("CoverageRegion new _maxPolygonsUsed reached=%d region=%s\n",_maxPolygonsUsed, getRegionName()); + //_myBoundingBox.printDebugDetails("map._myBoundingBox"); + } else { + //printLog("CoverageRegion::storeInArray() _polygonCount=%d region=%s\n",_polygonCount, getRegionName()); + } +} + + + +CoverageMapStorageResult CoverageRegion::checkRegion(VoxelProjectedPolygon* polygon, const BoundingBox& polygonBox, bool storeIt) { + + CoverageMapStorageResult result = DOESNT_FIT; + + 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. + + + _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 { + 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; + } + } + } + return result; +} diff --git a/libraries/voxels/src/CoverageMap.h b/libraries/voxels/src/CoverageMap.h index 0def65140c..8b01eabeee 100644 --- a/libraries/voxels/src/CoverageMap.h +++ b/libraries/voxels/src/CoverageMap.h @@ -12,6 +12,45 @@ #include #include "VoxelProjectedPolygon.h" +typedef enum {STORED, OCCLUDED, DOESNT_FIT, NOT_STORED} CoverageMapStorageResult; +typedef enum {TOP_HALF, BOTTOM_HALF, LEFT_HALF, RIGHT_HALF, REMAINDER} RegionName; + +class CoverageRegion { + +public: + + CoverageRegion(BoundingBox boundingBox, bool isRoot, bool managePolygons = true, RegionName regionName = REMAINDER); + ~CoverageRegion(); + + CoverageMapStorageResult checkRegion(VoxelProjectedPolygon* polygon, const BoundingBox& polygonBox, bool storeIt); + void storeInArray(VoxelProjectedPolygon* polygon); + + bool contains(const BoundingBox& box) const { return _myBoundingBox.contains(box); }; + void erase(); // erase the coverage region + + static int _maxPolygonsUsed; + static int _totalPolygons; + static int _occlusionTests; + static int _outOfOrderPolygon; + + + const char* getRegionName() const; + +private: + void init(); + + bool _isRoot; // is this map the root, if so, it never returns DOESNT_FIT + BoundingBox _myBoundingBox; + bool _managePolygons; // will the coverage map delete the polygons on destruct + RegionName _regionName; + int _polygonCount; // how many polygons at this level + int _polygonArraySize; // how much room is there to store polygons at this level + VoxelProjectedPolygon** _polygons; + float* _polygonDistances; + void growPolygonArray(); + static const int DEFAULT_GROW_SIZE = 100; +}; + class CoverageMap { public: @@ -24,32 +63,32 @@ public: CoverageMap(BoundingBox boundingBox = ROOT_BOUNDING_BOX, bool isRoot = IS_ROOT, bool managePolygons = true); ~CoverageMap(); - typedef enum {STORED, OCCLUDED, DOESNT_FIT, NOT_STORED} StorageResult; - StorageResult checkMap(VoxelProjectedPolygon* polygon, bool storeIt = true); + CoverageMapStorageResult checkMap(VoxelProjectedPolygon* polygon, bool storeIt = true); BoundingBox getChildBoundingBox(int childIndex); void erase(); // erase the coverage map + static bool wantDebugging; + private: void init(); - void growPolygonArray(); - void storeInArray(VoxelProjectedPolygon* polygon); bool _isRoot; // is this map the root, if so, it never returns DOESNT_FIT BoundingBox _myBoundingBox; - bool _managePolygons; // will the coverage map delete the polygons on destruct - int _polygonCount; // how many polygons at this level - int _polygonArraySize; // how much room is there to store polygons at this level - VoxelProjectedPolygon** _polygons; - float* _polygonDistances; CoverageMap* _childMaps[NUMBER_OF_CHILDREN]; + bool _managePolygons; // will the coverage map delete the polygons on destruct + + // We divide the map into 5 regions representing each possible half of the map, and the whole map + // this allows us to keep the list of polygons shorter + CoverageRegion _topHalf; + CoverageRegion _bottomHalf; + CoverageRegion _leftHalf; + CoverageRegion _rightHalf; + CoverageRegion _remainder; - static const int DEFAULT_GROW_SIZE = 100; - static int _mapCount; - static int _maxPolygonsUsed; - static int _totalPolygons; - + static int _mapCount; + static int _checkMapRootCalls; }; diff --git a/libraries/voxels/src/VoxelProjectedPolygon.cpp b/libraries/voxels/src/VoxelProjectedPolygon.cpp index 000758860a..c609b27df4 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.cpp +++ b/libraries/voxels/src/VoxelProjectedPolygon.cpp @@ -10,6 +10,30 @@ #include "Log.h" +BoundingBox BoundingBox::topHalf() const { + float halfY = size.y/2.0f; + BoundingBox result(glm::vec2(corner.x,corner.y + halfY), glm::vec2(size.x, halfY)); + return result; +} + +BoundingBox BoundingBox::bottomHalf() const { + float halfY = size.y/2.0f; + BoundingBox result(corner, glm::vec2(size.x, halfY)); + return result; +} + +BoundingBox BoundingBox::leftHalf() const { + float halfX = size.x/2.0f; + BoundingBox result(corner, glm::vec2(halfX, size.y)); + return result; +} + +BoundingBox BoundingBox::rightHalf() const { + float halfX = size.x/2.0f; + BoundingBox result(glm::vec2(corner.x + halfX , corner.y), glm::vec2(halfX, size.y)); + return result; +} + bool BoundingBox::contains(const BoundingBox& box) const { return ( (box.corner.x >= corner.x) && diff --git a/libraries/voxels/src/VoxelProjectedPolygon.h b/libraries/voxels/src/VoxelProjectedPolygon.h index 227e1895a0..8123ca6a85 100644 --- a/libraries/voxels/src/VoxelProjectedPolygon.h +++ b/libraries/voxels/src/VoxelProjectedPolygon.h @@ -21,6 +21,11 @@ public: glm::vec2 size; bool contains(const BoundingBox& box) const; float area() const { return size.x * size.y; }; + + BoundingBox topHalf() const; + BoundingBox bottomHalf() const; + BoundingBox leftHalf() const; + BoundingBox rightHalf() const; void printDebugDetails(const char* label=NULL) const; }; diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 06554abba6..e2f66fa52b 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1132,9 +1132,9 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp if (voxelPolygon->getAllInView()) { //node->printDebugDetails("upper section, voxelPolygon->getAllInView() node="); - CoverageMap::StorageResult result = params.map->checkMap(voxelPolygon, false); + CoverageMapStorageResult result = params.map->checkMap(voxelPolygon, false); delete voxelPolygon; // cleanup - if (result == CoverageMap::OCCLUDED) { + if (result == OCCLUDED) { //node->printDebugDetails("upper section, non-Leaf is occluded!! node="); //args->nonLeavesOccluded++; @@ -1247,17 +1247,17 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp // In order to check occlusion culling, the shadow has to be "all in view" otherwise, we will ignore occlusion // culling and proceed as normal if (voxelPolygon->getAllInView()) { - CoverageMap::StorageResult result = params.map->checkMap(voxelPolygon, true); + CoverageMapStorageResult result = params.map->checkMap(voxelPolygon, true); // In all cases where the shadow wasn't stored, we need to free our own memory. // In the case where it is stored, the CoverageMap will free memory for us later. - if (result != CoverageMap::STORED) { + if (result != STORED) { delete voxelPolygon; } // If while attempting to add this voxel's shadow, we determined it was occluded, then // we don't need to process it further and we can exit early. - if (result == CoverageMap::OCCLUDED) { + if (result == OCCLUDED) { childIsOccluded = true; } } else {