diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index cff2c7ee2e..06e9834804 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -179,7 +179,6 @@ void OctreeQueryNode::resetOctreePacket() { // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use // the clients requested color state. - _currentPacketIsColor = getWantColor(); _currentPacketIsCompressed = getWantCompression(); OCTREE_PACKET_FLAGS flags = 0; if (_currentPacketIsColor) { diff --git a/assignment-client/src/octree/OctreeQueryNode.h b/assignment-client/src/octree/OctreeQueryNode.h index 0c691a06a2..4140d42a5f 100644 --- a/assignment-client/src/octree/OctreeQueryNode.h +++ b/assignment-client/src/octree/OctreeQueryNode.h @@ -14,7 +14,6 @@ #include -#include #include #include #include @@ -55,7 +54,6 @@ public: void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; } OctreeElementBag elementBag; - CoverageMap map; OctreeElementExtraEncodeData extraEncodeData; ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; } @@ -80,7 +78,7 @@ public: bool getCurrentPacketIsColor() const { return _currentPacketIsColor; } bool getCurrentPacketIsCompressed() const { return _currentPacketIsCompressed; } bool getCurrentPacketFormatMatches() { - return (getCurrentPacketIsColor() == getWantColor() && getCurrentPacketIsCompressed() == getWantCompression()); + return (getCurrentPacketIsCompressed() == getWantCompression()); } bool hasLodChanged() const { return _lodChanged; } diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 0a32f574de..efc81d6a21 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -321,7 +321,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus // If we're starting a fresh packet, then... // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use // the clients requested color state. - bool wantColor = nodeData->getWantColor(); bool wantCompression = nodeData->getWantCompression(); // If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color @@ -350,7 +349,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus if (nodeData->moveShouldDump() || nodeData->hasLodChanged()) { nodeData->dumpOutOfView(); } - nodeData->map.erase(); } if (!viewFrustumChanged && !nodeData->getWantDelta()) { @@ -451,18 +449,15 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus } */ - bool wantOcclusionCulling = nodeData->getWantOcclusionCulling(); - CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP; - float octreeSizeScale = nodeData->getOctreeSizeScale(); int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust(); int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving() ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); - EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, + EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, - wantOcclusionCulling, coverageMap, boundaryLevelAdjust, octreeSizeScale, + boundaryLevelAdjust, octreeSizeScale, nodeData->getLastTimeBagEmpty(), isFullScene, &nodeData->stats, _myServer->getJurisdiction(), &nodeData->extraEncodeData); @@ -634,7 +629,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus if (nodeData->elementBag.isEmpty()) { nodeData->updateLastKnownViewFrustum(); nodeData->setViewSent(true); - nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes } } // end if bag wasn't empty, and so we sent stuff... diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e6678c8758..627f48f7cc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3074,9 +3074,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node // These will be the same for all servers, so we can set them up once and then reuse for each server we send to. _octreeQuery.setWantLowResMoving(true); - _octreeQuery.setWantColor(true); _octreeQuery.setWantDelta(true); - _octreeQuery.setWantOcclusionCulling(false); _octreeQuery.setWantCompression(true); _octreeQuery.setCameraPosition(_viewFrustum.getPosition()); diff --git a/libraries/octree/src/CoverageMap.cpp b/libraries/octree/src/CoverageMap.cpp deleted file mode 100644 index 626d4bcf1a..0000000000 --- a/libraries/octree/src/CoverageMap.cpp +++ /dev/null @@ -1,542 +0,0 @@ -// -// CoverageMap.cpp -// libraries/octree/src -// -// Created by Brad Hefta-Gaub on 06/11/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include - -#include - -#include "OctreeLogging.h" -#include "CoverageMap.h" - -int CoverageMap::_mapCount = 0; -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.0f,-1.0f), glm::vec2(2.0f,2.0f)); - -// 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), - _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(); -}; - -CoverageMap::~CoverageMap() { - erase(); -}; - -void CoverageMap::printStats() { - qCDebug(octree, "CoverageMap::printStats()..."); - qCDebug(octree, "MINIMUM_POLYGON_AREA_TO_STORE=%f", (double)MINIMUM_POLYGON_AREA_TO_STORE); - qCDebug(octree, "_mapCount=%d",_mapCount); - qCDebug(octree, "_checkMapRootCalls=%d",_checkMapRootCalls); - qCDebug(octree, "_notAllInView=%d",_notAllInView); - qCDebug(octree, "_maxPolygonsUsed=%d",CoverageRegion::_maxPolygonsUsed); - qCDebug(octree, "_totalPolygons=%d",CoverageRegion::_totalPolygons); - qCDebug(octree, "_occlusionTests=%d",CoverageRegion::_occlusionTests); - qCDebug(octree, "_regionSkips=%d",CoverageRegion::_regionSkips); - qCDebug(octree, "_tooSmallSkips=%d",CoverageRegion::_tooSmallSkips); - qCDebug(octree, "_regionFullSkips=%d",CoverageRegion::_regionFullSkips); - qCDebug(octree, "_outOfOrderPolygon=%d",CoverageRegion::_outOfOrderPolygon); - qCDebug(octree, "_clippedPolygons=%d",CoverageRegion::_clippedPolygons); -} - -void CoverageMap::erase() { - // 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]; - _childMaps[i] = NULL; - } - } - - if (_isRoot && wantDebugging) { - qCDebug(octree, "CoverageMap last to be deleted..."); - printStats(); - - CoverageRegion::_maxPolygonsUsed = 0; - CoverageRegion::_totalPolygons = 0; - CoverageRegion::_occlusionTests = 0; - CoverageRegion::_regionSkips = 0; - CoverageRegion::_tooSmallSkips = 0; - CoverageRegion::_regionFullSkips = 0; - CoverageRegion::_outOfOrderPolygon = 0; - CoverageRegion::_clippedPolygons = 0; - _mapCount = 0; - _checkMapRootCalls = 0; - _notAllInView = 0; - } -} - -void CoverageMap::init() { - memset(_childMaps,0,sizeof(_childMaps)); -} - -// 0 = bottom, right -// 1 = bottom, left -// 2 = top, right -// 3 = top, left -BoundingBox CoverageMap::getChildBoundingBox(int childIndex) { - const int LEFT_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 "left" bit is set, then add size.x to the corner - if ((childIndex & LEFT_BIT) == LEFT_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; -} - -int CoverageMap::getPolygonCount() const { - return (_topHalf.getPolygonCount() + - _bottomHalf.getPolygonCount() + - _leftHalf.getPolygonCount() + - _rightHalf.getPolygonCount() + - _remainder.getPolygonCount()); -} - -OctreeProjectedPolygon* 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(OctreeProjectedPolygon* 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. - if (!polygon->getAllInView()) { - _notAllInView++; - return DOESNT_FIT; - } - - BoundingBox polygonBox(polygon->getBoundingBox()); - if (_isRoot || _myBoundingBox.contains(polygonBox)) { - - CoverageMapStorageResult result = NOT_STORED; - CoverageRegion* storeIn = &_remainder; - - // Check each half of the box independently - 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; - } else if (_bottomHalf.contains(polygonBox)) { - result = _bottomHalf.checkRegion(polygon, polygonBox, storeIt); - storeIn = &_bottomHalf; - } else if (_leftHalf.contains(polygonBox)) { - result = _leftHalf.checkRegion(polygon, polygonBox, storeIt); - storeIn = &_leftHalf; - } else if (_rightHalf.contains(polygonBox)) { - result = _rightHalf.checkRegion(polygon, polygonBox, storeIt); - storeIn = &_rightHalf; - } - } - - // 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) { - - /* - if (result == STORED) - qCDebug(octree, "CoverageMap2::checkMap()... STORED\n"); - else - qCDebug(octree, "CoverageMap2::checkMap()... OCCLUDED\n"); - */ - - 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 - 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: - qCDebug(octree, "checkMap() = STORED\n"); - break; - case NOT_STORED: - qCDebug(octree, "checkMap() = NOT_STORED\n"); - break; - case OCCLUDED: - qCDebug(octree, "checkMap() = OCCLUDED\n"); - break; - default: - qCDebug(octree, "checkMap() = ????? \n"); - break; - } - */ - - 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) { - if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) { - if (storeIn->getPolygonCount() < MAX_POLYGONS_PER_REGION) { - storeIn->storeInArray(polygon); - return STORED; - } else { - CoverageRegion::_regionFullSkips++; - return NOT_STORED; - } - } else { - CoverageRegion::_tooSmallSkips++; - return NOT_STORED; - } - } else { - return NOT_STORED; - } - } - 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; - _polygonSizes = NULL; -} - - -void CoverageRegion::erase() { - -/** - if (_polygonCount) { - qCDebug(octree, "CoverageRegion::erase()...\n"); - qCDebug(octree, "_polygonCount=%d\n",_polygonCount); - _myBoundingBox.printDebugDetails(getRegionName()); - //for (int i = 0; i < _polygonCount; i++) { - // qCDebug(octree, "_polygons[%d]=",i); - // _polygons[i]->getBoundingBox().printDebugDetails(); - //} - } -**/ - // If we're in charge of managing the polygons, then clean them up first - if (_polygons && _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; - } - if (_polygonSizes) { - delete[] _polygonSizes; - _polygonSizes = NULL; - } -} - -void CoverageRegion::growPolygonArray() { - OctreeProjectedPolygon** newPolygons = new OctreeProjectedPolygon*[_polygonArraySize + DEFAULT_GROW_SIZE]; - float* newDistances = new float[_polygonArraySize + DEFAULT_GROW_SIZE]; - float* newSizes = new float[_polygonArraySize + DEFAULT_GROW_SIZE]; - - - if (_polygons) { - memcpy(newPolygons, _polygons, sizeof(OctreeProjectedPolygon*) * _polygonCount); - delete[] _polygons; - memcpy(newDistances, _polygonDistances, sizeof(float) * _polygonCount); - delete[] _polygonDistances; - memcpy(newSizes, _polygonSizes, sizeof(float) * _polygonCount); - delete[] _polygonSizes; - } - _polygons = newPolygons; - _polygonDistances = newDistances; - _polygonSizes = newSizes; - _polygonArraySize = _polygonArraySize + DEFAULT_GROW_SIZE; -} - -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::_regionSkips = 0; -int CoverageRegion::_tooSmallSkips = 0; -int CoverageRegion::_regionFullSkips = 0; -int CoverageRegion::_outOfOrderPolygon = 0; -int CoverageRegion::_clippedPolygons = 0; - - -bool CoverageRegion::mergeItemsInArray(OctreeProjectedPolygon* seed, bool seedInArray) { - for (int i = 0; i < _polygonCount; i++) { - OctreeProjectedPolygon* otherPolygon = _polygons[i]; - if (otherPolygon->canMerge(*seed)) { - otherPolygon->merge(*seed); - - if (seedInArray) { - int* IGNORED_ADDRESS = NULL; - // remove this otherOtherPolygon for our polygon array - _polygonCount = removeFromSortedArrays((void*)seed, - (void**)_polygons, _polygonDistances, IGNORED_ADDRESS, - _polygonCount, _polygonArraySize); - _totalPolygons--; - } - - // 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(OctreeProjectedPolygon* polygon) { - - _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(); - } - - // 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; - const int IGNORED = 0; - int* IGNORED_ADDRESS = 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 - float area = polygon->getBoundingBox().area(); - float reverseArea = 4.0f - area; - _polygonCount = insertIntoSortedArrays((void*)polygon, reverseArea, IGNORED, - (void**)_polygons, _polygonSizes, IGNORED_ADDRESS, - _polygonCount, _polygonArraySize); - } else { - _polygonCount = insertIntoSortedArrays((void*)polygon, polygon->getDistance(), IGNORED, - (void**)_polygons, _polygonDistances, IGNORED_ADDRESS, - _polygonCount, _polygonArraySize); - } - - // Debugging and Optimization Tuning code. - if (_polygonCount > _maxPolygonsUsed) { - _maxPolygonsUsed = _polygonCount; - } -} - - - -CoverageMapStorageResult CoverageRegion::checkRegion(OctreeProjectedPolygon* 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... - - // 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++) { - OctreeProjectedPolygon* 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) { - if (getPolygonCount() < MAX_POLYGONS_PER_REGION) { - storeInArray(polygon); - return STORED; - } else { - CoverageRegion::_regionFullSkips++; - return NOT_STORED; - } - } else { - _tooSmallSkips++; - 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/octree/src/CoverageMap.h b/libraries/octree/src/CoverageMap.h deleted file mode 100644 index bff6bb1078..0000000000 --- a/libraries/octree/src/CoverageMap.h +++ /dev/null @@ -1,120 +0,0 @@ -// -// CoverageMap.h -// libraries/octree/src -// -// Created by Brad Hefta-Gaub on 06/11/13. -// Copyright 2013 High Fidelity, Inc. -// -// 2D CoverageMap Quad tree for storage of OctreeProjectedPolygons -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_CoverageMap_h -#define hifi_CoverageMap_h - -#include -#include "OctreeProjectedPolygon.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(OctreeProjectedPolygon* polygon, const BoundingBox& polygonBox, bool storeIt); - void storeInArray(OctreeProjectedPolygon* 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 _regionSkips; - static int _tooSmallSkips; - static int _regionFullSkips; - static int _outOfOrderPolygon; - static int _clippedPolygons; - - - const char* getRegionName() const; - - int getPolygonCount() const { return _polygonCount; }; - OctreeProjectedPolygon* getPolygon(int index) const { return _polygons[index]; }; - -private: - void init(); - - 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 - int _polygonArraySize; // how much room is there to store polygons at this level - OctreeProjectedPolygon** _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; - - bool mergeItemsInArray(OctreeProjectedPolygon* seed, bool seedInArray); - -}; - -class CoverageMap { - -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; - - CoverageMap(BoundingBox boundingBox = ROOT_BOUNDING_BOX, bool isRoot = IS_ROOT, bool managePolygons = true); - ~CoverageMap(); - - CoverageMapStorageResult checkMap(OctreeProjectedPolygon* polygon, bool storeIt = true); - - BoundingBox getChildBoundingBox(int childIndex); - - void erase(); // erase the coverage map - void printStats(); - - static bool wantDebugging; - - int getPolygonCount() const; - OctreeProjectedPolygon* getPolygon(int index) const; - CoverageMap* getChild(int childIndex) const { return _childMaps[childIndex]; }; - -private: - void init(); - - bool _isRoot; // is this map the root, if so, it never returns DOESNT_FIT - BoundingBox _myBoundingBox; - 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 int _mapCount; - static int _checkMapRootCalls; - static int _notAllInView; -}; - - -#endif // hifi_CoverageMap_h diff --git a/libraries/octree/src/CoverageMapV2.cpp b/libraries/octree/src/CoverageMapV2.cpp deleted file mode 100644 index 8467ea1ee9..0000000000 --- a/libraries/octree/src/CoverageMapV2.cpp +++ /dev/null @@ -1,251 +0,0 @@ -// -// CoverageMapV2.cpp -// libraries/octree/src -// -// Created by Brad Hefta-Gaub on 06/11/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include - -#include - -#include - -#include "OctreeLogging.h" -#include "CoverageMapV2.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.0f,-1.0f), glm::vec2(2.0f,2.0f)); - -// 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(); -}; - -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) { - qCDebug(octree, "CoverageMapV2 last to be deleted..."); - qCDebug(octree, "MINIMUM_POLYGON_AREA_TO_STORE=%f", (double)MINIMUM_POLYGON_AREA_TO_STORE); - qCDebug(octree, "_mapCount=%d",_mapCount); - qCDebug(octree, "_checkMapRootCalls=%d",_checkMapRootCalls); - qCDebug(octree, "_notAllInView=%d",_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 = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT -CoverageMapV2StorageResult CoverageMapV2::checkMap(const OctreeProjectedPolygon* polygon, bool storeIt) { - 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()) { - 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++; - return V2_DOESNT_FIT; - } - - // 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; - - 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) { - return V2_STORED; // otherwise report that we STORED it - } - return V2_NOT_STORED; // unless we weren't asked to store it, then we didn't -} - -void CoverageMapV2::recurseMap(const OctreeProjectedPolygon* polygon, bool storeIt, - bool& seenOccludedMapNodes, bool& allOccludedMapNodesCovered) { - - // 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! - } - - // 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! - } - - // 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 - } - - // 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... -} diff --git a/libraries/octree/src/CoverageMapV2.h b/libraries/octree/src/CoverageMapV2.h deleted file mode 100644 index fc9a3ea70e..0000000000 --- a/libraries/octree/src/CoverageMapV2.h +++ /dev/null @@ -1,72 +0,0 @@ -// -// CoverageMapV2.h -// libraries/octree/src -// -// Created by Brad Hefta-Gaub on 06/11/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_CoverageMapV2_h -#define hifi_CoverageMapV2_h - -#include - -#include "OctreeProjectedPolygon.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 OctreeProjectedPolygon* 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: - void recurseMap(const OctreeProjectedPolygon* polygon, bool storeIt, - bool& seenOccludedMapNodes, bool& allOccludedMapNodesCovered); - - 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 // hifi_CoverageMapV2_h diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index c02a034778..aacb57f31d 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -41,7 +41,6 @@ #include #include -#include "CoverageMap.h" #include "OctreeConstants.h" #include "OctreeElementBag.h" #include "Octree.h" @@ -951,9 +950,9 @@ int Octree::encodeTreeBitstream(OctreeElementPointer element, // if childBytesWritten == 1 then something went wrong... that's not possible assert(childBytesWritten != 1); - // if includeColor and childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some + // if childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some // reason couldn't be written... so reset them here... This isn't true for the non-color included case - if (suppressEmptySubtrees() && params.includeColor && childBytesWritten == 2) { + if (suppressEmptySubtrees() && childBytesWritten == 2) { childBytesWritten = 0; //params.stopReason = EncodeBitstreamParams::UNKNOWN; // possibly should be DIDNT_FIT... } @@ -1103,31 +1102,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, params.stopReason = EncodeBitstreamParams::NO_CHANGE; return bytesAtThisLevel; } - - // If the user also asked for occlusion culling, check if this element is occluded, but only if it's not a leaf. - // leaf occlusion is handled down below when we check child nodes - if (params.wantOcclusionCulling && !element->isLeaf()) { - OctreeProjectedPolygon* voxelPolygon = - new OctreeProjectedPolygon(params.viewFrustum->getProjectedPolygon(element->getAACube())); - - // 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()) { - CoverageMapStorageResult result = params.map->checkMap(voxelPolygon, false); - delete voxelPolygon; // cleanup - if (result == OCCLUDED) { - if (params.stats) { - params.stats->skippedOccluded(element); - } - params.stopReason = EncodeBitstreamParams::OCCLUDED; - return bytesAtThisLevel; - } - } else { - // 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 voxelPolygon; - } - } } bool keepDiggingDeeper = true; // Assuming we're in view we have a great work ethic, we're always ready for more! @@ -1190,20 +1164,10 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, } } - if (params.wantOcclusionCulling) { - if (childElement) { - float distance = params.viewFrustum ? childElement->distanceToCamera(*params.viewFrustum) : 0; - - currentCount = insertOctreeElementIntoSortedArrays(childElement, distance, i, - sortedChildren, (float*)&distancesToChildren, - (int*)&indexOfChildren, currentCount, NUMBER_OF_CHILDREN); - } - } else { - sortedChildren[i] = childElement; - indexOfChildren[i] = i; - distancesToChildren[i] = 0.0f; - currentCount++; - } + sortedChildren[i] = childElement; + indexOfChildren[i] = i; + distancesToChildren[i] = 0.0f; + currentCount++; // track stats // must check childElement here, because it could be we got here with no childElement @@ -1255,36 +1219,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, bool childIsOccluded = false; // assume it's not occluded - // If the user also asked for occlusion culling, check if this element is occluded - if (params.wantOcclusionCulling && childElement->isLeaf()) { - // Don't check occlusion here, just add them to our distance ordered array... - - // FIXME params.ViewFrustum is used here, but later it is checked against nullptr. - OctreeProjectedPolygon* voxelPolygon = new OctreeProjectedPolygon( - params.viewFrustum->getProjectedPolygon(childElement->getAACube())); - - // In order to check occlusion culling, the shadow has to be "all in view" otherwise, we ignore occlusion - // culling and proceed as normal - if (voxelPolygon->getAllInView()) { - 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 != 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 == OCCLUDED) { - childIsOccluded = true; - } - } else { - delete voxelPolygon; - } - } // wants occlusion culling & isLeaf() - - bool shouldRender = !params.viewFrustum ? true : childElement->calculateShouldRender(params.viewFrustum, @@ -1359,7 +1293,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, // NOW might be a good time to give our tree subclass and this element a chance to set up and check any extra encode data element->initializeExtraEncodeData(params); - // write the child element data... NOTE: includeColor means include element data + // write the child element data... // NOTE: the format of the bitstream is generally this: // [octalcode] // [bitmask for existence of child data] @@ -1369,65 +1303,63 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, // N x [ ... tree for children ...] // // This section of the code, is writing the "N x [child data]" portion of this bitstream - if (params.includeColor) { - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - if (oneAtBit(childrenDataBits, i)) { - OctreeElementPointer childElement = element->getChildAtIndex(i); + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + if (oneAtBit(childrenDataBits, i)) { + OctreeElementPointer childElement = element->getChildAtIndex(i); - // the childrenDataBits were set up by the in view/LOD logic, it may contain children that we've already - // processed and sent the data bits for. Let our tree subclass determine if it really wants to send the - // data for this child at this point - if (childElement && element->shouldIncludeChildData(i, params)) { + // the childrenDataBits were set up by the in view/LOD logic, it may contain children that we've already + // processed and sent the data bits for. Let our tree subclass determine if it really wants to send the + // data for this child at this point + if (childElement && element->shouldIncludeChildData(i, params)) { - int bytesBeforeChild = packetData->getUncompressedSize(); + int bytesBeforeChild = packetData->getUncompressedSize(); - // a childElement may "partially" write it's data. for example, the model server where the entire - // contents of the element may be larger than can fit in a single MTU/packetData. In this case, - // we want to allow the appendElementData() to respond that it produced partial data, which should be - // written, but that the childElement needs to be reprocessed in an additional pass or passes - // to be completed. - LevelDetails childDataLevelKey = packetData->startLevel(); + // a childElement may "partially" write it's data. for example, the model server where the entire + // contents of the element may be larger than can fit in a single MTU/packetData. In this case, + // we want to allow the appendElementData() to respond that it produced partial data, which should be + // written, but that the childElement needs to be reprocessed in an additional pass or passes + // to be completed. + LevelDetails childDataLevelKey = packetData->startLevel(); - OctreeElement::AppendState childAppendState = childElement->appendElementData(packetData, params); + OctreeElement::AppendState childAppendState = childElement->appendElementData(packetData, params); - // allow our tree subclass to do any additional bookkeeping it needs to do with encoded data state - element->updateEncodedData(i, childAppendState, params); + // allow our tree subclass to do any additional bookkeeping it needs to do with encoded data state + element->updateEncodedData(i, childAppendState, params); - // Continue this level so long as some part of this child element was appended. - bool childFit = (childAppendState != OctreeElement::NONE); + // Continue this level so long as some part of this child element was appended. + bool childFit = (childAppendState != OctreeElement::NONE); - // some datatypes (like Voxels) assume that all child data will fit, if it doesn't fit - // the data type wants to bail on this element level completely - if (!childFit && mustIncludeAllChildData()) { - continueThisLevel = false; - break; - } + // some datatypes (like Voxels) assume that all child data will fit, if it doesn't fit + // the data type wants to bail on this element level completely + if (!childFit && mustIncludeAllChildData()) { + continueThisLevel = false; + break; + } - // If the child was partially or fully appended, then mark the actualChildrenDataBits as including - // this child data - if (childFit) { - actualChildrenDataBits += (1 << (7 - i)); - continueThisLevel = packetData->endLevel(childDataLevelKey); - } else { - packetData->discardLevel(childDataLevelKey); - elementAppendState = OctreeElement::PARTIAL; - params.stopReason = EncodeBitstreamParams::DIDNT_FIT; - } + // If the child was partially or fully appended, then mark the actualChildrenDataBits as including + // this child data + if (childFit) { + actualChildrenDataBits += (1 << (7 - i)); + continueThisLevel = packetData->endLevel(childDataLevelKey); + } else { + packetData->discardLevel(childDataLevelKey); + elementAppendState = OctreeElement::PARTIAL; + params.stopReason = EncodeBitstreamParams::DIDNT_FIT; + } - // If this child was partially appended, then consider this element to be partially appended - if (childAppendState == OctreeElement::PARTIAL) { - elementAppendState = OctreeElement::PARTIAL; - params.stopReason = EncodeBitstreamParams::DIDNT_FIT; - } + // If this child was partially appended, then consider this element to be partially appended + if (childAppendState == OctreeElement::PARTIAL) { + elementAppendState = OctreeElement::PARTIAL; + params.stopReason = EncodeBitstreamParams::DIDNT_FIT; + } - int bytesAfterChild = packetData->getUncompressedSize(); + int bytesAfterChild = packetData->getUncompressedSize(); - bytesAtThisLevel += (bytesAfterChild - bytesBeforeChild); // keep track of byte count for this child + bytesAtThisLevel += (bytesAfterChild - bytesBeforeChild); // keep track of byte count for this child - // don't need to check childElement here, because we can't get here with no childElement - if (params.stats && (childAppendState != OctreeElement::NONE)) { - params.stats->colorSent(childElement); - } + // don't need to check childElement here, because we can't get here with no childElement + if (params.stats && (childAppendState != OctreeElement::NONE)) { + params.stats->colorSent(childElement); } } } @@ -1510,10 +1442,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, // final packet in standard order. So what we're going to do is keep track of how big each subtree was in bytes, // and then later reshuffle these sections of our output buffer back into normal order. This allows us to make // a single recursive pass in distance sorted order, but retain standard order in our encoded packet - int recursiveSliceSizes[NUMBER_OF_CHILDREN]; - const unsigned char* recursiveSliceStarts[NUMBER_OF_CHILDREN]; - int firstRecursiveSliceOffset = packetData->getUncompressedByteOffset(); - int allSlicesSize = 0; // for each child element in Distance sorted order..., check to see if they exist, are colored, and in view, and if so // add them to our distance ordered array of children @@ -1524,8 +1452,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, if (oneAtBit(childrenExistInPacketBits, originalIndex)) { int thisLevel = currentEncodeLevel; - // remember this for reshuffling - recursiveSliceStarts[originalIndex] = packetData->getUncompressedData() + packetData->getUncompressedSize(); int childTreeBytesOut = 0; @@ -1546,10 +1472,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, } } - // remember this for reshuffling - recursiveSliceSizes[originalIndex] = childTreeBytesOut; - allSlicesSize += childTreeBytesOut; - // if the child wrote 0 bytes, it means that nothing below exists or was in view, or we ran out of space, // basically, the children below don't contain any info. @@ -1566,17 +1488,10 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, // so, if the child returns 2 bytes out, we can actually consider that an empty tree also!! // // we can make this act like no bytes out, by just resetting the bytes out in this case - if (suppressEmptySubtrees() && params.includeColor && !params.includeExistsBits && childTreeBytesOut == 2) { + if (suppressEmptySubtrees() && !params.includeExistsBits && childTreeBytesOut == 2) { childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees } - // We used to try to collapse trees that didn't contain any data, but this does appear to create a problem - // in detecting element deletion. So, I've commented this out but left it in here as a warning to anyone else - // about not attempting to add this optimization back in, without solving the element deletion case. - // We need to send these bitMasks in case the exists in tree bitmask is indicating the deletion of a tree - //if (params.includeColor && params.includeExistsBits && childTreeBytesOut == 3) { - // childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees - //} bytesAtThisLevel += childTreeBytesOut; @@ -1596,7 +1511,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, // If this is the last of the child exists bits, then we're actually be rolling out the entire tree if (params.stats && childrenExistInPacketBits == 0) { - params.stats->childBitsRemoved(params.includeExistsBits, params.includeColor); + params.stats->childBitsRemoved(params.includeExistsBits); } if (!continueThisLevel) { @@ -1613,33 +1528,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, } // end if (childTreeBytesOut == 0) } // end if (oneAtBit(childrenExistInPacketBits, originalIndex)) } // end for - - // reshuffle here... - if (continueThisLevel && params.wantOcclusionCulling) { - unsigned char tempReshuffleBuffer[MAX_OCTREE_UNCOMRESSED_PACKET_SIZE]; - - unsigned char* tempBufferTo = &tempReshuffleBuffer[0]; // this is our temporary destination - - // iterate through our childrenExistInPacketBits, these will be the sections of the packet that we copied subTree - // details into. Unfortunately, they're in distance sorted order, not original index order. we need to put them - // back into original distance order - for (int originalIndex = 0; originalIndex < NUMBER_OF_CHILDREN; originalIndex++) { - if (oneAtBit(childrenExistInPacketBits, originalIndex)) { - int thisSliceSize = recursiveSliceSizes[originalIndex]; - const unsigned char* thisSliceStarts = recursiveSliceStarts[originalIndex]; - - memcpy(tempBufferTo, thisSliceStarts, thisSliceSize); - tempBufferTo += thisSliceSize; - } - } - - // now that all slices are back in the correct order, copy them to the correct output buffer - continueThisLevel = packetData->updatePriorBytes(firstRecursiveSliceOffset, &tempReshuffleBuffer[0], allSlicesSize); - if (!continueThisLevel) { - qCDebug(octree) << "WARNING UNEXPECTED CASE: Failed to update recursive slice!!!"; - qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE...."; - } - } } // end keepDiggingDeeper // If we made it this far, then we've written all of our child data... if this element is the root @@ -1918,7 +1806,7 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr unsigned char* dataAt = entireFileDataSection; - ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, + ReadBitstreamToTreeParams args(NO_EXISTS_BITS, NULL, 0, SharedNodePointer(), wantImportProgress, gotVersion); readBitstreamToTree(dataAt, dataLength, args); @@ -1957,7 +1845,7 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr unsigned char* dataAt = fileChunk; unsigned long dataLength = chunkLength; - ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, + ReadBitstreamToTreeParams args(NO_EXISTS_BITS, NULL, 0, SharedNodePointer(), wantImportProgress, gotVersion); readBitstreamToTree(dataAt, dataLength, args); @@ -2105,7 +1993,7 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElementPointer element) bool lastPacketWritten = false; while (OctreeElementPointer subTree = elementBag.extract()) { - EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS); + EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, NO_EXISTS_BITS); withReadLock([&] { params.extraEncodeData = &extraEncodeData; bytesWritten = encodeTreeBitstream(subTree, &packetData, elementBag, params); diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 514a9b391b..3ca8528a2f 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -28,7 +28,6 @@ #include "OctreePacketData.h" #include "OctreeSceneStats.h" -class CoverageMap; class ReadBitstreamToTreeParams; class Octree; class OctreeElement; @@ -53,12 +52,8 @@ typedef QHash CubeList; const bool NO_EXISTS_BITS = false; const bool WANT_EXISTS_BITS = true; -const bool NO_COLOR = false; -const bool WANT_COLOR = true; const bool COLLAPSE_EMPTY_TREE = true; const bool DONT_COLLAPSE = false; -const bool NO_OCCLUSION_CULLING = false; -const bool WANT_OCCLUSION_CULLING = true; const int DONT_CHOP = 0; const int NO_BOUNDARY_ADJUST = 0; @@ -75,18 +70,15 @@ public: int maxEncodeLevel; int maxLevelReached; const ViewFrustum* viewFrustum; - bool includeColor; bool includeExistsBits; int chopLevels; bool deltaViewFrustum; const ViewFrustum* lastViewFrustum; - bool wantOcclusionCulling; int boundaryLevelAdjust; float octreeElementSizeScale; quint64 lastViewFrustumSent; bool forceSendScene; OctreeSceneStats* stats; - CoverageMap* map; JurisdictionMap* jurisdictionMap; OctreeElementExtraEncodeData* extraEncodeData; @@ -108,13 +100,10 @@ public: EncodeBitstreamParams( int maxEncodeLevel = INT_MAX, const ViewFrustum* viewFrustum = IGNORE_VIEW_FRUSTUM, - bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS, int chopLevels = 0, bool deltaViewFrustum = false, const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM, - bool wantOcclusionCulling = NO_OCCLUSION_CULLING, - CoverageMap* map = IGNORE_COVERAGE_MAP, int boundaryLevelAdjust = NO_BOUNDARY_ADJUST, float octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE, quint64 lastViewFrustumSent = IGNORE_LAST_SENT, @@ -125,18 +114,15 @@ public: maxEncodeLevel(maxEncodeLevel), maxLevelReached(0), viewFrustum(viewFrustum), - includeColor(includeColor), includeExistsBits(includeExistsBits), chopLevels(chopLevels), deltaViewFrustum(deltaViewFrustum), lastViewFrustum(lastViewFrustum), - wantOcclusionCulling(wantOcclusionCulling), boundaryLevelAdjust(boundaryLevelAdjust), octreeElementSizeScale(octreeElementSizeScale), lastViewFrustumSent(lastViewFrustumSent), forceSendScene(forceSendScene), stats(stats), - map(map), jurisdictionMap(jurisdictionMap), extraEncodeData(extraEncodeData), stopReason(UNKNOWN) @@ -190,7 +176,6 @@ public: class ReadBitstreamToTreeParams { public: - bool includeColor; bool includeExistsBits; OctreeElementPointer destinationElement; QUuid sourceUUID; @@ -201,14 +186,12 @@ public: int entitiesPerPacket = 0; ReadBitstreamToTreeParams( - bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS, OctreeElementPointer destinationElement = NULL, QUuid sourceUUID = QUuid(), SharedNodePointer sourceNode = SharedNodePointer(), bool wantImportProgress = false, PacketVersion bitstreamVersion = 0) : - includeColor(includeColor), includeExistsBits(includeExistsBits), destinationElement(destinationElement), sourceUUID(sourceUUID), @@ -315,7 +298,7 @@ public: Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL); // Note: this assumes the fileFormat is the HIO individual voxels code files - void loadOctreeFile(const char* fileName, bool wantColorRandomizer); + void loadOctreeFile(const char* fileName); // Octree exporters void writeToFile(const char* filename, OctreeElementPointer element = NULL, QString persistAsFileType = "svo"); diff --git a/libraries/octree/src/OctreeHeadlessViewer.cpp b/libraries/octree/src/OctreeHeadlessViewer.cpp index 88a77a4c53..f1c2172d86 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.cpp +++ b/libraries/octree/src/OctreeHeadlessViewer.cpp @@ -51,9 +51,7 @@ void OctreeHeadlessViewer::queryOctree() { // These will be the same for all servers, so we can set them up once and then reuse for each server we send to. _octreeQuery.setWantLowResMoving(true); - _octreeQuery.setWantColor(true); _octreeQuery.setWantDelta(true); - _octreeQuery.setWantOcclusionCulling(false); _octreeQuery.setWantCompression(true); // TODO: should be on by default _octreeQuery.setCameraPosition(_viewFrustum.getPosition()); diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index e8beb0404c..c431d66bf2 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -41,9 +41,7 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) { // bitMask of less than byte wide items unsigned char bitItems = 0; if (_wantLowResMoving) { setAtBit(bitItems, WANT_LOW_RES_MOVING_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); } if (_wantCompression) { setAtBit(bitItems, WANT_COMPRESSION); } *destinationBuffer++ = bitItems; @@ -84,9 +82,7 @@ int OctreeQuery::parseData(NLPacket& packet) { unsigned char bitItems = 0; bitItems = (unsigned char)*sourceBuffer++; _wantLowResMoving = oneAtBit(bitItems, WANT_LOW_RES_MOVING_BIT); - _wantColor = oneAtBit(bitItems, WANT_COLOR_AT_BIT); _wantDelta = oneAtBit(bitItems, WANT_DELTA_AT_BIT); - _wantOcclusionCulling = oneAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT); _wantCompression = oneAtBit(bitItems, WANT_COMPRESSION); // desired Max Octree PPS diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h index 86474ffc02..71c9361e68 100644 --- a/libraries/octree/src/OctreeQuery.h +++ b/libraries/octree/src/OctreeQuery.h @@ -35,9 +35,9 @@ typedef unsigned long long quint64; // First bitset const int WANT_LOW_RES_MOVING_BIT = 0; -const int WANT_COLOR_AT_BIT = 1; +const int UNUSED_BIT_1 = 1; // unused... available for new feature const int WANT_DELTA_AT_BIT = 2; -const int WANT_OCCLUSION_CULLING_BIT = 3; +const int UNUSED_BIT_3 = 3; // unused... available for new feature const int WANT_COMPRESSION = 4; // 5th bit class OctreeQuery : public NodeData { @@ -71,10 +71,8 @@ public: void setCameraEyeOffsetPosition(const glm::vec3& eyeOffsetPosition) { _cameraEyeOffsetPosition = eyeOffsetPosition; } // related to Octree Sending strategies - bool getWantColor() const { return _wantColor; } bool getWantDelta() const { return _wantDelta; } bool getWantLowResMoving() const { return _wantLowResMoving; } - bool getWantOcclusionCulling() const { return _wantOcclusionCulling; } bool getWantCompression() const { return _wantCompression; } int getMaxQueryPacketsPerSecond() const { return _maxQueryPPS; } float getOctreeSizeScale() const { return _octreeElementSizeScale; } @@ -82,9 +80,7 @@ public: public slots: void setWantLowResMoving(bool wantLowResMoving) { _wantLowResMoving = wantLowResMoving; } - void setWantColor(bool wantColor) { _wantColor = wantColor; } void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; } - void setWantOcclusionCulling(bool wantOcclusionCulling) { _wantOcclusionCulling = wantOcclusionCulling; } void setWantCompression(bool wantCompression) { _wantCompression = wantCompression; } void setMaxQueryPacketsPerSecond(int maxQueryPPS) { _maxQueryPPS = maxQueryPPS; } void setOctreeSizeScale(float octreeSizeScale) { _octreeElementSizeScale = octreeSizeScale; } @@ -101,10 +97,8 @@ protected: glm::vec3 _cameraEyeOffsetPosition = glm::vec3(0.0f); // octree server sending items - bool _wantColor = true; bool _wantDelta = true; bool _wantLowResMoving = true; - bool _wantOcclusionCulling = false; bool _wantCompression = false; int _maxQueryPPS = DEFAULT_MAX_OCTREE_PPS; float _octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE; /// used for LOD calculations diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index b7be4cf3e7..c65359f12f 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -115,7 +115,7 @@ void OctreeRenderer::processDatagram(NLPacket& packet, SharedNodePointer sourceN if (sectionLength) { // ask the VoxelTree to read the bitstream into the tree - ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL, + ReadBitstreamToTreeParams args(WANT_EXISTS_BITS, NULL, sourceUUID, sourceNode, false, packet.getVersion()); quint64 startUncompress, startLock = usecTimestampNow(); quint64 startReadBitsteam, endReadBitsteam; diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp index 22352fbe3b..c70e0e4935 100644 --- a/libraries/octree/src/OctreeSceneStats.cpp +++ b/libraries/octree/src/OctreeSceneStats.cpp @@ -371,14 +371,12 @@ void OctreeSceneStats::existsInPacketBitsWritten() { _existsInPacketBitsWritten++; } -void OctreeSceneStats::childBitsRemoved(bool includesExistsBits, bool includesColors) { +void OctreeSceneStats::childBitsRemoved(bool includesExistsBits) { _existsInPacketBitsWritten--; if (includesExistsBits) { _existsBitsWritten--; } - if (includesColors) { - _colorBitsWritten--; - } + _colorBitsWritten--; _treesRemoved++; } diff --git a/libraries/octree/src/OctreeSceneStats.h b/libraries/octree/src/OctreeSceneStats.h index bdb4ef206a..f8ecf93106 100644 --- a/libraries/octree/src/OctreeSceneStats.h +++ b/libraries/octree/src/OctreeSceneStats.h @@ -89,7 +89,7 @@ public: void existsInPacketBitsWritten(); /// Fix up tracking statistics in case where bitmasks were removed for some reason - void childBitsRemoved(bool includesExistsBits, bool includesColors); + void childBitsRemoved(bool includesExistsBits); /// Pack the details of the statistics into a buffer for sending as a network packet int packIntoPacket(); diff --git a/libraries/shared/src/OctalCode.cpp b/libraries/shared/src/OctalCode.cpp index 0b77683d4c..1fa18903cb 100644 --- a/libraries/shared/src/OctalCode.cpp +++ b/libraries/shared/src/OctalCode.cpp @@ -264,29 +264,6 @@ unsigned char* chopOctalCode(const unsigned char* originalOctalCode, int chopLev return newCode; } -unsigned char* rebaseOctalCode(const unsigned char* originalOctalCode, const unsigned char* newParentOctalCode, - bool includeColorSpace) { - - int oldCodeLength = numberOfThreeBitSectionsInCode(originalOctalCode); - int newParentCodeLength = numberOfThreeBitSectionsInCode(newParentOctalCode); - int newCodeLength = newParentCodeLength + oldCodeLength; - int bufferLength = newCodeLength + (includeColorSpace ? SIZE_OF_COLOR_DATA : 0); - unsigned char* newCode = new unsigned char[bufferLength]; - *newCode = newCodeLength; // set the length byte - - // copy parent code section first - for (int sectionFromParent = 0; sectionFromParent < newParentCodeLength; sectionFromParent++) { - char sectionValue = getOctalCodeSectionValue(newParentOctalCode, sectionFromParent); - setOctalCodeSectionValue(newCode, sectionFromParent, sectionValue); - } - // copy original code section next - for (int sectionFromOriginal = 0; sectionFromOriginal < oldCodeLength; sectionFromOriginal++) { - char sectionValue = getOctalCodeSectionValue(originalOctalCode, sectionFromOriginal); - setOctalCodeSectionValue(newCode, sectionFromOriginal + newParentCodeLength, sectionValue); - } - return newCode; -} - bool isAncestorOf(const unsigned char* possibleAncestor, const unsigned char* possibleDescendent, int descendentsChild) { if (!possibleAncestor || !possibleDescendent) { return false; diff --git a/libraries/shared/src/OctalCode.h b/libraries/shared/src/OctalCode.h index 9229157c3d..09766b685a 100644 --- a/libraries/shared/src/OctalCode.h +++ b/libraries/shared/src/OctalCode.h @@ -36,8 +36,6 @@ const int UNKNOWN_OCTCODE_LENGTH = -2; int numberOfThreeBitSectionsInCode(const unsigned char* octalCode, int maxBytes = UNKNOWN_OCTCODE_LENGTH); unsigned char* chopOctalCode(const unsigned char* originalOctalCode, int chopLevels); -unsigned char* rebaseOctalCode(const unsigned char* originalOctalCode, const unsigned char* newParentOctalCode, - bool includeColorSpace = false); const int CHECK_NODE_ONLY = -1; bool isAncestorOf(const unsigned char* possibleAncestor, const unsigned char* possibleDescendent,