History

Completed worklist job #19486

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.

matsukaze committed 12 years ago

Commit bbf4f881 parent d19cb6d16 files · +2346 −94Browse files

Showing the changes to interface/src/VoxelSystem.cpp (1 of 16 files). Show all

Unified · Split

interface/src/VoxelSystem.cpp+518−88
@@ -60,7 +60,9 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) · 59 unmodified lines
6060 : NodeData(),
6161 _treeScale(treeScale),
6262 _maxVoxels(maxVoxels),
63− _initialized(false) {
63+ _initialized(false),
64+ _usePrimitiveRenderer(false),
65+ _renderer(0) {
6466
6567 _voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0;
6668 _writeRenderFullVBO = true;
@@ -102,12 +104,13 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) · 35 unmodified lines
102104 _inhideOutOfView = false;
103105 _treeIsBusy = false;
104106
107+
105108 }
106109
107110 void VoxelSystem::elementDeleted(OctreeElement* element) {
108111 VoxelTreeElement* voxel = (VoxelTreeElement*)element;
109112 if (voxel->getVoxelSystem() == this) {
110− if (_voxelsInWriteArrays != 0) {
113+ if ((_voxelsInWriteArrays != 0) || _usePrimitiveRenderer) {
111114 forceRemoveNodeFromArrays(voxel);
112115 } else {
113116 if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) {
@@ -212,16 +215,16 @@ void VoxelSystem::freeBufferIndex(glBufferIndex index) { · 98 unmodified lines
212215 }
213216 }
214217 if (!inList) {
215− // make the index available for next node that needs to be drawn
216− _freeIndexLock.lock();
217− _freeIndexes.push_back(index);
218− _freeIndexLock.unlock();
218+ // make the index available for next node that needs to be drawn
219+ _freeIndexLock.lock();
220+ _freeIndexes.push_back(index);
221+ _freeIndexLock.unlock();
219222
220− // make the VBO slot "invisible" in case this slot is not used
221− const glm::vec3 startVertex(FLT_MAX, FLT_MAX, FLT_MAX);
222− const float voxelScale = 0;
223− const nodeColor BLACK = {0, 0, 0, 0};
224− updateArraysDetails(index, startVertex, voxelScale, BLACK);
223+ // make the VBO slot "invisible" in case this slot is not used
224+ const glm::vec3 startVertex(FLT_MAX, FLT_MAX, FLT_MAX);
225+ const float voxelScale = 0;
226+ const nodeColor BLACK = {0, 0, 0, 0};
227+ updateArraysDetails(index, startVertex, voxelScale, BLACK);
225228 }
226229 }
227230
@@ -250,6 +253,8 @@ VoxelSystem::~VoxelSystem() { · 22 unmodified lines
250253
251254 cleanupVoxelMemory();
252255 delete _tree;
256+
257+ _renderer = 0;
253258 }
254259
255260 void VoxelSystem::setMaxVoxels(int maxVoxels) {
@@ -258,12 +263,12 @@ void VoxelSystem::setMaxVoxels(int maxVoxels) { · 2 unmodified lines
258263 }
259264 bool wasInitialized = _initialized;
260265 if (wasInitialized) {
261− clearAllNodesBufferIndex();
262− cleanupVoxelMemory();
266+ clearAllNodesBufferIndex();
267+ cleanupVoxelMemory();
263268 }
264269 _maxVoxels = maxVoxels;
265270 if (wasInitialized) {
266− initVoxelMemory();
271+ initVoxelMemory();
267272 }
268273 if (wasInitialized) {
269274 forceRedrawEntireTree();
@@ -281,6 +286,7 @@ void VoxelSystem::setUseVoxelShader(bool useVoxelShader) { · 11 unmodified lines
281286 cleanupVoxelMemory();
282287 }
283288 _useVoxelShader = useVoxelShader;
289+ _usePrimitiveRenderer = false;
284290 if (wasInitialized) {
285291 initVoxelMemory();
286292 }
@@ -345,7 +351,8 @@ void VoxelSystem::cleanupVoxelMemory() { · 58 unmodified lines
345351
346352 _writeVoxelShaderData = _readVoxelShaderData = NULL;
347353
348− } else {
354+ } else
355+ if (! _usePrimitiveRenderer) {
349356 // Destroy glBuffers
350357 glDeleteBuffers(1, &_vboVerticesID);
351358 glDeleteBuffers(1, &_vboColorsID);
@@ -366,8 +373,11 @@ void VoxelSystem::cleanupVoxelMemory() { · 14 unmodified lines
366373 _writeVerticesArray = NULL;
367374 _readColorsArray = NULL;
368375 _writeColorsArray = NULL;
369−
370376 }
377+ else {
378+ delete _renderer;
379+ _renderer = 0;
380+ }
371381 delete[] _writeVoxelDirtyArray;
372382 delete[] _readVoxelDirtyArray;
373383 _writeVoxelDirtyArray = _readVoxelDirtyArray = NULL;
@@ -452,7 +462,8 @@ void VoxelSystem::initVoxelMemory() { · 78 unmodified lines
452462
453463 _readVoxelShaderData = new VoxelShaderVBOData[_maxVoxels];
454464 _memoryUsageRAM += (sizeof(VoxelShaderVBOData) * _maxVoxels);
455− } else {
465+ } else
466+ if (! _usePrimitiveRenderer) {
456467
457468 // Global Normals mode uses a technique of not including normals on any voxel vertices, and instead
458469 // rendering the voxel faces in 6 passes that use a global call to glNormal3f()
@@ -496,7 +507,6 @@ void VoxelSystem::initVoxelMemory() { · 37 unmodified lines
496507 _readColorsArray = new GLubyte[vertexPointsPerVoxel * _maxVoxels];
497508 _memoryUsageRAM += (sizeof(GLubyte) * vertexPointsPerVoxel * _maxVoxels);
498509
499−
500510 // create our simple fragment shader if we're the first system to init
501511 if (!_perlinModulateProgram.isLinked()) {
502512 switchToResourcesParentIfRequired();
@@ -516,6 +526,27 @@ void VoxelSystem::initVoxelMemory() { · 13 unmodified lines
516526 _shadowMapProgram.release();
517527 }
518528 }
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+ }
519550
520551 _initialized = true;
521552
@@ -655,8 +686,15 @@ void VoxelSystem::setupNewVoxelsForDrawing() { · 133 unmodified lines
655686 };
656687 PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer);
657688 _callsToTreesToArrays++;
689+
658690 if (_writeRenderFullVBO) {
659− clearFreeBufferIndexes();
691+ if (_usePrimitiveRenderer) {
692+ delete _renderer;
693+ _renderer = new PrimitiveRenderer(_maxVoxels);
694+ }
695+ else {
696+ clearFreeBufferIndexes();
697+ }
660698 }
661699 _voxelsUpdated = newTreeToArrays(_tree->getRoot());
662700 _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
672710 _voxelsUpdated = 0;
673711 }
674712
675− // lock on the buffer write lock so we can't modify the data when the GPU is reading it
676− _bufferWriteLock.lock();
713+ if (_usePrimitiveRenderer) {
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();
677721
678− if (_voxelsUpdated) {
679− _voxelsDirty=true;
680− }
722+ if (_voxelsUpdated) {
723+ _voxelsDirty=true;
724+ }
681725
682− // copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated
683− copyWrittenDataToReadArrays(didWriteFullVBO);
726+ // copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated
727+ copyWrittenDataToReadArrays(didWriteFullVBO);
684728
685− _bufferWriteLock.unlock();
729+ _bufferWriteLock.unlock();
730+ }
686731
687732 uint64_t end = usecTimestampNow();
688733 int elapsedmsec = (end - start) / 1000;
@@ -710,23 +755,28 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { · 21 unmodified lines
710755 return; // bail early, it hasn't been long enough since the last time we ran
711756 }
712757
713− // lock on the buffer write lock so we can't modify the data when the GPU is reading it
714− {
715− PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
716− "setupNewVoxelsForDrawingSingleNode()... _bufferWriteLock.lock();" );
717− _bufferWriteLock.lock();
718− }
719−
720− _voxelsDirty = true; // if we got this far, then we can assume some voxels are dirty
758+ if (_usePrimitiveRenderer) {
759+ _voxelsDirty = true; // if we got this far, then we can assume some voxels are dirty
760+ _voxelsUpdated = 0;
761+ }
762+ else {
763+ // lock on the buffer write lock so we can't modify the data when the GPU is reading it
764+ {
765+ PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
766+ "setupNewVoxelsForDrawingSingleNode()... _bufferWriteLock.lock();" );
767+ _bufferWriteLock.lock();
768+ }
721769
722− // copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated
723− copyWrittenDataToReadArrays(_writeRenderFullVBO);
770+ _voxelsDirty = true; // if we got this far, then we can assume some voxels are dirty
724771
725− // after...
726− _voxelsUpdated = 0;
772+ // copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated
773+ copyWrittenDataToReadArrays(_writeRenderFullVBO);
727774
728− _bufferWriteLock.unlock();
775+ // after...
776+ _voxelsUpdated = 0;
729777
778+ _bufferWriteLock.unlock();
779+ }
730780 uint64_t end = usecTimestampNow();
731781 int elapsedmsec = (end - start) / 1000;
732782 _setupNewVoxelsForDrawingLastFinished = end;
@@ -946,14 +996,24 @@ int VoxelSystem::forceRemoveNodeFromArrays(VoxelTreeElement* node) { · 213 unmodified lines
946996 return 0;
947997 }
948998
949− // if the node is not in the VBOs then we have nothing to do!
950− if (node->isKnownBufferIndex()) {
951− // If this node has not yet been written to the array, then add it to the end of the array.
952− glBufferIndex nodeIndex = node->getBufferIndex();
953− node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
954− freeBufferIndex(nodeIndex); // NOTE: This will make the node invisible!
955− return 1; // updated!
956− }
999+ if (_usePrimitiveRenderer) {
1000+ int primitiveIndex = node->getPrimitiveIndex();
1001+ if (primitiveIndex) {
1002+ _renderer->remove(primitiveIndex);
1003+ node->setPrimitiveIndex(0);
1004+ return 1;
1005+ }
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+ }
9571017 return 0; // not-updated
9581018 }
9591019
@@ -980,20 +1040,46 @@ int VoxelSystem::updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, boo · 20 unmodified lines
9801040 if (forceDraw || node->isDirty()) {
9811041 // If we're should render, use our legit location and scale,
9821042 if (node->getShouldRender()) {
983− glm::vec3 startVertex = node->getCorner();
984− float voxelScale = node->getScale();
985−
986− glBufferIndex nodeIndex = GLBUFFER_INDEX_UNKNOWN;
987− if (reuseIndex && node->isKnownBufferIndex()) {
988− nodeIndex = node->getBufferIndex();
989− } else {
990− nodeIndex = getNextBufferIndex();
991− node->setBufferIndex(nodeIndex);
992− node->setVoxelSystem(this);
993− }
994− // populate the array with points for the 8 vertices and RGB color for each added vertex
995− updateArraysDetails(nodeIndex, startVertex, voxelScale, node->getColor());
996− return 1; // updated!
1043+ glm::vec3 startVertex = node->getCorner();
1044+ float voxelScale = node->getScale();
1045+ nodeColor const & color = node->getColor();
1046+
1047+ if (_usePrimitiveRenderer) {
1048+ int primitiveIndex = node->getPrimitiveIndex();
1049+ if (primitiveIndex) {
1050+ _renderer->remove(primitiveIndex);
1051+ node->setPrimitiveIndex(0);
1052+ }
1053+ else {
1054+ node->setVoxelSystem(this);
1055+ }
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!
9971083 } else {
9981084 // If we shouldn't render, and we're in reuseIndex mode, then free our index, this only operates
9991085 // 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
11401226 };
11411227 // would like to include _callsToTreesToArrays
11421228 PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer);
1143− if (_voxelsDirty) {
1144− if (_readRenderFullVBO) {
1145− updateFullVBOs();
1146− } else {
1147− updatePartialVBOs();
1148− }
1149− _voxelsDirty = false;
1150− _readRenderFullVBO = false;
1151− }
1229+ if (! _usePrimitiveRenderer) {
1230+ if (_voxelsDirty) {
1231+ if (_readRenderFullVBO) {
1232+ updateFullVBOs();
1233+ } else {
1234+ updatePartialVBOs();
1235+ }
1236+ _voxelsDirty = false;
1237+ _readRenderFullVBO = false;
1238+ }
1239+ }
11521240 _callsToTreesToArrays = 0; // clear it
11531241 }
11541242
@@ -1274,7 +1362,8 @@ void VoxelSystem::render(bool texture) { · 119 unmodified lines
12741362 glDisableVertexAttribArray(attributeLocation);
12751363 glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
12761364 }
1277− } else {
1365+ } else
1366+ if (!_usePrimitiveRenderer) {
12781367 PerformanceWarning warn(showWarnings, "render().. TRIANGLES...");
12791368
12801369 {
@@ -1348,6 +1437,12 @@ void VoxelSystem::render(bool texture) { · 67 unmodified lines
13481437 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
13491438 }
13501439 }
1440+ else {
1441+ applyScaleAndBindProgram(texture);
1442+ _renderer->render();
1443+ removeScaleAndReleaseProgram(texture);
1444+
1445+ }
13511446 }
13521447
13531448 void VoxelSystem::applyScaleAndBindProgram(bool texture) {
@@ -1411,6 +1506,7 @@ bool VoxelSystem::clearAllNodesBufferIndexOperation(OctreeElement* element, void · 57 unmodified lines
14111506 _nodeCount++;
14121507 VoxelTreeElement* voxel = (VoxelTreeElement*)element;
14131508 voxel->setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
1509+ voxel->setPrimitiveIndex(0);
14141510 return true;
14151511 }
14161512
@@ -1424,6 +1520,215 @@ void VoxelSystem::clearAllNodesBufferIndex() { · 7 unmodified lines
14241520 }
14251521 }
14261522
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+
14271732 bool VoxelSystem::forceRedrawEntireTreeOperation(OctreeElement* element, void* extraData) {
14281733 _nodeCount++;
14291734 element->setDirtyBit();
@@ -1997,7 +2302,7 @@ bool VoxelSystem::hideAllSubTreeOperation(OctreeElement* element, void* extraDat · 567 unmodified lines
19972302 }
19982303
19992304 args->nodesOutside++;
2000− if (voxel->isKnownBufferIndex()) {
2305+ if (voxel->isKnownBufferIndex() || voxel->getPrimitiveIndex()) {
20012306 args->nodesRemoved++;
20022307 bool falseColorize = false;
20032308 if (falseColorize) {
@@ -2039,7 +2344,7 @@ bool VoxelSystem::showAllSubTreeOperation(OctreeElement* element, void* extraDat · 35 unmodified lines
20392344 bool shouldRender = voxel->calculateShouldRender(&args->thisViewFrustum, voxelSizeScale, boundaryLevelAdjust);
20402345 voxel->setShouldRender(shouldRender);
20412346
2042− if (shouldRender && !voxel->isKnownBufferIndex()) {
2347+ if (shouldRender && !(voxel->isKnownBufferIndex() || voxel->getPrimitiveIndex())) {
20432348 bool falseColorize = false;
20442349 if (falseColorize) {
20452350 voxel->setFalseColor(0,0,255); // false colorize
@@ -2047,6 +2352,12 @@ bool VoxelSystem::showAllSubTreeOperation(OctreeElement* element, void* extraDat · 1 unmodified line
20472352 // These are both needed to force redraw...
20482353 voxel->setDirtyBit();
20492354 voxel->markWithChangedTime();
2355+ // and this?
2356+ {
2357+ VoxelSystem* thisVoxelSystem = args->thisVoxelSystem;
2358+ thisVoxelSystem->_voxelsUpdated += thisVoxelSystem->updateNodeInArrays(voxel, true, true);
2359+ thisVoxelSystem->setupNewVoxelsForDrawingSingleNode();
2360+ }
20502361 args->nodesShown++;
20512362 }
20522363
@@ -2128,7 +2439,7 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData · 75 unmodified lines
21282439 // if the child node INTERSECTs the view, then we want to check to see if it thinks it should render
21292440 // if it should render but is missing it's VBO index, then we want to flip it on, and we can stop recursing from
21302441 // here because we know will block any children anyway
2131− if (voxel->getShouldRender() && !voxel->isKnownBufferIndex()) {
2442+ if (voxel->getShouldRender() && !(voxel->isKnownBufferIndex() || voxel->getPrimitiveIndex())) {
21322443 voxel->setDirtyBit(); // will this make it draw?
21332444 args->nodesShown++;
21342445 return false;
@@ -2225,7 +2536,9 @@ public: · 90 unmodified lines
22252536 nodesInVBONotShouldRender(0),
22262537 nodesInVBOOverExpectedMax(0),
22272538 duplicateVBOIndex(0),
2228− leafNodes(0)
2539+ leafNodes(0),
2540+ culledLeafNodes(0),
2541+ nodesInPrimitiveRenderer(0)
22292542 {
22302543 hasIndexFound = new bool[maxVoxels];
22312544 memset(hasIndexFound, false, maxVoxels * sizeof(bool));
@@ -2244,6 +2557,8 @@ public: · 12 unmodified lines
22442557 unsigned long nodesInVBOOverExpectedMax;
22452558 unsigned long duplicateVBOIndex;
22462559 unsigned long leafNodes;
2560+ unsigned long culledLeafNodes; ///< Number of completely culled nodes because of face sharing
2561+ unsigned long nodesInPrimitiveRenderer;
22472562
22482563 unsigned long expectedMax;
22492564
@@ -2258,6 +2573,9 @@ bool VoxelSystem::collectStatsForTreesAndVBOsOperation(OctreeElement* element, v · 8 unmodified lines
22582573
22592574 if (voxel->isLeaf()) {
22602575 args->leafNodes++;
2576+ if (voxel->getInteriorOcclusions() == OctreeElement::HalfSpace::All) {
2577+ args->culledLeafNodes++;
2578+ }
22612579 }
22622580
22632581 if (voxel->isColored()) {
@@ -2272,15 +2590,25 @@ bool VoxelSystem::collectStatsForTreesAndVBOsOperation(OctreeElement* element, v · 8 unmodified lines
22722590 args->dirtyNodes++;
22732591 }
22742592
2275− if (voxel->isKnownBufferIndex()) {
2276− args->nodesInVBO++;
2277− unsigned long nodeIndex = voxel->getBufferIndex();
2593+ unsigned long nodeIndex = 0;
2594+ if (voxel->isKnownBufferIndex()) {
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)) {
22782605
22792606 const bool extraDebugging = false; // enable for extra debugging
22802607 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",
22822609 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());
22842612 }
22852613
22862614 if (args->hasIndexFound[nodeIndex]) {
@@ -2309,12 +2637,14 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { · 22 unmodified lines
23092637 glBufferIndex minDirty = GLBUFFER_INDEX_UNKNOWN;
23102638 glBufferIndex maxDirty = 0;
23112639
2312− for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) {
2313− if (_writeVoxelDirtyArray[i]) {
2314− minDirty = std::min(minDirty,i);
2315− maxDirty = std::max(maxDirty,i);
2316− }
2317− }
2640+ if (!_usePrimitiveRenderer) {
2641+ for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) {
2642+ if (_writeVoxelDirtyArray[i]) {
2643+ minDirty = std::min(minDirty,i);
2644+ maxDirty = std::max(maxDirty,i);
2645+ }
2646+ }
2647+ }
23182648
23192649 collectStatsForTreesAndVBOsArgs args(_maxVoxels);
23202650 args.expectedMax = _voxelsInWriteArrays;
@@ -2323,7 +2653,7 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { · 2 unmodified lines
23232653
23242654 _tree->recurseTreeWithOperation(collectStatsForTreesAndVBOsOperation,&args);
23252655
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",
23272657 args.totalNodes, args.leafNodes, args.dirtyNodes, args.coloredNodes, args.shouldRenderNodes);
23282658
23292659 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
23322662 qDebug(" inVBO %ld \n nodesInVBOOverExpectedMax %ld \n duplicateVBOIndex %ld \n nodesInVBONotShouldRender %ld",
23332663 args.nodesInVBO, args.nodesInVBOOverExpectedMax, args.duplicateVBOIndex, args.nodesInVBONotShouldRender);
23342664
2665+ qDebug(" inPrimitiveRenderer %ld \n completely culled %ld \n",
2666+ args.nodesInPrimitiveRenderer, args.culledLeafNodes);
2667+
23352668 glBufferIndex minInVBO = GLBUFFER_INDEX_UNKNOWN;
23362669 glBufferIndex maxInVBO = 0;
23372670
@@ -2775,5 +3108,102 @@ void VoxelSystem::beginLoadingLocalVoxelCache() { · 437 unmodified lines
27753108 qDebug() << "DONE beginLoadingLocalVoxelCache()";
27763109 }
27773110
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+};
27783124
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+};
27793209