glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); // consider the _voxelDirtyArray[] clean! memset(_voxelDirtyArray, false, _voxelsInWriteArrays * sizeof(bool));} void VoxelSystem::updatePartialVBOs() { glBufferIndex segmentStart = 0; glBufferIndex segmentEnd = 0; bool inSegment = false; for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) { bool thisVoxelDirty = _voxelDirtyArray[i]; if (!inSegment) { if (thisVoxelDirty) { segmentStart = i; inSegment = true; _voxelDirtyArray[i] = false; // consider us clean! } } else { if (!thisVoxelDirty) { // If we got here because because this voxel is NOT dirty, so the last dirty voxel was the one before // this one and so that's where the "segment" ends segmentEnd = i - 1; inSegment = false; int segmentLength = (segmentEnd - segmentStart) + 1; GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom); segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); } _voxelDirtyArray[i] = false; // consider us clean! } } // if we got to the end of the array, and we're in an active dirty segment... if (inSegment) { segmentEnd = _voxelsInWriteArrays - 1; inSegment = false; int segmentLength = (segmentEnd - segmentStart) + 1; GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom); segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); }} void VoxelSystem::updateVBOs() { static char buffer[40] = { 0 }; if (_renderWarningsOn) { sprintf(buffer, "updateVBOs() _renderFullVBO=%s", (_renderFullVBO ? "yes" : "no")); }; PerformanceWarning warn(_renderWarningsOn, buffer); // would like to include _callsToTreesToArrays if (_voxelsDirty) { if (_renderFullVBO) { updateFullVBOs(); } else { updatePartialVBOs(); } _voxelsDirty = false; } _callsToTreesToArrays = 0; // clear it} void VoxelSystem::render() { PerformanceWarning warn(_renderWarningsOn, "render()"); glPushMatrix(); updateVBOs(); // tell OpenGL where to find vertex and color information glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); glVertexPointer(3, GL_FLOAT, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID); glNormalPointer(GL_FLOAT, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0); // draw the number of voxels we have glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID); glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE); glDrawElements(GL_TRIANGLES, 36 * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); // deactivate vertex and color arrays after drawing glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_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); // scale back down to 1 so heads aren't massive glPopMatrix();} int VoxelSystem::_nodeCount = 0; void VoxelSystem::killLocalVoxels() { _tree->eraseAllVoxels(); _voxelsInWriteArrays = _voxelsInReadArrays = 0; // better way to do this?? //setupNewVoxelsForDrawing();} bool VoxelSystem::randomColorOperation(VoxelNode* node, void* extraData) { _nodeCount++; if (node->isColored()) { nodeColor newColor = { 255, randomColorValue(150), randomColorValue(150), 1 }; node->setColor(newColor); } return true;} void VoxelSystem::randomizeVoxelColors() { _nodeCount = 0; _tree->recurseTreeWithOperation(randomColorOperation); printLog("setting randomized true color for %d nodes\n", _nodeCount); setupNewVoxelsForDrawing();} bool VoxelSystem::falseColorizeRandomOperation(VoxelNode* node, void* extraData) { _nodeCount++; // always false colorize node->setFalseColor(255, randomColorValue(150), randomColorValue(150)); return true; // keep going!} void VoxelSystem::falseColorizeRandom() { _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeRandomOperation); printLog("setting randomized false color for %d nodes\n", _nodeCount); setupNewVoxelsForDrawing();} bool VoxelSystem::trueColorizeOperation(VoxelNode* node, void* extraData) { _nodeCount++; node->setFalseColored(false); return true;} void VoxelSystem::trueColorize() { PerformanceWarning warn(true, "trueColorize()",true); _nodeCount = 0; _tree->recurseTreeWithOperation(trueColorizeOperation); printLog("setting true color for %d nodes\n", _nodeCount); setupNewVoxelsForDrawing();} // Will false colorize voxels that are not in viewbool VoxelSystem::falseColorizeInViewOperation(VoxelNode* node, void* extraData) { const ViewFrustum* viewFrustum = (const ViewFrustum*) extraData; _nodeCount++; if (node->isColored()) { if (!node->isInView(*viewFrustum)) { // Out of view voxels are colored RED node->setFalseColor(255, 0, 0); } } return true; // keep going!} void VoxelSystem::falseColorizeInView(ViewFrustum* viewFrustum) { _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeInViewOperation,(void*)viewFrustum); printLog("setting in view false color for %d nodes\n", _nodeCount); setupNewVoxelsForDrawing();} // Will false colorize voxels based on distance from viewbool VoxelSystem::falseColorizeDistanceFromViewOperation(VoxelNode* node, void* extraData) { ViewFrustum* viewFrustum = (ViewFrustum*) extraData; if (node->isColored()) { float distance = node->distanceToCamera(*viewFrustum); _nodeCount++; float distanceRatio = (_minDistance == _maxDistance) ? 1 : (distance - _minDistance) / (_maxDistance - _minDistance); // We want to colorize this in 16 bug chunks of color const unsigned char maxColor = 255; const unsigned char colorBands = 16; const unsigned char gradientOver = 128; unsigned char colorBand = (colorBands * distanceRatio); node->setFalseColor((colorBand * (gradientOver / colorBands)) + (maxColor - gradientOver), 0, 0); } return true; // keep going!} float VoxelSystem::_maxDistance = 0.0;float VoxelSystem::_minDistance = FLT_MAX; // Helper function will get the distance from view range, would be nice if you could just keep track// of this as voxels are created and/or colored... seems like some transform math could do that so// we wouldn't need to do two passes of the treebool VoxelSystem::getDistanceFromViewRangeOperation(VoxelNode* node, void* extraData) { ViewFrustum* viewFrustum = (ViewFrustum*) extraData; // only do this for truly colored voxels... if (node->isColored()) { float distance = node->distanceToCamera(*viewFrustum); // calculate the range of distances if (distance > _maxDistance) { _maxDistance = distance; } if (distance < _minDistance) { _minDistance = distance; } _nodeCount++; } return true; // keep going!} void VoxelSystem::falseColorizeDistanceFromView(ViewFrustum* viewFrustum) { _nodeCount = 0; _maxDistance = 0.0; _minDistance = FLT_MAX; _tree->recurseTreeWithOperation(getDistanceFromViewRangeOperation,(void*)viewFrustum); printLog("determining distance range for %d nodes\n", _nodeCount); _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeDistanceFromViewOperation,(void*)viewFrustum); printLog("setting in distance false color for %d nodes\n", _nodeCount); setupNewVoxelsForDrawing();} // combines the removeOutOfView args into a single classclass removeOutOfViewArgs {public: VoxelSystem* thisVoxelSystem; VoxelNodeBag dontRecurseBag; unsigned long nodesScanned; unsigned long nodesRemoved; unsigned long nodesInside; unsigned long nodesIntersect; unsigned long nodesOutside; removeOutOfViewArgs(VoxelSystem* voxelSystem) : thisVoxelSystem(voxelSystem), dontRecurseBag(), nodesScanned(0), nodesRemoved(0), nodesInside(0), nodesIntersect(0), nodesOutside(0) { }}; // "Remove" voxels from the tree that are not in view. We don't actually delete them,// we remove them from the tree and place them into a holding area for later deletionbool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) { removeOutOfViewArgs* args = (removeOutOfViewArgs*)extraData; // If our node was previously added to the don't recurse bag, then return false to // stop the further recursion. This means that the whole node and it's children are // known to be in view, so don't recurse them if (args->dontRecurseBag.contains(node)) { args->dontRecurseBag.remove(node); return false; // stop recursion } VoxelSystem* thisVoxelSystem = args->thisVoxelSystem; args->nodesScanned++; // Need to operate on our child nodes, so we can remove them for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { VoxelNode* childNode = node->getChildAtIndex(i); if (childNode) { ViewFrustum::location inFrustum = childNode->inFrustum(*thisVoxelSystem->_viewFrustum); switch (inFrustum) { case ViewFrustum::OUTSIDE: { args->nodesOutside++; args->nodesRemoved++; node->removeChildAtIndex(i); thisVoxelSystem->_removedVoxels.insert(childNode); // by removing the child, it will not get recursed! } break; case ViewFrustum::INSIDE: { // if the child node is fully INSIDE the view, then there's no need to recurse it // because we know all it's children will also be in the view, so we want to // tell the caller to NOT recurse this child args->nodesInside++; args->dontRecurseBag.insert(childNode); } break; case ViewFrustum::INTERSECT: { // if the child node INTERSECTs the view, then we don't want to remove it because // it is at least partially in view. But we DO want to recurse the children because // some of them may not be in view... nothing specifically to do, just keep iterating // the children args->nodesIntersect++; } break; } } } return true; // keep going!} bool VoxelSystem::hasViewChanged() { bool result = false; // assume the best if (_viewFrustum && !_lastKnowViewFrustum.matches(_viewFrustum)) { result = true; _lastKnowViewFrustum = *_viewFrustum; // save last known } return result;} void VoxelSystem::removeOutOfView() { PerformanceWarning warn(_renderWarningsOn, "removeOutOfView()"); removeOutOfViewArgs args(this); _tree->recurseTreeWithOperation(removeOutOfViewOperation,(void*)&args); if (args.nodesRemoved) { _tree->setDirtyBit(); } bool showRemoveDebugDetails = false; if (showRemoveDebugDetails) { printLog("removeOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld _removedVoxels.count()=%d \n", args.nodesScanned, args.nodesRemoved, args.nodesInside, args.nodesIntersect, args.nodesOutside, _removedVoxels.count() ); }} bool VoxelSystem::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, VoxelDetail& detail, float& distance, BoxFace& face) { VoxelNode* node; if (!_tree->findRayIntersection(origin, direction, node, distance, face)) { return false; } detail.x = node->getCorner().x; detail.y = node->getCorner().y; detail.z = node->getCorner().z; detail.s = node->getScale(); detail.red = node->getColor()[0]; detail.green = node->getColor()[1]; detail.blue = node->getColor()[2]; return true;} class falseColorizeRandomEveryOtherArgs {public: falseColorizeRandomEveryOtherArgs() : totalNodes(0), colorableNodes(0), coloredNodes(0), colorThis(true) {}; unsigned long totalNodes; unsigned long colorableNodes; unsigned long coloredNodes; bool colorThis;}; bool VoxelSystem::falseColorizeRandomEveryOtherOperation(VoxelNode* node, void* extraData) { falseColorizeRandomEveryOtherArgs* args = (falseColorizeRandomEveryOtherArgs*)extraData; args->totalNodes++; if (node->isColored()) { args->colorableNodes++; if (args->colorThis) { args->coloredNodes++; node->setFalseColor(255, randomColorValue(150), randomColorValue(150)); } args->colorThis = !args->colorThis; } return true; // keep going!} void VoxelSystem::falseColorizeRandomEveryOther() { falseColorizeRandomEveryOtherArgs args; _tree->recurseTreeWithOperation(falseColorizeRandomEveryOtherOperation,&args); printLog("randomized false color for every other node: total %ld, colorable %ld, colored %ld\n", args.totalNodes, args.colorableNodes, args.coloredNodes); setupNewVoxelsForDrawing();} class collectStatsForTreesAndVBOsArgs {public: collectStatsForTreesAndVBOsArgs() : totalNodes(0), dirtyNodes(0), shouldRenderNodes(0), coloredNodes(0), nodesInVBO(0), nodesInVBOOverExpectedMax(0), duplicateVBOIndex(0), leafNodes(0) { memset(hasIndexFound, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool)); }; unsigned long totalNodes; unsigned long dirtyNodes; unsigned long shouldRenderNodes; unsigned long coloredNodes; unsigned long nodesInVBO; unsigned long nodesInVBOOverExpectedMax; unsigned long duplicateVBOIndex; unsigned long leafNodes; unsigned long expectedMax; bool hasIndexFound[MAX_VOXELS_PER_SYSTEM];}; bool VoxelSystem::collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData) { collectStatsForTreesAndVBOsArgs* args = (collectStatsForTreesAndVBOsArgs*)extraData; args->totalNodes++; if (node->isLeaf()) { args->leafNodes++; } if (node->isColored()) { args->coloredNodes++; } if (node->getShouldRender()) { args->shouldRenderNodes++; } if (node->isDirty()) { args->dirtyNodes++; } if (node->isKnownBufferIndex()) { args->nodesInVBO++; unsigned long nodeIndex = node->getBufferIndex(); if (args->hasIndexFound[nodeIndex]) { args->duplicateVBOIndex++; printLog("duplicateVBO found... index=%ld, isDirty=%s, shouldRender=%s \n", nodeIndex, node->isDirty() ? "yes" : "no" , node->getShouldRender() ? "yes" : "no" ); } else { args->hasIndexFound[nodeIndex] = true; } if (nodeIndex > args->expectedMax) { args->nodesInVBOOverExpectedMax++; } } return true; // keep going!} void VoxelSystem::collectStatsForTreesAndVBOs() { PerformanceWarning warn(true, "collectStatsForTreesAndVBOs()", true); glBufferIndex minDirty = GLBUFFER_INDEX_UNKNOWN; glBufferIndex maxDirty = 0; for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) { if (_voxelDirtyArray[i]) { minDirty = std::min(minDirty,i); maxDirty = std::max(maxDirty,i); } } collectStatsForTreesAndVBOsArgs args; args.expectedMax = _voxelsInWriteArrays; _tree->recurseTreeWithOperation(collectStatsForTreesAndVBOsOperation,&args); printLog("_voxelsDirty=%s _voxelsInWriteArrays=%ld minDirty=%ld maxDirty=%ld \n", (_voxelsDirty ? "yes" : "no"), _voxelsInWriteArrays, minDirty, maxDirty); printLog("stats: total %ld, leaves %ld, dirty %ld, colored %ld, shouldRender %ld, inVBO %ld\n", args.totalNodes, args.leafNodes, args.dirtyNodes, args.coloredNodes, args.shouldRenderNodes); printLog("inVBO %ld, nodesInVBOOverExpectedMax %ld, duplicateVBOIndex %ld\n", args.nodesInVBO, args.nodesInVBOOverExpectedMax, args.duplicateVBOIndex); glBufferIndex minInVBO = GLBUFFER_INDEX_UNKNOWN; glBufferIndex maxInVBO = 0; for (glBufferIndex i = 0; i < MAX_VOXELS_PER_SYSTEM; i++) { if (args.hasIndexFound[i]) { minInVBO = std::min(minInVBO,i); maxInVBO = std::max(maxInVBO,i); } } printLog("minInVBO=%ld maxInVBO=%ld _voxelsInWriteArrays=%ld _voxelsInReadArrays=%ld\n", minInVBO, maxInVBO, _voxelsInWriteArrays, _voxelsInReadArrays); } void VoxelSystem::deleteVoxelAt(float x, float y, float z, float s) { printLog("VoxelSystem::deleteVoxelAt(%f,%f,%f,%f)\n",x,y,z,s); VoxelNode* node = _tree->getVoxelAt(x, y, z, s); if (node) { // tell the node we want it deleted node->stageForDeletion(); // tree is now dirty _tree->setDirtyBit();Lines 501–1000 of 1025. Previous lines Load more
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); // consider the _voxelDirtyArray[] clean! memset(_voxelDirtyArray, false, _voxelsInWriteArrays * sizeof(bool)); } void VoxelSystem::updatePartialVBOs() { glBufferIndex segmentStart = 0; glBufferIndex segmentEnd = 0; bool inSegment = false; for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) { bool thisVoxelDirty = _voxelDirtyArray[i]; if (!inSegment) { if (thisVoxelDirty) { segmentStart = i; inSegment = true; _voxelDirtyArray[i] = false; // consider us clean! } } else { if (!thisVoxelDirty) { // If we got here because because this voxel is NOT dirty, so the last dirty voxel was the one before // this one and so that's where the "segment" ends segmentEnd = i - 1; inSegment = false; int segmentLength = (segmentEnd - segmentStart) + 1; GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom); segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); } _voxelDirtyArray[i] = false; // consider us clean! } } // if we got to the end of the array, and we're in an active dirty segment... if (inSegment) { segmentEnd = _voxelsInWriteArrays - 1; inSegment = false; int segmentLength = (segmentEnd - segmentStart) + 1; GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom); segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); } } void VoxelSystem::updateVBOs() { static char buffer[40] = { 0 }; if (_renderWarningsOn) { sprintf(buffer, "updateVBOs() _renderFullVBO=%s", (_renderFullVBO ? "yes" : "no")); }; PerformanceWarning warn(_renderWarningsOn, buffer); // would like to include _callsToTreesToArrays if (_voxelsDirty) { if (_renderFullVBO) { updateFullVBOs(); } else { updatePartialVBOs(); } _voxelsDirty = false; } _callsToTreesToArrays = 0; // clear it } void VoxelSystem::render() { PerformanceWarning warn(_renderWarningsOn, "render()"); glPushMatrix(); updateVBOs(); // tell OpenGL where to find vertex and color information glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); glVertexPointer(3, GL_FLOAT, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID); glNormalPointer(GL_FLOAT, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0); // draw the number of voxels we have glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID); glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE); glDrawElements(GL_TRIANGLES, 36 * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); // deactivate vertex and color arrays after drawing glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_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); // scale back down to 1 so heads aren't massive glPopMatrix(); } int VoxelSystem::_nodeCount = 0; void VoxelSystem::killLocalVoxels() { _tree->eraseAllVoxels(); _voxelsInWriteArrays = _voxelsInReadArrays = 0; // better way to do this?? //setupNewVoxelsForDrawing(); } bool VoxelSystem::randomColorOperation(VoxelNode* node, void* extraData) { _nodeCount++; if (node->isColored()) { nodeColor newColor = { 255, randomColorValue(150), randomColorValue(150), 1 }; node->setColor(newColor); } return true; } void VoxelSystem::randomizeVoxelColors() { _nodeCount = 0; _tree->recurseTreeWithOperation(randomColorOperation); printLog("setting randomized true color for %d nodes\n", _nodeCount); setupNewVoxelsForDrawing(); } bool VoxelSystem::falseColorizeRandomOperation(VoxelNode* node, void* extraData) { _nodeCount++; // always false colorize node->setFalseColor(255, randomColorValue(150), randomColorValue(150)); return true; // keep going! } void VoxelSystem::falseColorizeRandom() { _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeRandomOperation); printLog("setting randomized false color for %d nodes\n", _nodeCount); setupNewVoxelsForDrawing(); } bool VoxelSystem::trueColorizeOperation(VoxelNode* node, void* extraData) { _nodeCount++; node->setFalseColored(false); return true; } void VoxelSystem::trueColorize() { PerformanceWarning warn(true, "trueColorize()",true); _nodeCount = 0; _tree->recurseTreeWithOperation(trueColorizeOperation); printLog("setting true color for %d nodes\n", _nodeCount); setupNewVoxelsForDrawing(); } // Will false colorize voxels that are not in view bool VoxelSystem::falseColorizeInViewOperation(VoxelNode* node, void* extraData) { const ViewFrustum* viewFrustum = (const ViewFrustum*) extraData; _nodeCount++; if (node->isColored()) { if (!node->isInView(*viewFrustum)) { // Out of view voxels are colored RED node->setFalseColor(255, 0, 0); } } return true; // keep going! } void VoxelSystem::falseColorizeInView(ViewFrustum* viewFrustum) { _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeInViewOperation,(void*)viewFrustum); printLog("setting in view false color for %d nodes\n", _nodeCount); setupNewVoxelsForDrawing(); } // Will false colorize voxels based on distance from view bool VoxelSystem::falseColorizeDistanceFromViewOperation(VoxelNode* node, void* extraData) { ViewFrustum* viewFrustum = (ViewFrustum*) extraData; if (node->isColored()) { float distance = node->distanceToCamera(*viewFrustum); _nodeCount++; float distanceRatio = (_minDistance == _maxDistance) ? 1 : (distance - _minDistance) / (_maxDistance - _minDistance); // We want to colorize this in 16 bug chunks of color const unsigned char maxColor = 255; const unsigned char colorBands = 16; const unsigned char gradientOver = 128; unsigned char colorBand = (colorBands * distanceRatio); node->setFalseColor((colorBand * (gradientOver / colorBands)) + (maxColor - gradientOver), 0, 0); } return true; // keep going! } float VoxelSystem::_maxDistance = 0.0; float VoxelSystem::_minDistance = FLT_MAX; // Helper function will get the distance from view range, would be nice if you could just keep track // of this as voxels are created and/or colored... seems like some transform math could do that so // we wouldn't need to do two passes of the tree bool VoxelSystem::getDistanceFromViewRangeOperation(VoxelNode* node, void* extraData) { ViewFrustum* viewFrustum = (ViewFrustum*) extraData; // only do this for truly colored voxels... if (node->isColored()) { float distance = node->distanceToCamera(*viewFrustum); // calculate the range of distances if (distance > _maxDistance) { _maxDistance = distance; } if (distance < _minDistance) { _minDistance = distance; } _nodeCount++; } return true; // keep going! } void VoxelSystem::falseColorizeDistanceFromView(ViewFrustum* viewFrustum) { _nodeCount = 0; _maxDistance = 0.0; _minDistance = FLT_MAX; _tree->recurseTreeWithOperation(getDistanceFromViewRangeOperation,(void*)viewFrustum); printLog("determining distance range for %d nodes\n", _nodeCount); _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeDistanceFromViewOperation,(void*)viewFrustum); printLog("setting in distance false color for %d nodes\n", _nodeCount); setupNewVoxelsForDrawing(); } // combines the removeOutOfView args into a single class class removeOutOfViewArgs { public: VoxelSystem* thisVoxelSystem; VoxelNodeBag dontRecurseBag; unsigned long nodesScanned; unsigned long nodesRemoved; unsigned long nodesInside; unsigned long nodesIntersect; unsigned long nodesOutside; removeOutOfViewArgs(VoxelSystem* voxelSystem) : thisVoxelSystem(voxelSystem), dontRecurseBag(), nodesScanned(0), nodesRemoved(0), nodesInside(0), nodesIntersect(0), nodesOutside(0) { } }; // "Remove" voxels from the tree that are not in view. We don't actually delete them, // we remove them from the tree and place them into a holding area for later deletion bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) { removeOutOfViewArgs* args = (removeOutOfViewArgs*)extraData; // If our node was previously added to the don't recurse bag, then return false to // stop the further recursion. This means that the whole node and it's children are // known to be in view, so don't recurse them if (args->dontRecurseBag.contains(node)) { args->dontRecurseBag.remove(node); return false; // stop recursion } VoxelSystem* thisVoxelSystem = args->thisVoxelSystem; args->nodesScanned++; // Need to operate on our child nodes, so we can remove them for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { VoxelNode* childNode = node->getChildAtIndex(i); if (childNode) { ViewFrustum::location inFrustum = childNode->inFrustum(*thisVoxelSystem->_viewFrustum); switch (inFrustum) { case ViewFrustum::OUTSIDE: { args->nodesOutside++; args->nodesRemoved++; node->removeChildAtIndex(i); thisVoxelSystem->_removedVoxels.insert(childNode); // by removing the child, it will not get recursed! } break; case ViewFrustum::INSIDE: { // if the child node is fully INSIDE the view, then there's no need to recurse it // because we know all it's children will also be in the view, so we want to // tell the caller to NOT recurse this child args->nodesInside++; args->dontRecurseBag.insert(childNode); } break; case ViewFrustum::INTERSECT: { // if the child node INTERSECTs the view, then we don't want to remove it because // it is at least partially in view. But we DO want to recurse the children because // some of them may not be in view... nothing specifically to do, just keep iterating // the children args->nodesIntersect++; } break; } } } return true; // keep going! } bool VoxelSystem::hasViewChanged() { bool result = false; // assume the best if (_viewFrustum && !_lastKnowViewFrustum.matches(_viewFrustum)) { result = true; _lastKnowViewFrustum = *_viewFrustum; // save last known } return result; } void VoxelSystem::removeOutOfView() { PerformanceWarning warn(_renderWarningsOn, "removeOutOfView()"); removeOutOfViewArgs args(this); _tree->recurseTreeWithOperation(removeOutOfViewOperation,(void*)&args); if (args.nodesRemoved) { _tree->setDirtyBit(); } bool showRemoveDebugDetails = false; if (showRemoveDebugDetails) { printLog("removeOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld _removedVoxels.count()=%d \n", args.nodesScanned, args.nodesRemoved, args.nodesInside, args.nodesIntersect, args.nodesOutside, _removedVoxels.count() ); } } bool VoxelSystem::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, VoxelDetail& detail, float& distance, BoxFace& face) { VoxelNode* node; if (!_tree->findRayIntersection(origin, direction, node, distance, face)) { return false; } detail.x = node->getCorner().x; detail.y = node->getCorner().y; detail.z = node->getCorner().z; detail.s = node->getScale(); detail.red = node->getColor()[0]; detail.green = node->getColor()[1]; detail.blue = node->getColor()[2]; return true; } class falseColorizeRandomEveryOtherArgs { public: falseColorizeRandomEveryOtherArgs() : totalNodes(0), colorableNodes(0), coloredNodes(0), colorThis(true) {}; unsigned long totalNodes; unsigned long colorableNodes; unsigned long coloredNodes; bool colorThis; }; bool VoxelSystem::falseColorizeRandomEveryOtherOperation(VoxelNode* node, void* extraData) { falseColorizeRandomEveryOtherArgs* args = (falseColorizeRandomEveryOtherArgs*)extraData; args->totalNodes++; if (node->isColored()) { args->colorableNodes++; if (args->colorThis) { args->coloredNodes++; node->setFalseColor(255, randomColorValue(150), randomColorValue(150)); } args->colorThis = !args->colorThis; } return true; // keep going! } void VoxelSystem::falseColorizeRandomEveryOther() { falseColorizeRandomEveryOtherArgs args; _tree->recurseTreeWithOperation(falseColorizeRandomEveryOtherOperation,&args); printLog("randomized false color for every other node: total %ld, colorable %ld, colored %ld\n", args.totalNodes, args.colorableNodes, args.coloredNodes); setupNewVoxelsForDrawing(); } class collectStatsForTreesAndVBOsArgs { public: collectStatsForTreesAndVBOsArgs() : totalNodes(0), dirtyNodes(0), shouldRenderNodes(0), coloredNodes(0), nodesInVBO(0), nodesInVBOOverExpectedMax(0), duplicateVBOIndex(0), leafNodes(0) { memset(hasIndexFound, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool)); }; unsigned long totalNodes; unsigned long dirtyNodes; unsigned long shouldRenderNodes; unsigned long coloredNodes; unsigned long nodesInVBO; unsigned long nodesInVBOOverExpectedMax; unsigned long duplicateVBOIndex; unsigned long leafNodes; unsigned long expectedMax; bool hasIndexFound[MAX_VOXELS_PER_SYSTEM]; }; bool VoxelSystem::collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData) { collectStatsForTreesAndVBOsArgs* args = (collectStatsForTreesAndVBOsArgs*)extraData; args->totalNodes++; if (node->isLeaf()) { args->leafNodes++; } if (node->isColored()) { args->coloredNodes++; } if (node->getShouldRender()) { args->shouldRenderNodes++; } if (node->isDirty()) { args->dirtyNodes++; } if (node->isKnownBufferIndex()) { args->nodesInVBO++; unsigned long nodeIndex = node->getBufferIndex(); if (args->hasIndexFound[nodeIndex]) { args->duplicateVBOIndex++; printLog("duplicateVBO found... index=%ld, isDirty=%s, shouldRender=%s \n", nodeIndex, node->isDirty() ? "yes" : "no" , node->getShouldRender() ? "yes" : "no" ); } else { args->hasIndexFound[nodeIndex] = true; } if (nodeIndex > args->expectedMax) { args->nodesInVBOOverExpectedMax++; } } return true; // keep going! } void VoxelSystem::collectStatsForTreesAndVBOs() { PerformanceWarning warn(true, "collectStatsForTreesAndVBOs()", true); glBufferIndex minDirty = GLBUFFER_INDEX_UNKNOWN; glBufferIndex maxDirty = 0; for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) { if (_voxelDirtyArray[i]) { minDirty = std::min(minDirty,i); maxDirty = std::max(maxDirty,i); } } collectStatsForTreesAndVBOsArgs args; args.expectedMax = _voxelsInWriteArrays; _tree->recurseTreeWithOperation(collectStatsForTreesAndVBOsOperation,&args); printLog("_voxelsDirty=%s _voxelsInWriteArrays=%ld minDirty=%ld maxDirty=%ld \n", (_voxelsDirty ? "yes" : "no"), _voxelsInWriteArrays, minDirty, maxDirty); printLog("stats: total %ld, leaves %ld, dirty %ld, colored %ld, shouldRender %ld, inVBO %ld\n", args.totalNodes, args.leafNodes, args.dirtyNodes, args.coloredNodes, args.shouldRenderNodes); printLog("inVBO %ld, nodesInVBOOverExpectedMax %ld, duplicateVBOIndex %ld\n", args.nodesInVBO, args.nodesInVBOOverExpectedMax, args.duplicateVBOIndex); glBufferIndex minInVBO = GLBUFFER_INDEX_UNKNOWN; glBufferIndex maxInVBO = 0; for (glBufferIndex i = 0; i < MAX_VOXELS_PER_SYSTEM; i++) { if (args.hasIndexFound[i]) { minInVBO = std::min(minInVBO,i); maxInVBO = std::max(maxInVBO,i); } } printLog("minInVBO=%ld maxInVBO=%ld _voxelsInWriteArrays=%ld _voxelsInReadArrays=%ld\n", minInVBO, maxInVBO, _voxelsInWriteArrays, _voxelsInReadArrays); } void VoxelSystem::deleteVoxelAt(float x, float y, float z, float s) { printLog("VoxelSystem::deleteVoxelAt(%f,%f,%f,%f)\n",x,y,z,s); VoxelNode* node = _tree->getVoxelAt(x, y, z, s); if (node) { // tell the node we want it deleted node->stageForDeletion(); // tree is now dirty _tree->setDirtyBit();