diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index b36c12440e..bfe5090b51 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -376,6 +376,13 @@ Menu::Menu() : QMenu* renderDebugMenu = developerMenu->addMenu("Render Debugging Tools"); addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings, Qt::CTRL | Qt::SHIFT | Qt::Key_P); addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::SuppressShortTimings, Qt::CTRL | Qt::SHIFT | Qt::Key_S); + + + addActionToQMenuAndActionHash(renderDebugMenu, + MenuOption::ShowAllLocalVoxels, + Qt::CTRL | Qt::Key_A, + appInstance->getVoxels(), + SLOT(showAllLocalVoxels())); addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::KillLocalVoxels, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index cb652f4fe7..2a83e388a1 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -198,6 +198,7 @@ namespace MenuOption { const QString SendVoxelColors = "Colored Voxels"; const QString SettingsImport = "Import Settings"; const QString SettingsExport = "Export Settings"; + const QString ShowAllLocalVoxels = "Show All Local Voxels"; const QString ShowTrueColors = "Show TRUE Colors"; const QString SimulateLeapHand = "Simulate Leap Hand"; const QString SkeletonTracking = "Skeleton Tracking"; diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 4a5f616ee6..c5b28740b2 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -107,6 +107,8 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) _inSetupNewVoxelsForDrawing = false; _useFastVoxelPipeline = false; + + _culledOnce = false; } void VoxelSystem::voxelDeleted(VoxelNode* node) { @@ -229,16 +231,21 @@ void VoxelSystem::freeBufferIndex(glBufferIndex index) { // This will run through the list of _freeIndexes and reset their VBO array values to be "invisible". void VoxelSystem::clearFreeBufferIndexes() { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "###### clearFreeBufferIndexes()"); + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "clearFreeBufferIndexes()"); _voxelsInWriteArrays = 0; // reset our VBO _abandonedVBOSlots = 0; // clear out freeIndexes - pthread_mutex_lock(&_freeIndexLock); - _freeIndexes.clear(); + { + PerformanceWarning warn(showWarnings,"clearFreeBufferIndexes() : pthread_mutex_lock(&_freeIndexLock)"); + pthread_mutex_lock(&_freeIndexLock); + } + { + PerformanceWarning warn(showWarnings,"clearFreeBufferIndexes() : _freeIndexes.clear()"); + _freeIndexes.clear(); + } pthread_mutex_unlock(&_freeIndexLock); - - clearAllNodesBufferIndex(); } VoxelSystem::~VoxelSystem() { @@ -745,7 +752,6 @@ void VoxelSystem::checkForCulling() { && !isViewChanging() ) ) { - _lastViewCulling = start; // When we call removeOutOfView() voxels, we don't actually remove the voxels from the VBOs, but we do remove @@ -950,6 +956,12 @@ int VoxelSystem::forceRemoveNodeFromArrays(VoxelNode* node) { int VoxelSystem::updateNodeInArrays(VoxelNode* node, bool reuseIndex, bool forceDraw) { // If we've run out of room, then just bail... if (_voxelsInWriteArrays >= _maxVoxels) { + // We need to think about what else we can do in this case. This basically means that all of our available + // VBO slots are used up, but we're trying to render more voxels. At this point, if this happens we'll just + // not render these Voxels. We need to think about ways to keep the entire scene intact but maybe lower quality + // possibly shifting down to lower LOD or something. This debug message is to help identify, if/when/how this + // state actually occurs. + qDebug("OHHHH NOOOOOO!!!! updateNodeInArrays() BAILING (_voxelsInWriteArrays >= _maxVoxels)\n"); return 0; } @@ -1062,10 +1074,24 @@ void VoxelSystem::changeTree(VoxelTree* newTree) { } void VoxelSystem::updateFullVBOs() { - updateVBOSegment(0, _voxelsInReadArrays); + bool outputWarning = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(outputWarning, "updateFullVBOs()"); + + { + static char buffer[128] = { 0 }; + if (outputWarning) { + sprintf(buffer, "updateFullVBOs() : updateVBOSegment(0, _voxelsInReadArrays=%lu);", _voxelsInReadArrays); + }; + + PerformanceWarning warn(outputWarning,buffer); + updateVBOSegment(0, _voxelsInReadArrays); + } - // consider the _readVoxelDirtyArray[] clean! - memset(_readVoxelDirtyArray, false, _voxelsInReadArrays * sizeof(bool)); + { + PerformanceWarning warn(outputWarning,"updateFullVBOs() : memset(_readVoxelDirtyArray...)"); + // consider the _readVoxelDirtyArray[] clean! + memset(_readVoxelDirtyArray, false, _voxelsInReadArrays * sizeof(bool)); + } } void VoxelSystem::updatePartialVBOs() { @@ -1117,6 +1143,9 @@ void VoxelSystem::updateVBOs() { } void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) { + bool showWarning = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarning, "updateVBOSegment()"); + if (_useVoxelShader) { int segmentLength = (segmentEnd - segmentStart) + 1; GLintptr segmentStartAt = segmentStart * sizeof(VoxelShaderVBOData); @@ -1131,18 +1160,36 @@ void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex seg GLintptr segmentStartAt = segmentStart * vertexPointsPerVoxel * sizeof(GLfloat); GLsizeiptr segmentSizeBytes = segmentLength * vertexPointsPerVoxel * sizeof(GLfloat); GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * vertexPointsPerVoxel); - glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); - glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom); + + { + PerformanceWarning warn(showWarning, "updateVBOSegment() : glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);"); + glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); + } + + { + PerformanceWarning warn(showWarning, "updateVBOSegment() : glBufferSubData() _vboVerticesID);"); + glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom); + } + segmentStartAt = segmentStart * vertexPointsPerVoxel * sizeof(GLubyte); segmentSizeBytes = segmentLength * vertexPointsPerVoxel * sizeof(GLubyte); GLubyte* readColorsFrom = _readColorsArray + (segmentStart * vertexPointsPerVoxel); - glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); - glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); + + { + PerformanceWarning warn(showWarning, "updateVBOSegment() : glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);"); + glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); + } + + { + PerformanceWarning warn(showWarning, "updateVBOSegment() : glBufferSubData() _vboColorsID);"); + glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); + } } } void VoxelSystem::render(bool texture) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "render()"); + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "render()"); // If we got here and we're not initialized then bail! if (!_initialized) { @@ -1154,8 +1201,7 @@ void VoxelSystem::render(bool texture) { bool dontCallOpenGLDraw = Menu::getInstance()->isOptionChecked(MenuOption::DontCallOpenGLForVoxels); // if not don't... then do... if (_useVoxelShader) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "render().. _useVoxelShader openGL.."); + PerformanceWarning warn(showWarnings,"render().. _useVoxelShader openGL.."); //Define this somewhere in your header file @@ -1201,68 +1247,78 @@ void VoxelSystem::render(bool texture) { glDisableVertexAttribArray(attributeLocation); } } else { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "render().. openGL..."); + PerformanceWarning warn(showWarnings, "render().. TRIANGLES..."); - // tell OpenGL where to find vertex and color information - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); + { + PerformanceWarning warn(showWarnings,"render().. setup before glDrawRangeElementsEXT()..."); + + // tell OpenGL where to find vertex and color information + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); - glVertexPointer(3, GL_FLOAT, 0, 0); + glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); + glVertexPointer(3, GL_FLOAT, 0, 0); - glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); - glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0); + glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); + glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0); - applyScaleAndBindProgram(texture); + applyScaleAndBindProgram(texture); - // for performance, enable backface culling - glEnable(GL_CULL_FACE); + // for performance, enable backface culling + glEnable(GL_CULL_FACE); + } // draw voxels in 6 passes if (!dontCallOpenGLDraw) { + PerformanceWarning warn(showWarnings, "render().. glDrawRangeElementsEXT()..."); + glNormal3f(0,1.0f,0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesTop); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1, + glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); glNormal3f(0,-1.0f,0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesBottom); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1, + glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); glNormal3f(-1.0f,0,0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesLeft); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1, + glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); glNormal3f(1.0f,0,0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesRight); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1, + glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); glNormal3f(0,0,-1.0f); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesFront); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1, + glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); glNormal3f(0,0,1.0f); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesBack); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1, + glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); } - glDisable(GL_CULL_FACE); + { + PerformanceWarning warn(showWarnings, "render().. cleanup after glDrawRangeElementsEXT()..."); + + glDisable(GL_CULL_FACE); - removeScaleAndReleaseProgram(texture); + removeScaleAndReleaseProgram(texture); - // deactivate vertex and color arrays after drawing - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); + // deactivate vertex and color arrays after drawing + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); - // bind with 0 to switch back to normal operation - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + // bind with 0 to switch back to normal operation + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } } } @@ -1527,11 +1583,11 @@ public: VoxelSystem* thisVoxelSystem; ViewFrustum thisViewFrustum; VoxelNodeBag dontRecurseBag; - unsigned long nodesScanned; - unsigned long nodesRemoved; - unsigned long nodesInside; - unsigned long nodesIntersect; - unsigned long nodesOutside; + unsigned long nodesScanned; + unsigned long nodesRemoved; + unsigned long nodesInside; + unsigned long nodesIntersect; + unsigned long nodesOutside; VoxelNode* insideRoot; VoxelNode* outsideRoot; @@ -1612,10 +1668,10 @@ bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) { bool VoxelSystem::isViewChanging() { bool result = false; // assume the best - // If our viewFrustum has changed since our _lastKnowViewFrustum - if (!_lastKnowViewFrustum.matches(_viewFrustum)) { + // If our viewFrustum has changed since our _lastKnownViewFrustum + if (!_lastKnownViewFrustum.matches(_viewFrustum)) { result = true; - _lastKnowViewFrustum = *_viewFrustum; // save last known + _lastKnownViewFrustum = *_viewFrustum; // save last known } return result; } @@ -1628,7 +1684,7 @@ bool VoxelSystem::hasViewChanged() { return false; } - // If our viewFrustum has changed since our _lastKnowViewFrustum + // If our viewFrustum has changed since our _lastKnownViewFrustum if (!_lastStableViewFrustum.matches(_viewFrustum)) { result = true; _lastStableViewFrustum = *_viewFrustum; // save last stable @@ -1653,27 +1709,92 @@ void VoxelSystem::removeOutOfView() { } } +// combines the removeOutOfView args into a single class +class showAllLocalVoxelsArgs { +public: + VoxelSystem* thisVoxelSystem; + ViewFrustum thisViewFrustum; + unsigned long nodesScanned; + + showAllLocalVoxelsArgs(VoxelSystem* voxelSystem) : + thisVoxelSystem(voxelSystem), + thisViewFrustum(*voxelSystem->getViewFrustum()), + nodesScanned(0) + { + } +}; + +void VoxelSystem::showAllLocalVoxels() { + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "showAllLocalVoxels()"); + showAllLocalVoxelsArgs args(this); + _tree->recurseTreeWithOperation(showAllLocalVoxelsOperation,(void*)&args); + + bool showRemoveDebugDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + if (showRemoveDebugDetails) { + qDebug("showAllLocalVoxels() scanned=%ld \n",args.nodesScanned ); + } +} + +bool VoxelSystem::showAllLocalVoxelsOperation(VoxelNode* node, void* extraData) { + showAllLocalVoxelsArgs* args = (showAllLocalVoxelsArgs*)extraData; + + args->nodesScanned++; + + bool shouldRender = true; // node->calculateShouldRender(&args->thisViewFrustum); + node->setShouldRender(shouldRender); + + if (shouldRender) { + bool falseColorize = false; + if (falseColorize) { + node->setFalseColor(0,0,255); // fake + } + // how does randomize color work? + node->setDirtyBit(); // will this make it draw! + node->markWithChangedTime(); // fake + } + + return true; // keep recursing! +} + + // combines the removeOutOfView args into a single class class hideOutOfViewArgs { public: - VoxelSystem* thisVoxelSystem; - VoxelTree* tree; - ViewFrustum thisViewFrustum; - unsigned long nodesScanned; - unsigned long nodesRemoved; - unsigned long nodesInside; - unsigned long nodesIntersect; - unsigned long nodesOutside; + VoxelSystem* thisVoxelSystem; + VoxelTree* tree; + ViewFrustum thisViewFrustum; + ViewFrustum lastViewFrustum; + bool culledOnce; + bool wantDeltaFrustums; + unsigned long nodesScanned; + unsigned long nodesRemoved; + unsigned long nodesInside; + unsigned long nodesIntersect; + unsigned long nodesOutside; + unsigned long nodesInsideInside; + unsigned long nodesIntersectInside; + unsigned long nodesOutsideInside; + unsigned long nodesInsideOutside; + unsigned long nodesOutsideOutside; - hideOutOfViewArgs(VoxelSystem* voxelSystem, VoxelTree* tree, bool widenViewFrustum = true) : + hideOutOfViewArgs(VoxelSystem* voxelSystem, VoxelTree* tree, + bool culledOnce, bool widenViewFrustum, bool wantDeltaFrustums) : thisVoxelSystem(voxelSystem), tree(tree), thisViewFrustum(*voxelSystem->getViewFrustum()), + lastViewFrustum(*voxelSystem->getLastCulledViewFrustum()), + culledOnce(culledOnce), + wantDeltaFrustums(wantDeltaFrustums), nodesScanned(0), nodesRemoved(0), nodesInside(0), nodesIntersect(0), - nodesOutside(0) + nodesOutside(0), + nodesInsideInside(0), + nodesIntersectInside(0), + nodesOutsideInside(0), + nodesInsideOutside(0), + nodesOutsideOutside(0) { // Widen the FOV for trimming if (widenViewFrustum) { @@ -1686,9 +1807,67 @@ public: }; void VoxelSystem::hideOutOfView() { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "hideOutOfView()"); - hideOutOfViewArgs args(this, this->_tree, true); // widen to match server! + bool showDebugDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showDebugDetails, "hideOutOfView()", showDebugDetails); + bool widenFrustum = true; + bool wantDeltaFrustums = false; // Menu::getInstance()->isOptionChecked(MenuOption::UseDeltaFrustumInHide); + hideOutOfViewArgs args(this, this->_tree, _culledOnce, widenFrustum, wantDeltaFrustums); + + const bool wantViewFrustumDebugging = false; // change to true for additional debugging + if (wantViewFrustumDebugging) { + printf("\n\n----------thisViewFrustum----------\n"); + args.thisViewFrustum.printDebugDetails(); + + if (!_culledOnce) { + printf("\n\n----------NOT YET CULLED !!!!----------\n"); + } else { + printf("\n\n----------lastViewFrustum----------\n"); + args.lastViewFrustum.printDebugDetails(); + } + } + + if (_culledOnce && args.lastViewFrustum.matches(args.thisViewFrustum)) { + //printf("view frustum hasn't changed BAIL!!!\n"); + return; + } + + // Changed hideOutOfView() to support "delta" view frustums and only hide/show items that are in the difference + // between the two view frustums. There are some potential problems with this idea... + // + // 1) This might work well for rotating, but what about moving forward? + // in the move forward case, you'll get new voxel details, but those + // new voxels will be in the last view... does that work? + // + // 2) what about voxels coming in from the network that are OUTSIDE of the view + // frustum... they don't get hidden... and so we can't assume they are correctly + // hidden... we could solve this with checking in view on voxelUpdated... + // + // 3) this seems to mostly work for a few minutes, then it starts to break after some period of time?? + // and is it related to what appears to be the view changing even when stationary...??? + // + // 4) if something goes wrong it stays wrong? unless new packets come from network... why aren't those redrawing... + // + // consider doing false colorization... + // + // What if we kept track of which voxels were visible, and iterated them in an array instead of in the tree? + // would that be faster? seems like it wouldn't be. + + // + // What's working well now.... + // Fast Voxel Pipeline + normal Remove Out Of View... + // this fails when you spin around, it will just have a big blank spot... + // you also see flashes of white/no voxels in areas you've already been + // + // When you add Don't Remove Out of View voxels... + // works OK... but... it loads up too many voxels... + // and frame rate drops... + // + // We'd like to add in hide/show... but things break... See above. + // this is the problem... SOLVE this problem... + _tree->recurseTreeWithOperation(hideOutOfViewOperation,(void*)&args); + _lastCulledViewFrustum = args.thisViewFrustum; // save last stable + _culledOnce = true; if (args.nodesRemoved) { _tree->setDirtyBit(); @@ -1696,24 +1875,47 @@ void VoxelSystem::hideOutOfView() { } - bool showRemoveDebugDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - if (showRemoveDebugDetails) { + if (showDebugDetails) { qDebug("hideOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld\n", args.nodesScanned, args.nodesRemoved, args.nodesInside, args.nodesIntersect, args.nodesOutside ); + qDebug(" inside/inside=%ld intersect/inside=%ld outside/outside=%ld\n", + args.nodesInsideInside, args.nodesIntersectInside, args.nodesOutsideOutside + ); } } bool VoxelSystem::hideAllSubTreeOperation(VoxelNode* node, void* extraData) { hideOutOfViewArgs* args = (hideOutOfViewArgs*)extraData; + // If we've culled at least once, then we will use the status of this voxel in the last culled frustum to determine + // how to proceed. If we've never culled, then we just consider all these voxels to be UNKNOWN so that we will not + // consider that case. + ViewFrustum::location inLastCulledFrustum; + + if (args->culledOnce && args->wantDeltaFrustums) { + inLastCulledFrustum = node->inFrustum(args->lastViewFrustum); + + // if this node is fully OUTSIDE our last culled view frustum, then we don't need to recurse further + if (inLastCulledFrustum == ViewFrustum::OUTSIDE) { + args->nodesOutsideOutside++; + return false; + } + } + args->nodesOutside++; if (node->isKnownBufferIndex()) { args->nodesRemoved++; - VoxelSystem* thisVoxelSystem = args->thisVoxelSystem; - thisVoxelSystem->_voxelsUpdated += thisVoxelSystem->forceRemoveNodeFromArrays(node); - thisVoxelSystem->setupNewVoxelsForDrawingSingleNode(); + bool falseColorize = false; + if (falseColorize) { + node->setFalseColor(255,0,0); // fake + } else { + VoxelSystem* thisVoxelSystem = args->thisVoxelSystem; + thisVoxelSystem->_voxelsUpdated += thisVoxelSystem->forceRemoveNodeFromArrays(node); + thisVoxelSystem->setupNewVoxelsForDrawingSingleNode(); + } + } return true; @@ -1722,17 +1924,40 @@ bool VoxelSystem::hideAllSubTreeOperation(VoxelNode* node, void* extraData) { bool VoxelSystem::showAllSubTreeOperation(VoxelNode* node, void* extraData) { hideOutOfViewArgs* args = (hideOutOfViewArgs*)extraData; + // If we've culled at least once, then we will use the status of this voxel in the last culled frustum to determine + // how to proceed. If we've never culled, then we just consider all these voxels to be UNKNOWN so that we will not + // consider that case. + ViewFrustum::location inLastCulledFrustum; + + if (args->culledOnce && args->wantDeltaFrustums) { + inLastCulledFrustum = node->inFrustum(args->lastViewFrustum); + + // if this node is fully inside our last culled view frustum, then we don't need to recurse further + if (inLastCulledFrustum == ViewFrustum::INSIDE) { + args->nodesInsideInside++; + return false; + } + } + args->nodesInside++; - if (node->getShouldRender() && !node->isKnownBufferIndex()) { + //printf("<<<<<<<<<<< showAllSubTreeOperation() >>>>>>>>>>>>>>>\n"); + + bool shouldRender = node->calculateShouldRender(&args->thisViewFrustum); + node->setShouldRender(shouldRender); + + if (shouldRender /*&& !node->isKnownBufferIndex()*/) { + bool falseColorize = false; + if (falseColorize) { + node->setFalseColor(0,0,255); // fake + } node->setDirtyBit(); // will this make it draw! + node->markWithChangedTime(); // fake } return true; // keep recursing! } - - // "hide" voxels in the VBOs that are still in the tree that but not in view. // We don't remove them from the tree, we don't delete them, we do remove them // from the VBOs and mark them as such in the tree. @@ -1742,26 +1967,67 @@ bool VoxelSystem::hideOutOfViewOperation(VoxelNode* node, void* extraData) { // If we're still recursing the tree using this operator, then we don't know if we're inside or outside... // so before we move forward we need to determine our frustum location ViewFrustum::location inFrustum = node->inFrustum(args->thisViewFrustum); + + // If we've culled at least once, then we will use the status of this voxel in the last culled frustum to determine + // how to proceed. If we've never culled, then we just consider all these voxels to be UNKNOWN so that we will not + // consider that case. + ViewFrustum::location inLastCulledFrustum; + + if (args->culledOnce && args->wantDeltaFrustums) { + inLastCulledFrustum = node->inFrustum(args->lastViewFrustum); + } // ok, now do some processing for this node... switch (inFrustum) { case ViewFrustum::OUTSIDE: { - // if this node is fully OUTSIDE the view, then we know that ALL of it's children are also fully OUTSIDE - // so we can recurse the children and simply mark them as hidden + + // If this node is outside the current view, then we might want to hide it... unless it was previously OUTSIDE, + // if it was previously outside, then we can safely assume it's already hidden, and we can also safely assume + // that all of it's children are outside both of our views, in which case we can just stop recursing... + if (args->culledOnce && args->wantDeltaFrustums && inLastCulledFrustum == ViewFrustum::OUTSIDE) { + args->nodesScanned++; + args->nodesOutsideOutside++; + return false; // stop recursing this branch! + } + + // if this node is fully OUTSIDE the view, but previously intersected and/or was inside the last view, then + // we need to hide it. Additionally we know that ALL of it's children are also fully OUTSIDE so we can recurse + // the children and simply mark them as hidden args->tree->recurseNodeWithOperation(node, hideAllSubTreeOperation, args ); return false; } break; case ViewFrustum::INSIDE: { - // if this node is fully INSIDE the view, then we know that ALL of it's children are also fully INSIDE - // so we can recurse the children and simply mark them as visible (as appropriate based on LOD) - args->tree->recurseNodeWithOperation(node, showAllSubTreeOperation, args ); + + // If this node is INSIDE the current view, then we might want to show it... unless it was previously INSIDE, + // if it was previously INSIDE, then we can safely assume it's already shown, and we can also safely assume + // that all of it's children are INSIDE both of our views, in which case we can just stop recursing... + if (args->culledOnce && args->wantDeltaFrustums && inLastCulledFrustum == ViewFrustum::INSIDE) { + args->nodesScanned++; + args->nodesInsideInside++; + return false; // stop recursing this branch! + } + + // if this node is fully INSIDE the view, but previously INTERSECTED and/or was OUTSIDE the last view, then + // we need to show it. Additionally we know that ALL of it's children are also fully INSIDE so we can recurse + // the children and simply mark them as visible (as appropriate based on LOD) + //printf("<<<<<<<<<<< HERE!!!!! >>>>>>>>>>>>>>>\n"); + args->tree->recurseNodeWithOperation(node, showAllSubTreeOperation, args); return false; } break; case ViewFrustum::INTERSECT: { args->nodesScanned++; + + // If this node INTERSECTS the current view, then we might want to show it... unless it was previously INSIDE + // the last known view, in which case it will already be visible, and we know that all it's children are also + // previously INSIDE and visible. So in this case stop recursing + if (args->culledOnce && args->wantDeltaFrustums && inLastCulledFrustum == ViewFrustum::INSIDE) { + args->nodesIntersectInside++; + return false; // stop recursing this branch! + } + args->nodesIntersect++; // if the child node INTERSECTs the view, then we want to check to see if it thinks it should render @@ -1887,6 +2153,7 @@ public: }; bool VoxelSystem::collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData) { + collectStatsForTreesAndVBOsArgs* args = (collectStatsForTreesAndVBOsArgs*)extraData; args->totalNodes++; diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 238b9f6d96..7428f82013 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -59,6 +59,9 @@ public: void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; } unsigned long getVoxelsUpdated() const { return _voxelsUpdated; } unsigned long getVoxelsRendered() const { return _voxelsInReadArrays; } + unsigned long getVoxelsWritten() const { return _voxelsInWriteArrays; } + + ViewFrustum* getLastCulledViewFrustum() { return &_lastCulledViewFrustum; } void loadVoxelsFile(const char* fileName,bool wantColorRandomizer); void writeToSVOFile(const char* filename, VoxelNode* node) const; @@ -123,6 +126,7 @@ public slots: void collectStatsForTreesAndVBOs(); // Methods that recurse tree + void showAllLocalVoxels(); void randomizeVoxelColors(); void falseColorizeRandom(); void trueColorize(); @@ -192,6 +196,7 @@ private: static bool hideOutOfViewUnrollOperation(VoxelNode* node, void* extraData); static bool hideAllSubTreeOperation(VoxelNode* node, void* extraData); static bool showAllSubTreeOperation(VoxelNode* node, void* extraData); + static bool showAllLocalVoxelsOperation(VoxelNode* node, void* extraData); int updateNodeInArrays(VoxelNode* node, bool reuseIndex, bool forceDraw); int forceRemoveNodeFromArrays(VoxelNode* node); @@ -252,10 +257,13 @@ private: pthread_mutex_t _bufferWriteLock; pthread_mutex_t _treeLock; - ViewFrustum _lastKnowViewFrustum; + ViewFrustum _lastKnownViewFrustum; ViewFrustum _lastStableViewFrustum; ViewFrustum* _viewFrustum; + ViewFrustum _lastCulledViewFrustum; // used for hide/show visible passes + bool _culledOnce; + void setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndices[]); int newTreeToArrays(VoxelNode *currentNode);