Job #19486 Don't render the duplicate triangles between adjacent voxels. Added checkable item to "developer" menu to activate/deactivate cull shared faces. Cull all shared voxel faces regardless of distribution in the octree. Includes leaf to leaf, leaf to non-leaf, and non-leaf to non-leaf comparison of contiguous faces. Added new GL primitive renderer to manage the transfer of cube faces from the voxel system to GL. Added some useful classes for queue and lock management. Modified statistics collection to report number of completely culled nodes. Extra debugging info also includes the interior occlusions bit mask of leaf voxels.
Showing the changes to interface/src/VoxelSystem.cpp (1 of 16 files). Show all
Unified · Split
| @@ -60,7 +60,9 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) · 59 unmodified lines | |||
| 60 | : NodeData(), | 60 | : NodeData(), |
| 61 | _treeScale(treeScale), | 61 | _treeScale(treeScale), |
| 62 | _maxVoxels(maxVoxels), | 62 | _maxVoxels(maxVoxels), |
| 63 | _initialized(false) { | 63 | _initialized(false), |
| 64 | _usePrimitiveRenderer(false), | ||
| 65 | _renderer(0) { | ||
| 64 | 66 | ||
| 65 | _voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0; | 67 | _voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0; |
| 66 | _writeRenderFullVBO = true; | 68 | _writeRenderFullVBO = true; |
| @@ -102,12 +104,13 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) · 35 unmodified lines | |||
| 102 | _inhideOutOfView = false; | 104 | _inhideOutOfView = false; |
| 103 | _treeIsBusy = false; | 105 | _treeIsBusy = false; |
| 104 | 106 | ||
| 107 | |||
| 105 | } | 108 | } |
| 106 | 109 | ||
| 107 | void VoxelSystem::elementDeleted(OctreeElement* element) { | 110 | void VoxelSystem::elementDeleted(OctreeElement* element) { |
| 108 | VoxelTreeElement* voxel = (VoxelTreeElement*)element; | 111 | VoxelTreeElement* voxel = (VoxelTreeElement*)element; |
| 109 | if (voxel->getVoxelSystem() == this) { | 112 | if (voxel->getVoxelSystem() == this) { |
| 110 | if (_voxelsInWriteArrays != 0) { | 113 | if ((_voxelsInWriteArrays != 0) || _usePrimitiveRenderer) { |
| 111 | forceRemoveNodeFromArrays(voxel); | 114 | forceRemoveNodeFromArrays(voxel); |
| 112 | } else { | 115 | } else { |
| 113 | if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { | 116 | if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { |
| @@ -212,16 +215,16 @@ void VoxelSystem::freeBufferIndex(glBufferIndex index) { · 98 unmodified lines | |||
| 212 | } | 215 | } |
| 213 | } | 216 | } |
| 214 | if (!inList) { | 217 | if (!inList) { |
| 215 | // make the index available for next node that needs to be drawn | 218 | // make the index available for next node that needs to be drawn |
| 216 | _freeIndexLock.lock(); | 219 | _freeIndexLock.lock(); |
| 217 | _freeIndexes.push_back(index); | 220 | _freeIndexes.push_back(index); |
| 218 | _freeIndexLock.unlock(); | 221 | _freeIndexLock.unlock(); |
| 219 | 222 | ||
| 220 | // make the VBO slot "invisible" in case this slot is not used | 223 | // make the VBO slot "invisible" in case this slot is not used |
| 221 | const glm::vec3 startVertex(FLT_MAX, FLT_MAX, FLT_MAX); | 224 | const glm::vec3 startVertex(FLT_MAX, FLT_MAX, FLT_MAX); |
| 222 | const float voxelScale = 0; | 225 | const float voxelScale = 0; |
| 223 | const nodeColor BLACK = {0, 0, 0, 0}; | 226 | const nodeColor BLACK = {0, 0, 0, 0}; |
| 224 | updateArraysDetails(index, startVertex, voxelScale, BLACK); | 227 | updateArraysDetails(index, startVertex, voxelScale, BLACK); |
| 225 | } | 228 | } |
| 226 | } | 229 | } |
| 227 | 230 | ||
| @@ -250,6 +253,8 @@ VoxelSystem::~VoxelSystem() { · 22 unmodified lines | |||
| 250 | 253 | ||
| 251 | cleanupVoxelMemory(); | 254 | cleanupVoxelMemory(); |
| 252 | delete _tree; | 255 | delete _tree; |
| 256 | |||
| 257 | _renderer = 0; | ||
| 253 | } | 258 | } |
| 254 | 259 | ||
| 255 | void VoxelSystem::setMaxVoxels(int maxVoxels) { | 260 | void VoxelSystem::setMaxVoxels(int maxVoxels) { |
| @@ -258,12 +263,12 @@ void VoxelSystem::setMaxVoxels(int maxVoxels) { · 2 unmodified lines | |||
| 258 | } | 263 | } |
| 259 | bool wasInitialized = _initialized; | 264 | bool wasInitialized = _initialized; |
| 260 | if (wasInitialized) { | 265 | if (wasInitialized) { |
| 261 | clearAllNodesBufferIndex(); | 266 | clearAllNodesBufferIndex(); |
| 262 | cleanupVoxelMemory(); | 267 | cleanupVoxelMemory(); |
| 263 | } | 268 | } |
| 264 | _maxVoxels = maxVoxels; | 269 | _maxVoxels = maxVoxels; |
| 265 | if (wasInitialized) { | 270 | if (wasInitialized) { |
| 266 | initVoxelMemory(); | 271 | initVoxelMemory(); |
| 267 | } | 272 | } |
| 268 | if (wasInitialized) { | 273 | if (wasInitialized) { |
| 269 | forceRedrawEntireTree(); | 274 | forceRedrawEntireTree(); |
| @@ -281,6 +286,7 @@ void VoxelSystem::setUseVoxelShader(bool useVoxelShader) { · 11 unmodified lines | |||
| 281 | cleanupVoxelMemory(); | 286 | cleanupVoxelMemory(); |
| 282 | } | 287 | } |
| 283 | _useVoxelShader = useVoxelShader; | 288 | _useVoxelShader = useVoxelShader; |
| 289 | _usePrimitiveRenderer = false; | ||
| 284 | if (wasInitialized) { | 290 | if (wasInitialized) { |
| 285 | initVoxelMemory(); | 291 | initVoxelMemory(); |
| 286 | } | 292 | } |
| @@ -345,7 +351,8 @@ void VoxelSystem::cleanupVoxelMemory() { · 58 unmodified lines | |||
| 345 | 351 | ||
| 346 | _writeVoxelShaderData = _readVoxelShaderData = NULL; | 352 | _writeVoxelShaderData = _readVoxelShaderData = NULL; |
| 347 | 353 | ||
| 348 | } else { | 354 | } else |
| 355 | if (! _usePrimitiveRenderer) { | ||
| 349 | // Destroy glBuffers | 356 | // Destroy glBuffers |
| 350 | glDeleteBuffers(1, &_vboVerticesID); | 357 | glDeleteBuffers(1, &_vboVerticesID); |
| 351 | glDeleteBuffers(1, &_vboColorsID); | 358 | glDeleteBuffers(1, &_vboColorsID); |
| @@ -366,8 +373,11 @@ void VoxelSystem::cleanupVoxelMemory() { · 14 unmodified lines | |||
| 366 | _writeVerticesArray = NULL; | 373 | _writeVerticesArray = NULL; |
| 367 | _readColorsArray = NULL; | 374 | _readColorsArray = NULL; |
| 368 | _writeColorsArray = NULL; | 375 | _writeColorsArray = NULL; |
| 369 | |||
| 370 | } | 376 | } |
| 377 | else { | ||
| 378 | delete _renderer; | ||
| 379 | _renderer = 0; | ||
| 380 | } | ||
| 371 | delete[] _writeVoxelDirtyArray; | 381 | delete[] _writeVoxelDirtyArray; |
| 372 | delete[] _readVoxelDirtyArray; | 382 | delete[] _readVoxelDirtyArray; |
| 373 | _writeVoxelDirtyArray = _readVoxelDirtyArray = NULL; | 383 | _writeVoxelDirtyArray = _readVoxelDirtyArray = NULL; |
| @@ -452,7 +462,8 @@ void VoxelSystem::initVoxelMemory() { · 78 unmodified lines | |||
| 452 | 462 | ||
| 453 | _readVoxelShaderData = new VoxelShaderVBOData[_maxVoxels]; | 463 | _readVoxelShaderData = new VoxelShaderVBOData[_maxVoxels]; |
| 454 | _memoryUsageRAM += (sizeof(VoxelShaderVBOData) * _maxVoxels); | 464 | _memoryUsageRAM += (sizeof(VoxelShaderVBOData) * _maxVoxels); |
| 455 | } else { | 465 | } else |
| 466 | if (! _usePrimitiveRenderer) { | ||
| 456 | 467 | ||
| 457 | // Global Normals mode uses a technique of not including normals on any voxel vertices, and instead | 468 | // Global Normals mode uses a technique of not including normals on any voxel vertices, and instead |
| 458 | // rendering the voxel faces in 6 passes that use a global call to glNormal3f() | 469 | // rendering the voxel faces in 6 passes that use a global call to glNormal3f() |
| @@ -496,7 +507,6 @@ void VoxelSystem::initVoxelMemory() { · 37 unmodified lines | |||
| 496 | _readColorsArray = new GLubyte[vertexPointsPerVoxel * _maxVoxels]; | 507 | _readColorsArray = new GLubyte[vertexPointsPerVoxel * _maxVoxels]; |
| 497 | _memoryUsageRAM += (sizeof(GLubyte) * vertexPointsPerVoxel * _maxVoxels); | 508 | _memoryUsageRAM += (sizeof(GLubyte) * vertexPointsPerVoxel * _maxVoxels); |
| 498 | 509 | ||
| 499 | |||
| 500 | // create our simple fragment shader if we're the first system to init | 510 | // create our simple fragment shader if we're the first system to init |
| 501 | if (!_perlinModulateProgram.isLinked()) { | 511 | if (!_perlinModulateProgram.isLinked()) { |
| 502 | switchToResourcesParentIfRequired(); | 512 | switchToResourcesParentIfRequired(); |
| @@ -516,6 +526,27 @@ void VoxelSystem::initVoxelMemory() { · 13 unmodified lines | |||
| 516 | _shadowMapProgram.release(); | 526 | _shadowMapProgram.release(); |
| 517 | } | 527 | } |
| 518 | } | 528 | } |
| 529 | else { | ||
| 530 | _renderer = new PrimitiveRenderer(_maxVoxels); | ||
| 531 | // create our simple fragment shader if we're the first system to init | ||
| 532 | if (!_perlinModulateProgram.isLinked()) { | ||
| 533 | switchToResourcesParentIfRequired(); | ||
| 534 | _perlinModulateProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/perlin_modulate.vert"); | ||
| 535 | _perlinModulateProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/perlin_modulate.frag"); | ||
| 536 | _perlinModulateProgram.link(); | ||
| 537 | |||
| 538 | _perlinModulateProgram.bind(); | ||
| 539 | _perlinModulateProgram.setUniformValue("permutationNormalTexture", 0); | ||
| 540 | _perlinModulateProgram.release(); | ||
| 541 | |||
| 542 | _shadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/shadow_map.frag"); | ||
| 543 | _shadowMapProgram.link(); | ||
| 544 | |||
| 545 | _shadowMapProgram.bind(); | ||
| 546 | _shadowMapProgram.setUniformValue("shadowMap", 0); | ||
| 547 | _shadowMapProgram.release(); | ||
| 548 | } | ||
| 549 | } | ||
| 519 | 550 | ||
| 520 | _initialized = true; | 551 | _initialized = true; |
| 521 | 552 | ||
| @@ -655,8 +686,15 @@ void VoxelSystem::setupNewVoxelsForDrawing() { · 133 unmodified lines | |||
| 655 | }; | 686 | }; |
| 656 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer); | 687 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer); |
| 657 | _callsToTreesToArrays++; | 688 | _callsToTreesToArrays++; |
| 689 | |||
| 658 | if (_writeRenderFullVBO) { | 690 | if (_writeRenderFullVBO) { |
| 659 | clearFreeBufferIndexes(); | 691 | if (_usePrimitiveRenderer) { |
| 692 | delete _renderer; | ||
| 693 | _renderer = new PrimitiveRenderer(_maxVoxels); | ||
| 694 | } | ||
| 695 | else { | ||
| 696 | clearFreeBufferIndexes(); | ||
| 697 | } | ||
| 660 | } | 698 | } |
| 661 | _voxelsUpdated = newTreeToArrays(_tree->getRoot()); | 699 | _voxelsUpdated = newTreeToArrays(_tree->getRoot()); |
| 662 | _tree->clearDirtyBit(); // after we pull the trees into the array, we can consider the tree clean | 700 | _tree->clearDirtyBit(); // after we pull the trees into the array, we can consider the tree clean |
| @@ -672,17 +710,24 @@ void VoxelSystem::setupNewVoxelsForDrawing() { · 9 unmodified lines | |||
| 672 | _voxelsUpdated = 0; | 710 | _voxelsUpdated = 0; |
| 673 | } | 711 | } |
| 674 | 712 | ||
| 675 | // lock on the buffer write lock so we can't modify the data when the GPU is reading it | 713 | if (_usePrimitiveRenderer) { |
| 676 | _bufferWriteLock.lock(); | 714 | if (_voxelsUpdated) { |
| 715 | _voxelsDirty=true; | ||
| 716 | } | ||
| 717 | } | ||
| 718 | else { | ||
| 719 | // lock on the buffer write lock so we can't modify the data when the GPU is reading it | ||
| 720 | _bufferWriteLock.lock(); | ||
| 677 | 721 | ||
| 678 | if (_voxelsUpdated) { | 722 | if (_voxelsUpdated) { |
| 679 | _voxelsDirty=true; | 723 | _voxelsDirty=true; |
| 680 | } | 724 | } |
| 681 | 725 | ||
| 682 | // copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated | 726 | // copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated |
| 683 | copyWrittenDataToReadArrays(didWriteFullVBO); | 727 | copyWrittenDataToReadArrays(didWriteFullVBO); |
| 684 | 728 | ||
| 685 | _bufferWriteLock.unlock(); | 729 | _bufferWriteLock.unlock(); |
| 730 | } | ||
| 686 | 731 | ||
| 687 | uint64_t end = usecTimestampNow(); | 732 | uint64_t end = usecTimestampNow(); |
| 688 | int elapsedmsec = (end - start) / 1000; | 733 | int elapsedmsec = (end - start) / 1000; |
| @@ -710,23 +755,28 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { · 21 unmodified lines | |||
| 710 | return; // bail early, it hasn't been long enough since the last time we ran | 755 | return; // bail early, it hasn't been long enough since the last time we ran |
| 711 | } | 756 | } |
| 712 | 757 | ||
| 713 | // lock on the buffer write lock so we can't modify the data when the GPU is reading it | 758 | if (_usePrimitiveRenderer) { |
| 714 | { | 759 | _voxelsDirty = true; // if we got this far, then we can assume some voxels are dirty |
| 715 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), | 760 | _voxelsUpdated = 0; |
| 716 | "setupNewVoxelsForDrawingSingleNode()... _bufferWriteLock.lock();" ); | 761 | } |
| 717 | _bufferWriteLock.lock(); | 762 | else { |
| 718 | } | 763 | // lock on the buffer write lock so we can't modify the data when the GPU is reading it |
| 719 | 764 | { | |
| 720 | _voxelsDirty = true; // if we got this far, then we can assume some voxels are dirty | 765 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), |
| 766 | "setupNewVoxelsForDrawingSingleNode()... _bufferWriteLock.lock();" ); | ||
| 767 | _bufferWriteLock.lock(); | ||
| 768 | } | ||
| 721 | 769 | ||
| 722 | // copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated | 770 | _voxelsDirty = true; // if we got this far, then we can assume some voxels are dirty |
| 723 | copyWrittenDataToReadArrays(_writeRenderFullVBO); | ||
| 724 | 771 | ||
| 725 | // after... | 772 | // copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated |
| 726 | _voxelsUpdated = 0; | 773 | copyWrittenDataToReadArrays(_writeRenderFullVBO); |
| 727 | 774 | ||
| 728 | _bufferWriteLock.unlock(); | 775 | // after... |
| 776 | _voxelsUpdated = 0; | ||
| 729 | 777 | ||
| 778 | _bufferWriteLock.unlock(); | ||
| 779 | } | ||
| 730 | uint64_t end = usecTimestampNow(); | 780 | uint64_t end = usecTimestampNow(); |
| 731 | int elapsedmsec = (end - start) / 1000; | 781 | int elapsedmsec = (end - start) / 1000; |
| 732 | _setupNewVoxelsForDrawingLastFinished = end; | 782 | _setupNewVoxelsForDrawingLastFinished = end; |
| @@ -946,14 +996,24 @@ int VoxelSystem::forceRemoveNodeFromArrays(VoxelTreeElement* node) { · 213 unmodified lines | |||
| 946 | return 0; | 996 | return 0; |
| 947 | } | 997 | } |
| 948 | 998 | ||
| 949 | // if the node is not in the VBOs then we have nothing to do! | 999 | if (_usePrimitiveRenderer) { |
| 950 | if (node->isKnownBufferIndex()) { | 1000 | int primitiveIndex = node->getPrimitiveIndex(); |
| 951 | // If this node has not yet been written to the array, then add it to the end of the array. | 1001 | if (primitiveIndex) { |
| 952 | glBufferIndex nodeIndex = node->getBufferIndex(); | 1002 | _renderer->remove(primitiveIndex); |
| 953 | node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN); | 1003 | node->setPrimitiveIndex(0); |
| 954 | freeBufferIndex(nodeIndex); // NOTE: This will make the node invisible! | 1004 | return 1; |
| 955 | return 1; // updated! | 1005 | } |
| 956 | } | 1006 | } |
| 1007 | else { | ||
| 1008 | // if the node is not in the VBOs then we have nothing to do! | ||
| 1009 | if (node->isKnownBufferIndex()) { | ||
| 1010 | // If this node has not yet been written to the array, then add it to the end of the array. | ||
| 1011 | glBufferIndex nodeIndex = node->getBufferIndex(); | ||
| 1012 | node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN); | ||
| 1013 | freeBufferIndex(nodeIndex); // NOTE: This will make the node invisible! | ||
| 1014 | return 1; // updated! | ||
| 1015 | } | ||
| 1016 | } | ||
| 957 | return 0; // not-updated | 1017 | return 0; // not-updated |
| 958 | } | 1018 | } |
| 959 | 1019 | ||
| @@ -980,20 +1040,46 @@ int VoxelSystem::updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, boo · 20 unmodified lines | |||
| 980 | if (forceDraw || node->isDirty()) { | 1040 | if (forceDraw || node->isDirty()) { |
| 981 | // If we're should render, use our legit location and scale, | 1041 | // If we're should render, use our legit location and scale, |
| 982 | if (node->getShouldRender()) { | 1042 | if (node->getShouldRender()) { |
| 983 | glm::vec3 startVertex = node->getCorner(); | 1043 | glm::vec3 startVertex = node->getCorner(); |
| 984 | float voxelScale = node->getScale(); | 1044 | float voxelScale = node->getScale(); |
| 985 | 1045 | nodeColor const & color = node->getColor(); | |
| 986 | glBufferIndex nodeIndex = GLBUFFER_INDEX_UNKNOWN; | 1046 | |
| 987 | if (reuseIndex && node->isKnownBufferIndex()) { | 1047 | if (_usePrimitiveRenderer) { |
| 988 | nodeIndex = node->getBufferIndex(); | 1048 | int primitiveIndex = node->getPrimitiveIndex(); |
| 989 | } else { | 1049 | if (primitiveIndex) { |
| 990 | nodeIndex = getNextBufferIndex(); | 1050 | _renderer->remove(primitiveIndex); |
| 991 | node->setBufferIndex(nodeIndex); | 1051 | node->setPrimitiveIndex(0); |
| 992 | node->setVoxelSystem(this); | 1052 | } |
| 993 | } | 1053 | else { |
| 994 | // populate the array with points for the 8 vertices and RGB color for each added vertex | 1054 | node->setVoxelSystem(this); |
| 995 | updateArraysDetails(nodeIndex, startVertex, voxelScale, node->getColor()); | 1055 | } |
| 996 | return 1; // updated! | 1056 | inspectForInteriorOcclusionsOperation(node, 0); |
| 1057 | |||
| 1058 | if (node->getInteriorOcclusions() != OctreeElement::HalfSpace::All) | ||
| 1059 | { | ||
| 1060 | Cube *cube = new Cube( | ||
| 1061 | startVertex.x, startVertex.y, startVertex.z, voxelScale, | ||
| 1062 | color[RED_INDEX], color[GREEN_INDEX], color[BLUE_INDEX], | ||
| 1063 | node->getInteriorOcclusions()); | ||
| 1064 | if (cube) { | ||
| 1065 | primitiveIndex = _renderer->add(cube); | ||
| 1066 | node->setPrimitiveIndex(primitiveIndex); | ||
| 1067 | } | ||
| 1068 | } | ||
| 1069 | } | ||
| 1070 | else { | ||
| 1071 | glBufferIndex nodeIndex = GLBUFFER_INDEX_UNKNOWN; | ||
| 1072 | if (reuseIndex && node->isKnownBufferIndex()) { | ||
| 1073 | nodeIndex = node->getBufferIndex(); | ||
| 1074 | } else { | ||
| 1075 | nodeIndex = getNextBufferIndex(); | ||
| 1076 | node->setBufferIndex(nodeIndex); | ||
| 1077 | node->setVoxelSystem(this); | ||
| 1078 | } | ||
| 1079 | // populate the array with points for the 8 vertices and RGB color for each added vertex | ||
| 1080 | updateArraysDetails(nodeIndex, startVertex, voxelScale, node->getColor()); | ||
| 1081 | } | ||
| 1082 | return 1; // updated! | ||
| 997 | } else { | 1083 | } else { |
| 998 | // If we shouldn't render, and we're in reuseIndex mode, then free our index, this only operates | 1084 | // If we shouldn't render, and we're in reuseIndex mode, then free our index, this only operates |
| 999 | // on nodes with known index values, so it's safe to call for any node. | 1085 | // on nodes with known index values, so it's safe to call for any node. |
| @@ -1140,15 +1226,17 @@ void VoxelSystem::updateVBOs() { · 140 unmodified lines | |||
| 1140 | }; | 1226 | }; |
| 1141 | // would like to include _callsToTreesToArrays | 1227 | // would like to include _callsToTreesToArrays |
| 1142 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer); | 1228 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer); |
| 1143 | if (_voxelsDirty) { | 1229 | if (! _usePrimitiveRenderer) { |
| 1144 | if (_readRenderFullVBO) { | 1230 | if (_voxelsDirty) { |
| 1145 | updateFullVBOs(); | 1231 | if (_readRenderFullVBO) { |
| 1146 | } else { | 1232 | updateFullVBOs(); |
| 1147 | updatePartialVBOs(); | 1233 | } else { |
| 1148 | } | 1234 | updatePartialVBOs(); |
| 1149 | _voxelsDirty = false; | 1235 | } |
| 1150 | _readRenderFullVBO = false; | 1236 | _voxelsDirty = false; |
| 1151 | } | 1237 | _readRenderFullVBO = false; |
| 1238 | } | ||
| 1239 | } | ||
| 1152 | _callsToTreesToArrays = 0; // clear it | 1240 | _callsToTreesToArrays = 0; // clear it |
| 1153 | } | 1241 | } |
| 1154 | 1242 | ||
| @@ -1274,7 +1362,8 @@ void VoxelSystem::render(bool texture) { · 119 unmodified lines | |||
| 1274 | glDisableVertexAttribArray(attributeLocation); | 1362 | glDisableVertexAttribArray(attributeLocation); |
| 1275 | glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); | 1363 | glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); |
| 1276 | } | 1364 | } |
| 1277 | } else { | 1365 | } else |
| 1366 | if (!_usePrimitiveRenderer) { | ||
| 1278 | PerformanceWarning warn(showWarnings, "render().. TRIANGLES..."); | 1367 | PerformanceWarning warn(showWarnings, "render().. TRIANGLES..."); |
| 1279 | 1368 | ||
| 1280 | { | 1369 | { |
| @@ -1348,6 +1437,12 @@ void VoxelSystem::render(bool texture) { · 67 unmodified lines | |||
| 1348 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); | 1437 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| 1349 | } | 1438 | } |
| 1350 | } | 1439 | } |
| 1440 | else { | ||
| 1441 | applyScaleAndBindProgram(texture); | ||
| 1442 | _renderer->render(); | ||
| 1443 | removeScaleAndReleaseProgram(texture); | ||
| 1444 | |||
| 1445 | } | ||
| 1351 | } | 1446 | } |
| 1352 | 1447 | ||
| 1353 | void VoxelSystem::applyScaleAndBindProgram(bool texture) { | 1448 | void VoxelSystem::applyScaleAndBindProgram(bool texture) { |
| @@ -1411,6 +1506,7 @@ bool VoxelSystem::clearAllNodesBufferIndexOperation(OctreeElement* element, void · 57 unmodified lines | |||
| 1411 | _nodeCount++; | 1506 | _nodeCount++; |
| 1412 | VoxelTreeElement* voxel = (VoxelTreeElement*)element; | 1507 | VoxelTreeElement* voxel = (VoxelTreeElement*)element; |
| 1413 | voxel->setBufferIndex(GLBUFFER_INDEX_UNKNOWN); | 1508 | voxel->setBufferIndex(GLBUFFER_INDEX_UNKNOWN); |
| 1509 | voxel->setPrimitiveIndex(0); | ||
| 1414 | return true; | 1510 | return true; |
| 1415 | } | 1511 | } |
| 1416 | 1512 | ||
| @@ -1424,6 +1520,215 @@ void VoxelSystem::clearAllNodesBufferIndex() { · 7 unmodified lines | |||
| 1424 | } | 1520 | } |
| 1425 | } | 1521 | } |
| 1426 | 1522 | ||
| 1523 | bool VoxelSystem::inspectForInteriorOcclusionsOperation(OctreeElement* element, void* extraData) { | ||
| 1524 | _nodeCount++; | ||
| 1525 | VoxelTreeElement* voxel = (VoxelTreeElement*)element; | ||
| 1526 | |||
| 1527 | // Nothing to do at the leaf level | ||
| 1528 | if (voxel->isLeaf()) { | ||
| 1529 | return false; | ||
| 1530 | } | ||
| 1531 | |||
| 1532 | // Bit mask of occluded shared faces indexed by child | ||
| 1533 | unsigned char occludedSharedFace[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; | ||
| 1534 | |||
| 1535 | // Traverse all pair combinations of children | ||
| 1536 | for (int i = NUMBER_OF_CHILDREN; --i >= 0; ) { | ||
| 1537 | |||
| 1538 | VoxelTreeElement *childA = voxel->getChildAtIndex(i); | ||
| 1539 | if (childA) { | ||
| 1540 | |||
| 1541 | // Get the child A's occluding faces, for a leaf that will be | ||
| 1542 | // all six voxel faces, and for a non leaf, that will be | ||
| 1543 | // all faces which are completely covered by four child octants. | ||
| 1544 | unsigned char exteriorOcclusionsA = childA->getExteriorOcclusions(); | ||
| 1545 | |||
| 1546 | if (exteriorOcclusionsA == 0) { | ||
| 1547 | // There is nothing to be done with this child, next... | ||
| 1548 | continue; | ||
| 1549 | } | ||
| 1550 | |||
| 1551 | for (int j = i; --j >= 0; ) { | ||
| 1552 | |||
| 1553 | VoxelTreeElement *childB = voxel->getChildAtIndex(j); | ||
| 1554 | if (childB) { | ||
| 1555 | |||
| 1556 | // Get child B's occluding faces | ||
| 1557 | unsigned char exteriorOcclusionsB = childB->getExteriorOcclusions(); | ||
| 1558 | |||
| 1559 | if (exteriorOcclusionsB == 0) { | ||
| 1560 | // There is nothing to be done with this child, next... | ||
| 1561 | continue; | ||
| 1562 | } | ||
| 1563 | |||
| 1564 | // Determine the shared halfspace partition between siblings A and B, | ||
| 1565 | // i.e., near/far, left/right, or top/bottom | ||
| 1566 | unsigned char partition = octantIndexToSharedBitMask[i][j] & | ||
| 1567 | exteriorOcclusionsA & exteriorOcclusionsB; | ||
| 1568 | |||
| 1569 | // Determine which face of each sibling is occluded. | ||
| 1570 | // Note the intentionally crossed indicies. It is necessary because | ||
| 1571 | // the octantIndexToBitMask is a partition occupancy mask. For | ||
| 1572 | // example, if the near-left-top (NLT) and near-left-bottom (NLB) child voxels | ||
| 1573 | // exist, the shared partition is top-bottom (TB), and thus the occluded | ||
| 1574 | // shared face of the NLT voxel is its bottom face. | ||
| 1575 | occludedSharedFace[i] |= (partition & octantIndexToBitMask[j]); | ||
| 1576 | occludedSharedFace[j] |= (partition & octantIndexToBitMask[i]); | ||
| 1577 | } | ||
| 1578 | } | ||
| 1579 | // Combine this voxel's interior excluded shared face only to those children which are coincident | ||
| 1580 | // with the excluded face. | ||
| 1581 | occludedSharedFace[i] |= (voxel->getInteriorOcclusions() & octantIndexToBitMask[i]); | ||
| 1582 | |||
| 1583 | // Inform the child | ||
| 1584 | childA->setInteriorOcclusions(occludedSharedFace[i]); | ||
| 1585 | if (occludedSharedFace[i]) { | ||
| 1586 | //const glm::vec3& v = voxel->getCorner(); | ||
| 1587 | //float s = voxel->getScale(); | ||
| 1588 | |||
| 1589 | //qDebug("Child %d of voxel at %f %f %f size: %f has %02x occlusions", i, v.x, v.y, v.z, s, occludedSharedFace[i]); | ||
| 1590 | } | ||
| 1591 | } | ||
| 1592 | } | ||
| 1593 | return true; | ||
| 1594 | } | ||
| 1595 | |||
| 1596 | bool VoxelSystem::inspectForExteriorOcclusionsOperation(OctreeElement* element, void* extraData) { | ||
| 1597 | _nodeCount++; | ||
| 1598 | VoxelTreeElement* voxel = (VoxelTreeElement*)element; | ||
| 1599 | |||
| 1600 | // Nothing to do at the leaf level | ||
| 1601 | if (voxel->isLeaf()) { | ||
| 1602 | return false; | ||
| 1603 | } | ||
| 1604 | |||
| 1605 | // Count of exterior occluding faces of this voxel element indexed | ||
| 1606 | // by half space partition | ||
| 1607 | unsigned int exteriorOcclusionsCt[6] = { 0, 0, 0, 0, 0, 0 }; | ||
| 1608 | |||
| 1609 | // Traverse all children | ||
| 1610 | for (int i = NUMBER_OF_CHILDREN; --i >= 0; ) { | ||
| 1611 | |||
| 1612 | VoxelTreeElement *child = voxel->getChildAtIndex(i); | ||
| 1613 | if (child) { | ||
| 1614 | // Get the child's occluding faces, for a leaf, that will be | ||
| 1615 | // all six voxel faces, and for a non leaf, that will be | ||
| 1616 | // all faces which are completely covered by four child octants. | ||
| 1617 | unsigned char exteriorOcclusionsOfChild = child->getExteriorOcclusions(); | ||
| 1618 | exteriorOcclusionsOfChild &= octantIndexToBitMask[i]; | ||
| 1619 | |||
| 1620 | for (int j = 6; --j >= 0; ) { | ||
| 1621 | |||
| 1622 | // Determine if the halfspace partition indexed by 1 << j is | ||
| 1623 | // present in the exterior occlusions of the child. | ||
| 1624 | unsigned char partition = exteriorOcclusionsOfChild & (1 << j); | ||
| 1625 | |||
| 1626 | if (partition) { | ||
| 1627 | exteriorOcclusionsCt[j]++; | ||
| 1628 | } | ||
| 1629 | } | ||
| 1630 | } | ||
| 1631 | } | ||
| 1632 | { | ||
| 1633 | // Derive the exterior occlusions of the voxel elements from the exclusions | ||
| 1634 | // of its children | ||
| 1635 | unsigned char exteriorOcclusions = 0; | ||
| 1636 | for (int i = 6; --i >= 0; ) { | ||
| 1637 | if (exteriorOcclusionsCt[i] == 4) { | ||
| 1638 | |||
| 1639 | // Exactly four octants qualify for full exterior occlusion | ||
| 1640 | exteriorOcclusions |= (1 << i); | ||
| 1641 | } | ||
| 1642 | } | ||
| 1643 | |||
| 1644 | // Inform the voxel element | ||
| 1645 | voxel->setExteriorOcclusions(exteriorOcclusions); | ||
| 1646 | |||
| 1647 | if (exteriorOcclusions == OctreeElement::HalfSpace::All) { | ||
| 1648 | //const glm::vec3& v = voxel->getCorner(); | ||
| 1649 | //float s = voxel->getScale(); | ||
| 1650 | |||
| 1651 | //qDebug("Completely occupied voxel at %f %f %f size: %f", v.x, v.y, v.z, s); | ||
| 1652 | |||
| 1653 | // TODO: All of the exterior faces of this voxel element are | ||
| 1654 | // occluders, which means that this element is completely | ||
| 1655 | // occupied. Hence, the subtree from this node could be | ||
| 1656 | // pruned and replaced by a leaf voxel, if the visible | ||
| 1657 | // properties of the children are the same | ||
| 1658 | } | ||
| 1659 | else | ||
| 1660 | if (exteriorOcclusions) { | ||
| 1661 | //const glm::vec3& v = voxel->getCorner(); | ||
| 1662 | //float s = voxel->getScale(); | ||
| 1663 | |||
| 1664 | //qDebug("Partially occupied voxel at %f %f %f size: %f with %02x", v.x, v.y, v.z, s, exteriorOcclusions); | ||
| 1665 | } | ||
| 1666 | } | ||
| 1667 | return true; | ||
| 1668 | } | ||
| 1669 | |||
| 1670 | bool VoxelSystem::clearOcclusionsOperation(OctreeElement* element, void* extraData) { | ||
| 1671 | _nodeCount++; | ||
| 1672 | VoxelTreeElement* voxel = (VoxelTreeElement*)element; | ||
| 1673 | |||
| 1674 | bool rc; | ||
| 1675 | if (voxel->isLeaf()) { | ||
| 1676 | |||
| 1677 | // By definition the the exterior faces of a leaf voxel are | ||
| 1678 | // always occluders. | ||
| 1679 | voxel->setExteriorOcclusions(OctreeElement::HalfSpace::All); | ||
| 1680 | |||
| 1681 | // And the sibling occluders | ||
| 1682 | voxel->setInteriorOcclusions(0); | ||
| 1683 | rc = false; | ||
| 1684 | } | ||
| 1685 | else { | ||
| 1686 | voxel->setExteriorOcclusions(0); | ||
| 1687 | voxel->setInteriorOcclusions(0); | ||
| 1688 | rc = true; | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | return rc; | ||
| 1692 | } | ||
| 1693 | |||
| 1694 | void VoxelSystem::cullSharedFaces() { | ||
| 1695 | _nodeCount = 0; | ||
| 1696 | |||
| 1697 | if (Menu::getInstance()->isOptionChecked(MenuOption::CullSharedFaces)) { | ||
| 1698 | cleanupVoxelMemory(); | ||
| 1699 | _useVoxelShader = false; | ||
| 1700 | _usePrimitiveRenderer = true; | ||
| 1701 | initVoxelMemory(); | ||
| 1702 | clearAllNodesBufferIndex(); | ||
| 1703 | lockTree(); | ||
| 1704 | _tree->recurseTreeWithPostOperation(inspectForExteriorOcclusionsOperation); | ||
| 1705 | _tree->recurseTreeWithPostOperation(inspectForExteriorOcclusionsOperation); | ||
| 1706 | unlockTree(); | ||
| 1707 | |||
| 1708 | _nodeCount = 0; | ||
| 1709 | lockTree(); | ||
| 1710 | _tree->recurseTreeWithOperation(inspectForInteriorOcclusionsOperation); | ||
| 1711 | unlockTree(); | ||
| 1712 | qDebug("culling shared faces in %d nodes", _nodeCount); | ||
| 1713 | } | ||
| 1714 | else { | ||
| 1715 | cleanupVoxelMemory(); | ||
| 1716 | _usePrimitiveRenderer = false; | ||
| 1717 | initVoxelMemory(); | ||
| 1718 | clearAllNodesBufferIndex(); | ||
| 1719 | lockTree(); | ||
| 1720 | _tree->recurseTreeWithOperation(clearOcclusionsOperation); | ||
| 1721 | unlockTree(); | ||
| 1722 | |||
| 1723 | |||
| 1724 | qDebug("unculling shared faces in %d nodes", _nodeCount); | ||
| 1725 | } | ||
| 1726 | _writeRenderFullVBO = true; | ||
| 1727 | _tree->setDirtyBit(); | ||
| 1728 | setupNewVoxelsForDrawing(); | ||
| 1729 | |||
| 1730 | } | ||
| 1731 | |||
| 1427 | bool VoxelSystem::forceRedrawEntireTreeOperation(OctreeElement* element, void* extraData) { | 1732 | bool VoxelSystem::forceRedrawEntireTreeOperation(OctreeElement* element, void* extraData) { |
| 1428 | _nodeCount++; | 1733 | _nodeCount++; |
| 1429 | element->setDirtyBit(); | 1734 | element->setDirtyBit(); |
| @@ -1997,7 +2302,7 @@ bool VoxelSystem::hideAllSubTreeOperation(OctreeElement* element, void* extraDat · 567 unmodified lines | |||
| 1997 | } | 2302 | } |
| 1998 | 2303 | ||
| 1999 | args->nodesOutside++; | 2304 | args->nodesOutside++; |
| 2000 | if (voxel->isKnownBufferIndex()) { | 2305 | if (voxel->isKnownBufferIndex() || voxel->getPrimitiveIndex()) { |
| 2001 | args->nodesRemoved++; | 2306 | args->nodesRemoved++; |
| 2002 | bool falseColorize = false; | 2307 | bool falseColorize = false; |
| 2003 | if (falseColorize) { | 2308 | if (falseColorize) { |
| @@ -2039,7 +2344,7 @@ bool VoxelSystem::showAllSubTreeOperation(OctreeElement* element, void* extraDat · 35 unmodified lines | |||
| 2039 | bool shouldRender = voxel->calculateShouldRender(&args->thisViewFrustum, voxelSizeScale, boundaryLevelAdjust); | 2344 | bool shouldRender = voxel->calculateShouldRender(&args->thisViewFrustum, voxelSizeScale, boundaryLevelAdjust); |
| 2040 | voxel->setShouldRender(shouldRender); | 2345 | voxel->setShouldRender(shouldRender); |
| 2041 | 2346 | ||
| 2042 | if (shouldRender && !voxel->isKnownBufferIndex()) { | 2347 | if (shouldRender && !(voxel->isKnownBufferIndex() || voxel->getPrimitiveIndex())) { |
| 2043 | bool falseColorize = false; | 2348 | bool falseColorize = false; |
| 2044 | if (falseColorize) { | 2349 | if (falseColorize) { |
| 2045 | voxel->setFalseColor(0,0,255); // false colorize | 2350 | voxel->setFalseColor(0,0,255); // false colorize |
| @@ -2047,6 +2352,12 @@ bool VoxelSystem::showAllSubTreeOperation(OctreeElement* element, void* extraDat · 1 unmodified line | |||
| 2047 | // These are both needed to force redraw... | 2352 | // These are both needed to force redraw... |
| 2048 | voxel->setDirtyBit(); | 2353 | voxel->setDirtyBit(); |
| 2049 | voxel->markWithChangedTime(); | 2354 | voxel->markWithChangedTime(); |
| 2355 | // and this? | ||
| 2356 | { | ||
| 2357 | VoxelSystem* thisVoxelSystem = args->thisVoxelSystem; | ||
| 2358 | thisVoxelSystem->_voxelsUpdated += thisVoxelSystem->updateNodeInArrays(voxel, true, true); | ||
| 2359 | thisVoxelSystem->setupNewVoxelsForDrawingSingleNode(); | ||
| 2360 | } | ||
| 2050 | args->nodesShown++; | 2361 | args->nodesShown++; |
| 2051 | } | 2362 | } |
| 2052 | 2363 | ||
| @@ -2128,7 +2439,7 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData · 75 unmodified lines | |||
| 2128 | // if the child node INTERSECTs the view, then we want to check to see if it thinks it should render | 2439 | // if the child node INTERSECTs the view, then we want to check to see if it thinks it should render |
| 2129 | // if it should render but is missing it's VBO index, then we want to flip it on, and we can stop recursing from | 2440 | // if it should render but is missing it's VBO index, then we want to flip it on, and we can stop recursing from |
| 2130 | // here because we know will block any children anyway | 2441 | // here because we know will block any children anyway |
| 2131 | if (voxel->getShouldRender() && !voxel->isKnownBufferIndex()) { | 2442 | if (voxel->getShouldRender() && !(voxel->isKnownBufferIndex() || voxel->getPrimitiveIndex())) { |
| 2132 | voxel->setDirtyBit(); // will this make it draw? | 2443 | voxel->setDirtyBit(); // will this make it draw? |
| 2133 | args->nodesShown++; | 2444 | args->nodesShown++; |
| 2134 | return false; | 2445 | return false; |
| @@ -2225,7 +2536,9 @@ public: · 90 unmodified lines | |||
| 2225 | nodesInVBONotShouldRender(0), | 2536 | nodesInVBONotShouldRender(0), |
| 2226 | nodesInVBOOverExpectedMax(0), | 2537 | nodesInVBOOverExpectedMax(0), |
| 2227 | duplicateVBOIndex(0), | 2538 | duplicateVBOIndex(0), |
| 2228 | leafNodes(0) | 2539 | leafNodes(0), |
| 2540 | culledLeafNodes(0), | ||
| 2541 | nodesInPrimitiveRenderer(0) | ||
| 2229 | { | 2542 | { |
| 2230 | hasIndexFound = new bool[maxVoxels]; | 2543 | hasIndexFound = new bool[maxVoxels]; |
| 2231 | memset(hasIndexFound, false, maxVoxels * sizeof(bool)); | 2544 | memset(hasIndexFound, false, maxVoxels * sizeof(bool)); |
| @@ -2244,6 +2557,8 @@ public: · 12 unmodified lines | |||
| 2244 | unsigned long nodesInVBOOverExpectedMax; | 2557 | unsigned long nodesInVBOOverExpectedMax; |
| 2245 | unsigned long duplicateVBOIndex; | 2558 | unsigned long duplicateVBOIndex; |
| 2246 | unsigned long leafNodes; | 2559 | unsigned long leafNodes; |
| 2560 | unsigned long culledLeafNodes; ///< Number of completely culled nodes because of face sharing | ||
| 2561 | unsigned long nodesInPrimitiveRenderer; | ||
| 2247 | 2562 | ||
| 2248 | unsigned long expectedMax; | 2563 | unsigned long expectedMax; |
| 2249 | 2564 | ||
| @@ -2258,6 +2573,9 @@ bool VoxelSystem::collectStatsForTreesAndVBOsOperation(OctreeElement* element, v · 8 unmodified lines | |||
| 2258 | 2573 | ||
| 2259 | if (voxel->isLeaf()) { | 2574 | if (voxel->isLeaf()) { |
| 2260 | args->leafNodes++; | 2575 | args->leafNodes++; |
| 2576 | if (voxel->getInteriorOcclusions() == OctreeElement::HalfSpace::All) { | ||
| 2577 | args->culledLeafNodes++; | ||
| 2578 | } | ||
| 2261 | } | 2579 | } |
| 2262 | 2580 | ||
| 2263 | if (voxel->isColored()) { | 2581 | if (voxel->isColored()) { |
| @@ -2272,15 +2590,25 @@ bool VoxelSystem::collectStatsForTreesAndVBOsOperation(OctreeElement* element, v · 8 unmodified lines | |||
| 2272 | args->dirtyNodes++; | 2590 | args->dirtyNodes++; |
| 2273 | } | 2591 | } |
| 2274 | 2592 | ||
| 2275 | if (voxel->isKnownBufferIndex()) { | 2593 | unsigned long nodeIndex = 0; |
| 2276 | args->nodesInVBO++; | 2594 | if (voxel->isKnownBufferIndex()) { |
| 2277 | unsigned long nodeIndex = voxel->getBufferIndex(); | 2595 | args->nodesInVBO++; |
| 2596 | nodeIndex = voxel->getBufferIndex(); | ||
| 2597 | } | ||
| 2598 | |||
| 2599 | if (voxel->getPrimitiveIndex()) { | ||
| 2600 | args->nodesInPrimitiveRenderer++; | ||
| 2601 | nodeIndex = voxel->getPrimitiveIndex(); | ||
| 2602 | } | ||
| 2603 | |||
| 2604 | if (voxel->isKnownBufferIndex() || (voxel->getPrimitiveIndex() != 0)) { | ||
| 2278 | 2605 | ||
| 2279 | const bool extraDebugging = false; // enable for extra debugging | 2606 | const bool extraDebugging = false; // enable for extra debugging |
| 2280 | if (extraDebugging) { | 2607 | if (extraDebugging) { |
| 2281 | qDebug("node In VBO... [%f,%f,%f] %f ... index=%ld, isDirty=%s, shouldRender=%s", | 2608 | qDebug("node In VBO... [%f,%f,%f] %f ... index=%ld, isDirty=%s, shouldRender=%s, culledFaces=0x%02x \n", |
| 2282 | voxel->getCorner().x, voxel->getCorner().y, voxel->getCorner().z, voxel->getScale(), | 2609 | voxel->getCorner().x, voxel->getCorner().y, voxel->getCorner().z, voxel->getScale(), |
| 2283 | nodeIndex, debug::valueOf(voxel->isDirty()), debug::valueOf(voxel->getShouldRender())); | 2610 | nodeIndex, debug::valueOf(voxel->isDirty()), debug::valueOf(voxel->getShouldRender()), |
| 2611 | voxel->getInteriorOcclusions()); | ||
| 2284 | } | 2612 | } |
| 2285 | 2613 | ||
| 2286 | if (args->hasIndexFound[nodeIndex]) { | 2614 | if (args->hasIndexFound[nodeIndex]) { |
| @@ -2309,12 +2637,14 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { · 22 unmodified lines | |||
| 2309 | glBufferIndex minDirty = GLBUFFER_INDEX_UNKNOWN; | 2637 | glBufferIndex minDirty = GLBUFFER_INDEX_UNKNOWN; |
| 2310 | glBufferIndex maxDirty = 0; | 2638 | glBufferIndex maxDirty = 0; |
| 2311 | 2639 | ||
| 2312 | for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) { | 2640 | if (!_usePrimitiveRenderer) { |
| 2313 | if (_writeVoxelDirtyArray[i]) { | 2641 | for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) { |
| 2314 | minDirty = std::min(minDirty,i); | 2642 | if (_writeVoxelDirtyArray[i]) { |
| 2315 | maxDirty = std::max(maxDirty,i); | 2643 | minDirty = std::min(minDirty,i); |
| 2316 | } | 2644 | maxDirty = std::max(maxDirty,i); |
| 2317 | } | 2645 | } |
| 2646 | } | ||
| 2647 | } | ||
| 2318 | 2648 | ||
| 2319 | collectStatsForTreesAndVBOsArgs args(_maxVoxels); | 2649 | collectStatsForTreesAndVBOsArgs args(_maxVoxels); |
| 2320 | args.expectedMax = _voxelsInWriteArrays; | 2650 | args.expectedMax = _voxelsInWriteArrays; |
| @@ -2323,7 +2653,7 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { · 2 unmodified lines | |||
| 2323 | 2653 | ||
| 2324 | _tree->recurseTreeWithOperation(collectStatsForTreesAndVBOsOperation,&args); | 2654 | _tree->recurseTreeWithOperation(collectStatsForTreesAndVBOsOperation,&args); |
| 2325 | 2655 | ||
| 2326 | qDebug("Local Voxel Tree Statistics:\n total nodes %ld \n leaves %ld \n dirty %ld \n colored %ld \n shouldRender %ld", | 2656 | qDebug("Local Voxel Tree Statistics:\n total nodes %ld \n leaves %ld \n dirty %ld \n colored %ld \n shouldRender %ld \n", |
| 2327 | args.totalNodes, args.leafNodes, args.dirtyNodes, args.coloredNodes, args.shouldRenderNodes); | 2657 | args.totalNodes, args.leafNodes, args.dirtyNodes, args.coloredNodes, args.shouldRenderNodes); |
| 2328 | 2658 | ||
| 2329 | qDebug(" _voxelsDirty=%s \n _voxelsInWriteArrays=%ld \n minDirty=%ld \n maxDirty=%ld", debug::valueOf(_voxelsDirty), | 2659 | qDebug(" _voxelsDirty=%s \n _voxelsInWriteArrays=%ld \n minDirty=%ld \n maxDirty=%ld", debug::valueOf(_voxelsDirty), |
| @@ -2332,6 +2662,9 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { · 2 unmodified lines | |||
| 2332 | qDebug(" inVBO %ld \n nodesInVBOOverExpectedMax %ld \n duplicateVBOIndex %ld \n nodesInVBONotShouldRender %ld", | 2662 | qDebug(" inVBO %ld \n nodesInVBOOverExpectedMax %ld \n duplicateVBOIndex %ld \n nodesInVBONotShouldRender %ld", |
| 2333 | args.nodesInVBO, args.nodesInVBOOverExpectedMax, args.duplicateVBOIndex, args.nodesInVBONotShouldRender); | 2663 | args.nodesInVBO, args.nodesInVBOOverExpectedMax, args.duplicateVBOIndex, args.nodesInVBONotShouldRender); |
| 2334 | 2664 | ||
| 2665 | qDebug(" inPrimitiveRenderer %ld \n completely culled %ld \n", | ||
| 2666 | args.nodesInPrimitiveRenderer, args.culledLeafNodes); | ||
| 2667 | |||
| 2335 | glBufferIndex minInVBO = GLBUFFER_INDEX_UNKNOWN; | 2668 | glBufferIndex minInVBO = GLBUFFER_INDEX_UNKNOWN; |
| 2336 | glBufferIndex maxInVBO = 0; | 2669 | glBufferIndex maxInVBO = 0; |
| 2337 | 2670 | ||
| @@ -2775,5 +3108,102 @@ void VoxelSystem::beginLoadingLocalVoxelCache() { · 437 unmodified lines | |||
| 2775 | qDebug() << "DONE beginLoadingLocalVoxelCache()"; | 3108 | qDebug() << "DONE beginLoadingLocalVoxelCache()"; |
| 2776 | } | 3109 | } |
| 2777 | 3110 | ||
| 3111 | // Octant bitmask array indexed by octant. The mask value indicates the octant's halfspace partitioning. The index | ||
| 3112 | // value corresponds to the voxel's octal code derived in "pointToVoxel" in SharedUtil.cpp, which, BTW, does *not* | ||
| 3113 | // correspond to the "ChildIndex" enum value in OctreeElement.h | ||
| 3114 | unsigned char VoxelSystem::octantIndexToBitMask[8] = { | ||
| 3115 | OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Left | OctreeElement::HalfSpace::Near, | ||
| 3116 | OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Left | OctreeElement::HalfSpace::Far, | ||
| 3117 | OctreeElement::HalfSpace::Top | OctreeElement::HalfSpace::Left | OctreeElement::HalfSpace::Near, | ||
| 3118 | OctreeElement::HalfSpace::Top | OctreeElement::HalfSpace::Left | OctreeElement::HalfSpace::Far, | ||
| 3119 | OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Near, | ||
| 3120 | OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Far, | ||
| 3121 | OctreeElement::HalfSpace::Top | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Near, | ||
| 3122 | OctreeElement::HalfSpace::Top | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Far, | ||
| 3123 | }; | ||
| 2778 | 3124 | ||
| 3125 | // Two dimensional array map indexed by octant row and column. The mask value | ||
| 3126 | // indicates the two faces shared by the octants | ||
| 3127 | unsigned char VoxelSystem::octantIndexToSharedBitMask[8][8] = { | ||
| 3128 | { // Index 0: Bottom-Left-Near | ||
| 3129 | 0, // Bottom-Left-Near | ||
| 3130 | OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Bottom-Left-Far | ||
| 3131 | OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Top-Left-Near | ||
| 3132 | 0, // Top-Left-Far | ||
| 3133 | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Bottom-Right-Near | ||
| 3134 | 0, // Bottom-Right-Far | ||
| 3135 | 0, // Top-Right-Near | ||
| 3136 | 0, // Top-Right-Far | ||
| 3137 | }, | ||
| 3138 | { // Index 1: Bottom-Left-Far | ||
| 3139 | OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Bottom-Left-Near | ||
| 3140 | 0, // Bottom-Left-Far | ||
| 3141 | 0, // Top-Left-Near | ||
| 3142 | OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Top-Left-Far | ||
| 3143 | 0, // Bottom-Right-Near | ||
| 3144 | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Bottom-Right-Far | ||
| 3145 | 0, // Top-Right-Near | ||
| 3146 | 0, // Top-Right-Far | ||
| 3147 | }, | ||
| 3148 | { // Index 2: Top-Left-Near | ||
| 3149 | OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Bottom-Left-Near | ||
| 3150 | 0, // Bottom-Left-Far | ||
| 3151 | 0, // Top-Left-Near | ||
| 3152 | OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Top-Left-Far | ||
| 3153 | 0, // Bottom-Right-Near | ||
| 3154 | 0, // Bottom-Right-Far | ||
| 3155 | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Top-Right-Near | ||
| 3156 | 0, // Top-Right-Far | ||
| 3157 | }, | ||
| 3158 | { // Index 3: Top-Left-Far | ||
| 3159 | 0, // Bottom-Left-Near | ||
| 3160 | OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Bottom-Left-Far | ||
| 3161 | OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Top-Left-Near | ||
| 3162 | 0, // Top-Left-Far | ||
| 3163 | 0, // Bottom-Right-Near | ||
| 3164 | 0, // Bottom-Right-Far | ||
| 3165 | 0, // Top-Right-Near | ||
| 3166 | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Top-Right-Far | ||
| 3167 | }, | ||
| 3168 | { // Index 4: Bottom-Right-Near | ||
| 3169 | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Bottom-Left-Near | ||
| 3170 | 0, // Bottom-Left-Far | ||
| 3171 | 0, // Top-Left-Near | ||
| 3172 | 0, // Top-Left-Far | ||
| 3173 | 0, // Bottom-Right-Near | ||
| 3174 | OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Bottom-Right-Far | ||
| 3175 | OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Top-Right-Near | ||
| 3176 | 0, // Top-Right-Far | ||
| 3177 | }, | ||
| 3178 | { // Index 5: Bottom-Right-Far | ||
| 3179 | 0, // Bottom-Left-Near | ||
| 3180 | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Bottom-Left-Far | ||
| 3181 | 0, // Top-Left-Near | ||
| 3182 | 0, // Top-Left-Far | ||
| 3183 | OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Bottom-Right-Near | ||
| 3184 | 0, // Bottom-Right-Far | ||
| 3185 | 0, // Top-Right-Near | ||
| 3186 | OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Top-Right-Far | ||
| 3187 | }, | ||
| 3188 | { // Index 6: Top-Right-Near | ||
| 3189 | 0, // Bottom-Left-Near | ||
| 3190 | 0, // Bottom-Left-Far | ||
| 3191 | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Top-Left-Near | ||
| 3192 | 0, // Top-Left-Far | ||
| 3193 | OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Bottom-Right-Near | ||
| 3194 | 0, // Bottom-Right-Far | ||
| 3195 | 0, // Top-Right-Near | ||
| 3196 | OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Top-Right-Far | ||
| 3197 | }, | ||
| 3198 | { // Index 7: Top-Right-Far | ||
| 3199 | 0, // Bottom-Left-Near | ||
| 3200 | 0, // Bottom-Left-Far | ||
| 3201 | 0, // Top-Left-Near | ||
| 3202 | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Top-Left-Far | ||
| 3203 | 0, // Bottom-Right-Near | ||
| 3204 | OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Bottom-Right-Far | ||
| 3205 | OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Top-Right-Near | ||
| 3206 | 0, // Top-Right-Far | ||
| 3207 | }, | ||
| 3208 | }; | ||
| 2779 | 3209 | ||