From 873ae85d4eac3b0b86f50e4ff40733633634b0bf Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 5 Jun 2013 10:32:36 -0700 Subject: [PATCH 1/5] changed deleteVoxelCodeFromTree() to use recursion to allow for proper unwinding, updating of parent path --- libraries/voxels/src/VoxelTree.cpp | 132 +++++++++++++++++++++-------- libraries/voxels/src/VoxelTree.h | 3 +- 2 files changed, 99 insertions(+), 36 deletions(-) diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 49d79141de..241b6e5f64 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -264,61 +264,123 @@ void VoxelTree::deleteVoxelAt(float x, float y, float z, float s, bool stage) { reaverageVoxelColors(rootNode); } +class DeleteVoxelCodeFromTreeArgs { +public: + bool stage; + bool collapseEmptyTrees; + unsigned char* codeBuffer; + int lengthOfCode; + bool deleteLastChild; + bool pathChanged; +}; // Note: uses the codeColorBuffer format, but the color's are ignored, because // this only finds and deletes the node from the tree. void VoxelTree::deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool stage, bool collapseEmptyTrees) { - VoxelNode* parentNode = NULL; - VoxelNode* nodeToDelete = nodeForOctalCode(rootNode, codeBuffer, &parentNode); - // If the node exists... - int lengthInBytes = bytesRequiredForCodeLength(*codeBuffer); // includes octet count, not color! - // if the code we got back matches our target, then we know we can actually delete it - if (memcmp(nodeToDelete->getOctalCode(), codeBuffer, lengthInBytes) == 0) { - if (parentNode) { - int childIndex = branchIndexWithDescendant(parentNode->getOctalCode(), codeBuffer); - if (stage) { - nodeToDelete->stageForDeletion(); - } else { - parentNode->deleteChildAtIndex(childIndex); - if (_shouldReaverage) { - parentNode->setColorFromAverageOfChildren(); - } - } + // recurse the tree while decoding the codeBuffer, once you find the node in question, recurse + // back and implement color reaveraging, and marking of lastChanged + DeleteVoxelCodeFromTreeArgs args; + args.stage = stage; + args.collapseEmptyTrees = collapseEmptyTrees; + args.codeBuffer = codeBuffer; + args.lengthOfCode = numberOfThreeBitSectionsInCode(codeBuffer); + args.deleteLastChild = false; + args.pathChanged = false; + + VoxelNode* node = rootNode; + deleteVoxelCodeFromTreeRecursion(node, &args); +} - // If we're in collapseEmptyTrees mode, and we're the last child of this parent, then delete the parent. - // This will collapse the empty tree above us. - if (collapseEmptyTrees && parentNode->getChildCount() == 0) { - // Can't delete the root this way. - if (parentNode != rootNode) { - deleteVoxelCodeFromTree(parentNode->getOctalCode(), stage, collapseEmptyTrees); - } - } - _isDirty = true; - } - } else if (nodeToDelete->isLeaf()) { +void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData) { + DeleteVoxelCodeFromTreeArgs* args = (DeleteVoxelCodeFromTreeArgs*)extraData; + + int lengthOfNodeCode = numberOfThreeBitSectionsInCode(node->getOctalCode()); + + // Since we traverse the tree in code order, we know that if our code + // matches, then we've reached our target node. + if (lengthOfNodeCode == args->lengthOfCode) { + // we've reached our target, depending on how we're called we may be able to operate on it + // if we're in "stage" mode, then we can could have the node staged, otherwise we can't really delete + // it here, we need to recurse up, and delete it there. So we handle these cases the same to keep + // the logic consistent. + args->deleteLastChild = true; + return; + } + + // Ok, we know we haven't reached our target node yet, so keep looking + int childIndex = branchIndexWithDescendant(node->getOctalCode(), args->codeBuffer); + VoxelNode* childNode = node->getChildAtIndex(childIndex); + + // If there is no child at the target location, then it likely means we were asked to delete a child out + // of a larger leaf voxel. We support this by breaking up the parent voxel into smaller pieces. + if (!childNode) { // we need to break up ancestors until we get to the right level - VoxelNode* ancestorNode = nodeToDelete; + VoxelNode* ancestorNode = node; while (true) { - int index = branchIndexWithDescendant(ancestorNode->getOctalCode(), codeBuffer); - for (int i = 0; i < 8; i++) { + int index = branchIndexWithDescendant(ancestorNode->getOctalCode(), args->codeBuffer); + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { if (i != index) { ancestorNode->addChildAtIndex(i); - if (nodeToDelete->isColored()) { - ancestorNode->getChildAtIndex(i)->setColor(nodeToDelete->getColor()); + if (node->isColored()) { + ancestorNode->getChildAtIndex(i)->setColor(node->getColor()); } } } - if (*ancestorNode->getOctalCode() == *codeBuffer - 1) { + int lengthOfancestorNode = numberOfThreeBitSectionsInCode(ancestorNode->getOctalCode()); + + // If we've reached the parent of the target, then stop breaking up children + if (lengthOfancestorNode == (args->lengthOfCode - 1)) { break; } ancestorNode->addChildAtIndex(index); ancestorNode = ancestorNode->getChildAtIndex(index); - if (nodeToDelete->isColored()) { - ancestorNode->setColor(nodeToDelete->getColor()); + if (node->isColored()) { + ancestorNode->setColor(node->getColor()); } } _isDirty = true; + args->pathChanged = true; + + // ends recursion, unwinds up stack + return; + } + + // recurse... + deleteVoxelCodeFromTreeRecursion(childNode, args); + + // If the lower level determined it needs to be deleted, then we should delete now. + if (args->deleteLastChild) { + if (args->stage) { + childNode->stageForDeletion(); + } else { + node->deleteChildAtIndex(childIndex); // note: this will track dirtiness and lastChanged for this node + if (_shouldReaverage) { + node->setColorFromAverageOfChildren(); + } + } + + // track our tree dirtiness + _isDirty = true; + + // track that path has changed + args->pathChanged = true; + + // If we're in collapseEmptyTrees mode, and this was the last child of this node, then we also want + // to delete this node. This will collapse the empty tree above us. + if (args->collapseEmptyTrees && node->getChildCount() == 0) { + // Can't delete the root this way. + if (node == rootNode) { + args->deleteLastChild = false; // reset so that further up the unwinding chain we don't do anything + } + } else { + args->deleteLastChild = false; // reset so that further up the unwinding chain we don't do anything + } + } + + // If the lower level did some work, then we need to track our lastChanged status. + if (args->pathChanged) { + node->markWithChangedTime(); } } diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index b63e6eb738..16aad9b72b 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -10,7 +10,6 @@ #define __hifi__VoxelTree__ #include "SimpleMovingAverage.h" - #include "ViewFrustum.h" #include "VoxelNode.h" #include "VoxelNodeBag.h" @@ -101,6 +100,8 @@ public: void copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destinationNode); private: + void deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData); + int encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel, VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag, const ViewFrustum* viewFrustum, bool includeColor, bool includeExistsBits, From 4f1131fc97fef249de030f7fc5d34b77247d2015 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 5 Jun 2013 10:31:24 -0700 Subject: [PATCH 2/5] add markWithChangedTime() method as public method Conflicts: libraries/voxels/src/VoxelNode.cpp libraries/voxels/src/VoxelNode.h --- libraries/voxels/src/VoxelNode.cpp | 10 +++++++++- libraries/voxels/src/VoxelNode.h | 5 +++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index f542fe0dd4..0a4d813ec7 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -46,7 +46,7 @@ void VoxelNode::init(unsigned char * octalCode) { _isDirty = true; _shouldRender = false; _isStagedForDeletion = false; - + markWithChangedTime(); calculateAABox(); } @@ -66,6 +66,7 @@ void VoxelNode::setShouldRender(bool shouldRender) { if (shouldRender != _shouldRender) { _shouldRender = shouldRender; _isDirty = true; + markWithChangedTime(); } } @@ -89,6 +90,7 @@ void VoxelNode::deleteChildAtIndex(int childIndex) { delete _children[childIndex]; _children[childIndex] = NULL; _isDirty = true; + markWithChangedTime(); _childCount--; } } @@ -99,6 +101,7 @@ VoxelNode* VoxelNode::removeChildAtIndex(int childIndex) { if (_children[childIndex]) { _children[childIndex] = NULL; _isDirty = true; + markWithChangedTime(); _childCount--; } return returnedChild; @@ -108,6 +111,7 @@ void VoxelNode::addChildAtIndex(int childIndex) { if (!_children[childIndex]) { _children[childIndex] = new VoxelNode(childOctalCode(_octalCode, childIndex)); _isDirty = true; + markWithChangedTime(); _childCount++; } } @@ -135,6 +139,7 @@ void VoxelNode::safeDeepDeleteChildAtIndex(int childIndex, bool& stagedForDeleti deleteChildAtIndex(childIndex); _isDirty = true; } + markWithChangedTime(); } } @@ -178,6 +183,7 @@ void VoxelNode::setFalseColor(colorPart red, colorPart green, colorPart blue) { _currentColor[2] = blue; _currentColor[3] = 1; // XXXBHG - False colors are always considered set _isDirty = true; + markWithChangedTime(); } } @@ -189,6 +195,7 @@ void VoxelNode::setFalseColored(bool isFalseColored) { } _falseColored = isFalseColored; _isDirty = true; + markWithChangedTime(); } }; @@ -202,6 +209,7 @@ void VoxelNode::setColor(const nodeColor& color) { memcpy(&_currentColor,&color,sizeof(nodeColor)); } _isDirty = true; + markWithChangedTime(); } } #endif diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index 1b21962261..5aa1fe4fa0 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -9,6 +9,7 @@ #ifndef __hifi__VoxelNode__ #define __hifi__VoxelNode__ +#include #include "AABox.h" #include "ViewFrustum.h" #include "VoxelConstants.h" @@ -26,6 +27,7 @@ private: #endif glBufferIndex _glBufferIndex; bool _isDirty; + double _lastChanged; bool _shouldRender; bool _isStagedForDeletion; AABox _box; @@ -75,6 +77,9 @@ public: void printDebugDetails(const char* label) const; bool isDirty() const { return _isDirty; }; void clearDirtyBit() { _isDirty = false; }; + bool hasChangedSince(double time) const { return (_lastChanged > time); }; + void markWithChangedTime() { _lastChanged = usecTimestampNow(); }; + glBufferIndex getBufferIndex() const { return _glBufferIndex; }; bool isKnownBufferIndex() const { return (_glBufferIndex != GLBUFFER_INDEX_UNKNOWN); }; void setBufferIndex(glBufferIndex index) { _glBufferIndex = index; }; From 57aff0af64bf518217876c7ab0be7335518322e9 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 6 Jun 2013 01:00:12 -0700 Subject: [PATCH 3/5] fixed bug in recursive delete that caused animation ghosts --- libraries/voxels/src/VoxelTree.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 241b6e5f64..a9b69d2725 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -264,6 +264,7 @@ void VoxelTree::deleteVoxelAt(float x, float y, float z, float s, bool stage) { reaverageVoxelColors(rootNode); } + class DeleteVoxelCodeFromTreeArgs { public: bool stage; @@ -277,7 +278,6 @@ public: // Note: uses the codeColorBuffer format, but the color's are ignored, because // this only finds and deletes the node from the tree. void VoxelTree::deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool stage, bool collapseEmptyTrees) { - // recurse the tree while decoding the codeBuffer, once you find the node in question, recurse // back and implement color reaveraging, and marking of lastChanged DeleteVoxelCodeFromTreeArgs args; @@ -312,9 +312,10 @@ void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraDat int childIndex = branchIndexWithDescendant(node->getOctalCode(), args->codeBuffer); VoxelNode* childNode = node->getChildAtIndex(childIndex); - // If there is no child at the target location, then it likely means we were asked to delete a child out - // of a larger leaf voxel. We support this by breaking up the parent voxel into smaller pieces. - if (!childNode) { + // If there is no child at the target location, and the current parent node is a colored leaf, + // then it means we were asked to delete a child out of a larger leaf voxel. + // We support this by breaking up the parent voxel into smaller pieces. + if (!childNode && node->isLeaf() && node->isColored()) { // we need to break up ancestors until we get to the right level VoxelNode* ancestorNode = node; while (true) { @@ -345,8 +346,17 @@ void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraDat // ends recursion, unwinds up stack return; } + + // if we don't have a child and we reach this point, then we actually know that the parent + // isn't a colored leaf, and the child branch doesn't exist, so there's nothing to do below and + // we can safely return, ending the recursion and unwinding + if (!childNode) { + //printLog("new___deleteVoxelCodeFromTree() child branch doesn't exist, but parent is not a leaf, just unwind\n"); + return; + } - // recurse... + // If we got this far then we have a child for the branch we're looking for, but we're not there yet + // recurse till we get there deleteVoxelCodeFromTreeRecursion(childNode, args); // If the lower level determined it needs to be deleted, then we should delete now. From 66ebb7d01cd230b4816452438e4c3fdb9c851ab9 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 6 Jun 2013 01:39:40 -0700 Subject: [PATCH 4/5] Made readCodeColorBufferToTree() use self contained recursion so we can implement color averaging properly --- libraries/shared/src/SharedUtil.cpp | 4 - libraries/voxels/src/VoxelNode.cpp | 3 +- libraries/voxels/src/VoxelNode.h | 2 +- libraries/voxels/src/VoxelTree.cpp | 114 ++++++++++++++++++++-------- libraries/voxels/src/VoxelTree.h | 1 + 5 files changed, 88 insertions(+), 36 deletions(-) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 08b84e2b7e..cf9e5f9b78 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -188,10 +188,6 @@ bool cmdOptionExists(int argc, const char * argv[],const char* option) { // // HACK ATTACK: Well, what if this is larger than the MTU? That's the caller's problem, we // just truncate the message -// Usage: -// unsigned char* voxelData = pointToVoxel(x,y,z,s,red,green,blue); -// tree->readCodeColorBufferToTree(voxelData); -// delete voxelData; // // Complaints: Brad :) #define GUESS_OF_VOXELCODE_SIZE 10 diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index 0a4d813ec7..b5c5b10cac 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -107,13 +107,14 @@ VoxelNode* VoxelNode::removeChildAtIndex(int childIndex) { return returnedChild; } -void VoxelNode::addChildAtIndex(int childIndex) { +VoxelNode* VoxelNode::addChildAtIndex(int childIndex) { if (!_children[childIndex]) { _children[childIndex] = new VoxelNode(childOctalCode(_octalCode, childIndex)); _isDirty = true; markWithChangedTime(); _childCount++; } + return _children[childIndex]; } // handles staging or deletion of all deep children diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index 5aa1fe4fa0..997c495693 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -48,7 +48,7 @@ public: VoxelNode* getChildAtIndex(int childIndex) const { return _children[childIndex]; }; void deleteChildAtIndex(int childIndex); VoxelNode* removeChildAtIndex(int childIndex); - void addChildAtIndex(int childIndex); + VoxelNode* addChildAtIndex(int childIndex); void safeDeepDeleteChildAtIndex(int childIndex, bool& stagedForDeletion); // handles staging or deletion of all descendents void setColorFromAverageOfChildren(); diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index a9b69d2725..30c61fe4a1 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -401,50 +401,104 @@ void VoxelTree::eraseAllVoxels() { _isDirty = true; } -void VoxelTree::readCodeColorBufferToTree(unsigned char *codeColorBuffer, bool destructive) { - VoxelNode* lastCreatedNode = nodeForOctalCode(rootNode, codeColorBuffer, NULL); - // create the node if it does not exist - if (*lastCreatedNode->getOctalCode() != *codeColorBuffer) { - lastCreatedNode = createMissingNode(lastCreatedNode, codeColorBuffer); - _isDirty = true; - } else { - // if it does exist, make sure it has no children - for (int i = 0; i < 8; i++) { - if (lastCreatedNode->getChildAtIndex(i)) { - if (destructive) { - lastCreatedNode->deleteChildAtIndex(i); - } else { - printLog("WARNING! operation would require deleting child at index %d, add Voxel ignored!\n ", i); - } +class ReadCodeColorBufferToTreeArgs { +public: + unsigned char* codeColorBuffer; + int lengthOfCode; + bool destructive; + bool pathChanged; +}; + +void VoxelTree::readCodeColorBufferToTree(unsigned char* codeColorBuffer, bool destructive) { + ReadCodeColorBufferToTreeArgs args; + args.codeColorBuffer = codeColorBuffer; + args.lengthOfCode = numberOfThreeBitSectionsInCode(codeColorBuffer); + args.destructive = destructive; + args.pathChanged = false; + + + VoxelNode* node = rootNode; + + readCodeColorBufferToTreeRecursion(node, &args); +} + + +void VoxelTree::readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraData) { + ReadCodeColorBufferToTreeArgs* args = (ReadCodeColorBufferToTreeArgs*)extraData; + + int lengthOfNodeCode = numberOfThreeBitSectionsInCode(node->getOctalCode()); + + // Since we traverse the tree in code order, we know that if our code + // matches, then we've reached our target node. + if (lengthOfNodeCode == args->lengthOfCode) { + // we've reached our target -- we might have found our node, but that node might have children. + // in this case, we only allow you to set the color if you explicitly asked for a destructive + // write. + if (!node->isLeaf() && args->destructive) { + // if it does exist, make sure it has no children + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + node->deleteChildAtIndex(i); + } + } else { + if (!node->isLeaf()) { + printLog("WARNING! operation would require deleting children, add Voxel ignored!\n "); } } + + // If we get here, then it means, we either had a true leaf to begin with, or we were in + // destructive mode and we deleted all the child trees. So we can color. + if (node->isLeaf()) { + // give this node its color + int octalCodeBytes = bytesRequiredForCodeLength(args->lengthOfCode); + + nodeColor newColor; + memcpy(newColor, args->codeColorBuffer + octalCodeBytes, SIZE_OF_COLOR_DATA); + newColor[SIZE_OF_COLOR_DATA] = 1; + node->setColor(newColor); + + // It's possible we just reset the node to it's exact same color, in + // which case we don't consider this to be dirty... + if (node->isDirty()) { + // track our tree dirtiness + _isDirty = true; + // track that path has changed + args->pathChanged = true; + } + } + return; } - if (lastCreatedNode->isLeaf()) { - // give this node its color - int octalCodeBytes = bytesRequiredForCodeLength(*codeColorBuffer); + // Ok, we know we haven't reached our target node yet, so keep looking + int childIndex = branchIndexWithDescendant(node->getOctalCode(), args->codeColorBuffer); + VoxelNode* childNode = node->getChildAtIndex(childIndex); + + // If the branch we need to traverse does not exist, then create it on the way down... + if (!childNode) { + childNode = node->addChildAtIndex(childIndex); + } - nodeColor newColor; - memcpy(newColor, codeColorBuffer + octalCodeBytes, 3); - newColor[3] = 1; - lastCreatedNode->setColor(newColor); - if (lastCreatedNode->isDirty()) { - _isDirty = true; - } + // recurse... + readCodeColorBufferToTreeRecursion(childNode, args); + + // Unwinding... + + // If the lower level did some work, then we need to track our lastChanged status. + if (args->pathChanged) { + node->markWithChangedTime(); } } void VoxelTree::processRemoveVoxelBitstream(unsigned char * bitstream, int bufferSizeBytes) { //unsigned short int itemNumber = (*((unsigned short int*)&bitstream[sizeof(PACKET_HEADER)])); int atByte = sizeof(short int) + sizeof(PACKET_HEADER); - unsigned char* pVoxelData = (unsigned char*)&bitstream[atByte]; + unsigned char* voxelCode = (unsigned char*)&bitstream[atByte]; while (atByte < bufferSizeBytes) { - unsigned char octets = (unsigned char)*pVoxelData; - int voxelDataSize = bytesRequiredForCodeLength(octets)+3; // 3 for color! + int codeLength = numberOfThreeBitSectionsInCode(voxelCode); + int voxelDataSize = bytesRequiredForCodeLength(codeLength) + SIZE_OF_COLOR_DATA; - deleteVoxelCodeFromTree(pVoxelData, ACTUALLY_DELETE, COLLAPSE_EMPTY_TREE); + deleteVoxelCodeFromTree(voxelCode, ACTUALLY_DELETE, COLLAPSE_EMPTY_TREE); - pVoxelData+=voxelDataSize; + voxelCode+=voxelDataSize; atByte+=voxelDataSize; } reaverageVoxelColors(rootNode); // Fix our colors!! Need to call it on rootNode diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 16aad9b72b..1158596527 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -101,6 +101,7 @@ public: private: void deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData); + void readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraData); int encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel, VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag, From ae3db89f63005a902f239159b1cb5b10e9556f52 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 6 Jun 2013 10:55:11 -0700 Subject: [PATCH 5/5] added method for general subtree change bookkeeping --- libraries/voxels/src/VoxelNode.cpp | 17 +++++++++++++++++ libraries/voxels/src/VoxelNode.h | 3 +++ libraries/voxels/src/VoxelTree.cpp | 10 ++++++---- libraries/voxels/src/VoxelTree.h | 2 ++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index b5c5b10cac..bd8feadb35 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -12,6 +12,7 @@ #include "SharedUtil.h" #include "Log.h" #include "VoxelNode.h" +#include "VoxelTree.h" #include "VoxelConstants.h" #include "OctalCode.h" #include "AABox.h" @@ -61,6 +62,22 @@ VoxelNode::~VoxelNode() { } } +// This method is called by VoxelTree when the subtree below this node +// is known to have changed. It's intended to be used as a place to do +// bookkeeping that a node may need to do when the subtree below it has +// changed. However, you should hopefully make your bookkeeping relatively +// localized, because this method will get called for every node in an +// recursive unwinding case like delete or add voxel +void VoxelNode::handleSubtreeChanged(VoxelTree* myTree) { + markWithChangedTime(); + + // here's a good place to do color re-averaging... + if (myTree->getShouldReaverage()) { + setColorFromAverageOfChildren(); + } +} + + void VoxelNode::setShouldRender(bool shouldRender) { // if shouldRender is changing, then consider ourselves dirty if (shouldRender != _shouldRender) { diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index 997c495693..3fd8e67e9c 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -14,6 +14,8 @@ #include "ViewFrustum.h" #include "VoxelConstants.h" +class VoxelTree; // forward delclaration + typedef unsigned char colorPart; typedef unsigned char nodeColor[4]; typedef unsigned char rgbColor[3]; @@ -79,6 +81,7 @@ public: void clearDirtyBit() { _isDirty = false; }; bool hasChangedSince(double time) const { return (_lastChanged > time); }; void markWithChangedTime() { _lastChanged = usecTimestampNow(); }; + void handleSubtreeChanged(VoxelTree* myTree); glBufferIndex getBufferIndex() const { return _glBufferIndex; }; bool isKnownBufferIndex() const { return (_glBufferIndex != GLBUFFER_INDEX_UNKNOWN); }; diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 30c61fe4a1..ab724d92a9 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -388,9 +388,10 @@ void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraDat } } - // If the lower level did some work, then we need to track our lastChanged status. + // If the lower level did some work, then we need to let this node know, so it can + // do any bookkeeping it wants to, like color re-averaging, time stamp marking, etc if (args->pathChanged) { - node->markWithChangedTime(); + node->handleSubtreeChanged(this); } } @@ -482,9 +483,10 @@ void VoxelTree::readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraD // Unwinding... - // If the lower level did some work, then we need to track our lastChanged status. + // If the lower level did some work, then we need to let this node know, so it can + // do any bookkeeping it wants to, like color re-averaging, time stamp marking, etc if (args->pathChanged) { - node->markWithChangedTime(); + node->handleSubtreeChanged(this); } } diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 1158596527..a238cb603a 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -99,6 +99,8 @@ public: void copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinationTree, bool rebaseToRoot); void copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destinationNode); + bool getShouldReaverage() const { return _shouldReaverage; } + private: void deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData); void readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraData);