From 6755dfd8e4dddb52f851c65ec1f5d73c3ddac0b2 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 19 Jun 2013 11:46:22 -0700 Subject: [PATCH] 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; }