diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c297f771ca..8f08b75a93 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1394,13 +1394,13 @@ void Application::increaseVoxelSize() { const int MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE = 1500; struct SendVoxelsOperationArgs { - unsigned char* newBaseOctCode; + const unsigned char* newBaseOctCode; }; bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) { SendVoxelsOperationArgs* args = (SendVoxelsOperationArgs*)extraData; if (node->isColored()) { - unsigned char* nodeOctalCode = node->getOctalCode(); + const unsigned char* nodeOctalCode = node->getOctalCode(); unsigned char* codeColorBuffer = NULL; int codeLength = 0; @@ -2784,24 +2784,40 @@ void Application::displayOverlay() { if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse) && !Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && USING_INVENSENSE_MPU9150) { - // Display small target box at center or head mouse target that can also be used to measure LOD - glColor3f(1.0, 1.0, 1.0); + // Display small target box at center or head mouse target that can also be used to measure LOD + glColor3f(1.0, 1.0, 1.0); + glDisable(GL_LINE_SMOOTH); + const int PIXEL_BOX = 16; + glBegin(GL_LINES); + glVertex2f(_headMouseX - PIXEL_BOX/2, _headMouseY); + glVertex2f(_headMouseX + PIXEL_BOX/2, _headMouseY); + glVertex2f(_headMouseX, _headMouseY - PIXEL_BOX/2); + glVertex2f(_headMouseX, _headMouseY + PIXEL_BOX/2); + glEnd(); + glEnable(GL_LINE_SMOOTH); + glColor3f(1.f, 0.f, 0.f); + glPointSize(3.0f); + glDisable(GL_POINT_SMOOTH); + glBegin(GL_POINTS); + glVertex2f(_headMouseX - 1, _headMouseY + 1); + glEnd(); + // If Faceshift is active, show eye pitch and yaw as separate pointer + if (_faceshift.isActive()) { + const float EYE_TARGET_PIXELS_PER_DEGREE = 40.0; + int eyeTargetX = (_glWidget->width() / 2) - _faceshift.getEstimatedEyeYaw() * EYE_TARGET_PIXELS_PER_DEGREE; + int eyeTargetY = (_glWidget->height() / 2) - _faceshift.getEstimatedEyePitch() * EYE_TARGET_PIXELS_PER_DEGREE; + + glColor3f(0.0, 1.0, 1.0); glDisable(GL_LINE_SMOOTH); - const int PIXEL_BOX = 16; glBegin(GL_LINES); - glVertex2f(_headMouseX - PIXEL_BOX/2, _headMouseY); - glVertex2f(_headMouseX + PIXEL_BOX/2, _headMouseY); - glVertex2f(_headMouseX, _headMouseY - PIXEL_BOX/2); - glVertex2f(_headMouseX, _headMouseY + PIXEL_BOX/2); - glEnd(); - glEnable(GL_LINE_SMOOTH); - glColor3f(1.f, 0.f, 0.f); - glPointSize(3.0f); - glDisable(GL_POINT_SMOOTH); - glBegin(GL_POINTS); - glVertex2f(_headMouseX - 1, _headMouseY + 1); + glVertex2f(eyeTargetX - PIXEL_BOX/2, eyeTargetY); + glVertex2f(eyeTargetX + PIXEL_BOX/2, eyeTargetY); + glVertex2f(eyeTargetX, eyeTargetY - PIXEL_BOX/2); + glVertex2f(eyeTargetX, eyeTargetY + PIXEL_BOX/2); glEnd(); + } + } // Show detected levels from the serial I/O ADC channel sensors if (_displayLevels) _serialHeadSensor.renderLevels(_glWidget->width(), _glWidget->height()); diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 152ce8d74b..d0492f3903 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -84,24 +84,27 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o memset(outputLeft, 0, PACKET_LENGTH_BYTES_PER_CHANNEL); memset(outputRight, 0, PACKET_LENGTH_BYTES_PER_CHANNEL); + // If Mute button is pressed, clear the input buffer + if (_muted) { + memset(inputLeft, 0, PACKET_LENGTH_BYTES_PER_CHANNEL); + } + // Add Procedural effects to input samples addProceduralSounds(inputLeft, outputLeft, outputRight, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); if (nodeList && inputLeft) { - if (!_muted) { - // Measure the loudness of the signal from the microphone and store in audio object - float loudness = 0; - for (int i = 0; i < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) { - loudness += abs(inputLeft[i]); - } - - loudness /= BUFFER_LENGTH_SAMPLES_PER_CHANNEL; - _lastInputLoudness = loudness; - - // add input (@microphone) data to the scope - _scope->addSamples(0, inputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); + // Measure the loudness of the signal from the microphone and store in audio object + float loudness = 0; + for (int i = 0; i < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) { + loudness += abs(inputLeft[i]); } + + loudness /= BUFFER_LENGTH_SAMPLES_PER_CHANNEL; + _lastInputLoudness = loudness; + + // add input (@microphone) data to the scope + _scope->addSamples(0, inputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); Node* audioMixer = nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 120ee2c09b..e1c18089fd 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1162,8 +1162,8 @@ void MyAvatar::updateChatCircle(float deltaTime) { glm::vec3 delta = _position - center; glm::vec3 projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f); float myAngle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f; - float leftDistance = PIf; - float rightDistance = PIf; + float leftDistance = PI_TIMES_TWO; + float rightDistance = PI_TIMES_TWO; foreach (const SortedAvatar& sortedAvatar, sortedAvatars) { delta = sortedAvatar.avatar->getPosition() - center; projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f); @@ -1178,8 +1178,13 @@ void MyAvatar::updateChatCircle(float deltaTime) { } } + // if we're on top of a neighbor, we need to randomize so that they don't both go in the same direction + if (rightDistance == 0.0f && randomBoolean()) { + swap(leftDistance, rightDistance); + } + // split the difference between our neighbors - float targetAngle = myAngle + (rightDistance - leftDistance) / 2.0f; + float targetAngle = myAngle + (rightDistance - leftDistance) / 4.0f; glm::vec3 targetPosition = center + (front * sinf(targetAngle) + right * cosf(targetAngle)) * radius; // approach the target position diff --git a/libraries/shared/src/OctalCode.cpp b/libraries/shared/src/OctalCode.cpp index 8146924cf8..80f6ba121f 100644 --- a/libraries/shared/src/OctalCode.cpp +++ b/libraries/shared/src/OctalCode.cpp @@ -16,7 +16,7 @@ #include "SharedUtil.h" #include "OctalCode.h" -int numberOfThreeBitSectionsInCode(unsigned char * octalCode) { +int numberOfThreeBitSectionsInCode(const unsigned char* octalCode) { assert(octalCode); if (*octalCode == 255) { return *octalCode + numberOfThreeBitSectionsInCode(octalCode + 1); @@ -25,18 +25,18 @@ int numberOfThreeBitSectionsInCode(unsigned char * octalCode) { } } -void printOctalCode(unsigned char * octalCode) { +void printOctalCode(const unsigned char* octalCode) { if (!octalCode) { qDebug("NULL\n"); } else { - for (int i = 0; i < bytesRequiredForCodeLength(*octalCode); i++) { + for (int i = 0; i < bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(octalCode)); i++) { outputBits(octalCode[i],false); } qDebug("\n"); } } -char sectionValue(unsigned char * startByte, char startIndexInByte) { +char sectionValue(const unsigned char* startByte, char startIndexInByte) { char rightShift = 8 - startIndexInByte - 3; if (rightShift < 0) { @@ -54,14 +54,14 @@ int bytesRequiredForCodeLength(unsigned char threeBitCodes) { } } -int branchIndexWithDescendant(unsigned char * ancestorOctalCode, unsigned char * descendantOctalCode) { +int branchIndexWithDescendant(const unsigned char* ancestorOctalCode, const unsigned char* descendantOctalCode) { int parentSections = numberOfThreeBitSectionsInCode(ancestorOctalCode); int branchStartBit = parentSections * 3; return sectionValue(descendantOctalCode + 1 + (branchStartBit / 8), branchStartBit % 8); } -unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber) { +unsigned char* childOctalCode(const unsigned char* parentOctalCode, char childNumber) { // find the length (in number of three bit code sequences) // in the parent @@ -76,7 +76,7 @@ unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber int childCodeBytes = bytesRequiredForCodeLength(parentCodeSections + 1); // create a new buffer to hold the new octal code - unsigned char *newCode = new unsigned char[childCodeBytes]; + unsigned char* newCode = new unsigned char[childCodeBytes]; // copy the parent code to the child if (parentOctalCode != NULL) { @@ -115,7 +115,7 @@ unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber return newCode; } -void voxelDetailsForCode(unsigned char * octalCode, VoxelPositionSize& voxelPositionSize) { +void voxelDetailsForCode(const unsigned char* octalCode, VoxelPositionSize& voxelPositionSize) { float output[3]; memset(&output[0], 0, 3 * sizeof(float)); float currentScale = 1.0; @@ -138,7 +138,7 @@ void voxelDetailsForCode(unsigned char * octalCode, VoxelPositionSize& voxelPosi voxelPositionSize.s = currentScale; } -void copyFirstVertexForCode(unsigned char * octalCode, float* output) { +void copyFirstVertexForCode(const unsigned char* octalCode, float* output) { memset(output, 0, 3 * sizeof(float)); float currentScale = 0.5; @@ -154,13 +154,13 @@ void copyFirstVertexForCode(unsigned char * octalCode, float* output) { } } -float * firstVertexForCode(unsigned char * octalCode) { +float * firstVertexForCode(const unsigned char* octalCode) { float * firstVertex = new float[3]; copyFirstVertexForCode(octalCode, firstVertex); return firstVertex; } -OctalCodeComparison compareOctalCodes(unsigned char* codeA, unsigned char* codeB) { +OctalCodeComparison compareOctalCodes(const unsigned char* codeA, const unsigned char* codeB) { if (!codeA || !codeB) { return ILLEGAL_CODE; } @@ -196,10 +196,10 @@ OctalCodeComparison compareOctalCodes(unsigned char* codeA, unsigned char* codeB } -char getOctalCodeSectionValue(unsigned char* octalCode, int section) { +char getOctalCodeSectionValue(const unsigned char* octalCode, int section) { int startAtByte = 1 + (BITS_IN_OCTAL * section / BITS_IN_BYTE); char startIndexInByte = (BITS_IN_OCTAL * section) % BITS_IN_BYTE; - unsigned char* startByte = octalCode + startAtByte; + const unsigned char* startByte = octalCode + startAtByte; return sectionValue(startByte, startIndexInByte); } @@ -243,7 +243,7 @@ void setOctalCodeSectionValue(unsigned char* octalCode, int section, char sectio } } -unsigned char* chopOctalCode(unsigned char* originalOctalCode, int chopLevels) { +unsigned char* chopOctalCode(const unsigned char* originalOctalCode, int chopLevels) { int codeLength = numberOfThreeBitSectionsInCode(originalOctalCode); unsigned char* newCode = NULL; if (codeLength > chopLevels) { @@ -259,7 +259,9 @@ unsigned char* chopOctalCode(unsigned char* originalOctalCode, int chopLevels) { return newCode; } -unsigned char* rebaseOctalCode(unsigned char* originalOctalCode, unsigned char* newParentOctalCode, bool includeColorSpace) { +unsigned char* rebaseOctalCode(const unsigned char* originalOctalCode, const unsigned char* newParentOctalCode, + bool includeColorSpace) { + int oldCodeLength = numberOfThreeBitSectionsInCode(originalOctalCode); int newParentCodeLength = numberOfThreeBitSectionsInCode(newParentOctalCode); int newCodeLength = newParentCodeLength + oldCodeLength; @@ -280,7 +282,7 @@ unsigned char* rebaseOctalCode(unsigned char* originalOctalCode, unsigned char* return newCode; } -bool isAncestorOf(unsigned char* possibleAncestor, unsigned char* possibleDescendent, int descendentsChild) { +bool isAncestorOf(const unsigned char* possibleAncestor, const unsigned char* possibleDescendent, int descendentsChild) { if (!possibleAncestor || !possibleDescendent) { return false; } @@ -350,7 +352,7 @@ unsigned char* hexStringToOctalCode(const QString& input) { return bytes; } -QString octalCodeToHexString(unsigned char* octalCode) { +QString octalCodeToHexString(const unsigned char* octalCode) { const int HEX_NUMBER_BASE = 16; const int HEX_BYTE_SIZE = 2; QString output; diff --git a/libraries/shared/src/OctalCode.h b/libraries/shared/src/OctalCode.h index 4a43142bb8..27746474eb 100644 --- a/libraries/shared/src/OctalCode.h +++ b/libraries/shared/src/OctalCode.h @@ -20,27 +20,28 @@ const int RED_INDEX = 0; const int GREEN_INDEX = 1; const int BLUE_INDEX = 2; -void printOctalCode(unsigned char * octalCode); +void printOctalCode(const unsigned char* octalCode); int bytesRequiredForCodeLength(unsigned char threeBitCodes); -int branchIndexWithDescendant(unsigned char * ancestorOctalCode, unsigned char * descendantOctalCode); -unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber); -int numberOfThreeBitSectionsInCode(unsigned char * octalCode); -unsigned char* chopOctalCode(unsigned char* originalOctalCode, int chopLevels); -unsigned char* rebaseOctalCode(unsigned char* originalOctalCode, unsigned char* newParentOctalCode, +int branchIndexWithDescendant(const unsigned char* ancestorOctalCode, const unsigned char* descendantOctalCode); +unsigned char* childOctalCode(const unsigned char* parentOctalCode, char childNumber); +int numberOfThreeBitSectionsInCode(const unsigned char* octalCode); +unsigned char* chopOctalCode(const unsigned char* originalOctalCode, int chopLevels); +unsigned char* rebaseOctalCode(const unsigned char* originalOctalCode, const unsigned char* newParentOctalCode, bool includeColorSpace = false); const int CHECK_NODE_ONLY = -1; -bool isAncestorOf(unsigned char* possibleAncestor, unsigned char* possibleDescendent, int descendentsChild = CHECK_NODE_ONLY); +bool isAncestorOf(const unsigned char* possibleAncestor, const unsigned char* possibleDescendent, + int descendentsChild = CHECK_NODE_ONLY); // Note: copyFirstVertexForCode() is preferred because it doesn't allocate memory for the return // but other than that these do the same thing. -float * firstVertexForCode(unsigned char * octalCode); -void copyFirstVertexForCode(unsigned char * octalCode, float* output); +float * firstVertexForCode(const unsigned char* octalCode); +void copyFirstVertexForCode(const unsigned char* octalCode, float* output); struct VoxelPositionSize { float x, y, z, s; }; -void voxelDetailsForCode(unsigned char* octalCode, VoxelPositionSize& voxelPositionSize); +void voxelDetailsForCode(const unsigned char* octalCode, VoxelPositionSize& voxelPositionSize); typedef enum { ILLEGAL_CODE = -2, @@ -49,9 +50,9 @@ typedef enum { GREATER_THAN = 1 } OctalCodeComparison; -OctalCodeComparison compareOctalCodes(unsigned char* code1, unsigned char* code2); +OctalCodeComparison compareOctalCodes(const unsigned char* code1, const unsigned char* code2); -QString octalCodeToHexString(unsigned char* octalCode); +QString octalCodeToHexString(const unsigned char* octalCode); unsigned char* hexStringToOctalCode(const QString& input); #endif /* defined(__hifi__OctalCode__) */ diff --git a/libraries/shared/src/PerfStat.cpp b/libraries/shared/src/PerfStat.cpp index 31f4b1a30b..267108f87e 100644 --- a/libraries/shared/src/PerfStat.cpp +++ b/libraries/shared/src/PerfStat.cpp @@ -108,7 +108,8 @@ bool PerformanceWarning::_suppressShortTimings = false; // Destructor handles recording all of our stats PerformanceWarning::~PerformanceWarning() { uint64_t end = usecTimestampNow(); - double elapsedmsec = (end - _start) / 1000.0; + uint64_t elapsedusec = (end - _start); + double elapsedmsec = elapsedusec / 1000.0; if ((_alwaysDisplay || _renderWarningsOn) && elapsedmsec > 1) { if (elapsedmsec > 1000) { double elapsedsec = (end - _start) / 1000000.0; @@ -127,6 +128,13 @@ PerformanceWarning::~PerformanceWarning() { } else if (_alwaysDisplay) { qDebug("%s took %lf milliseconds\n", _message, elapsedmsec); } + // if the caller gave us a pointer to store the running total, track it now. + if (_runningTotal) { + *_runningTotal += elapsedusec; + } + if (_totalCalls) { + *_totalCalls += 1; + } }; diff --git a/libraries/shared/src/PerfStat.h b/libraries/shared/src/PerfStat.h index 2c5924e5ce..e460548f1d 100644 --- a/libraries/shared/src/PerfStat.h +++ b/libraries/shared/src/PerfStat.h @@ -89,14 +89,19 @@ private: const char* _message; bool _renderWarningsOn; bool _alwaysDisplay; + uint64_t* _runningTotal; + uint64_t* _totalCalls; static bool _suppressShortTimings; public: - PerformanceWarning(bool renderWarnings, const char* message, bool alwaysDisplay = false) : + + PerformanceWarning(bool renderWarnings, const char* message, bool alwaysDisplay = false, uint64_t* runningTotal = NULL, uint64_t* totalCalls = NULL) : _start(usecTimestampNow()), _message(message), _renderWarningsOn(renderWarnings), - _alwaysDisplay(alwaysDisplay) { } - + _alwaysDisplay(alwaysDisplay), + _runningTotal(runningTotal), + _totalCalls(totalCalls) { } + ~PerformanceWarning(); static void setSuppressShortTimings(bool suppressShortTimings) { _suppressShortTimings = suppressShortTimings; } diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 359a154c4f..3d4759c98a 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -105,10 +105,41 @@ void setAtBit(unsigned char& byte, int bitIndex) { byte += (1 << (7 - bitIndex)); } +void clearAtBit(unsigned char& byte, int bitIndex) { + if (oneAtBit(byte, bitIndex)) { + byte -= (1 << (7 - bitIndex)); + } +} + int getSemiNibbleAt(unsigned char& byte, int bitIndex) { return (byte >> (6 - bitIndex) & 3); // semi-nibbles store 00, 01, 10, or 11 } +int getNthBit(unsigned char byte, int ordinal) { + const int ERROR = -1; + const int MIN_ORDINAL = 1; + const int MAX_ORDINAL = 8; + if (ordinal < MIN_ORDINAL || ordinal > MAX_ORDINAL) { + return ERROR; + } + int bitsSet = 0; + for (int bitIndex = 0; bitIndex < MAX_ORDINAL; bitIndex++) { + if (oneAtBit(byte, bitIndex)) { + bitsSet++; + } + if (bitsSet == ordinal) { + return bitIndex; + } + } + return ERROR; +} + +bool isBetween(int64_t value, int64_t max, int64_t min) { + return ((value <= max) && (value >= min)); +} + + + void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value) { //assert(value <= 3 && value >= 0); byte += ((value & 3) << (6 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11 diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 77c9cdd040..ece63d4737 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -55,10 +55,12 @@ void printVoxelCode(unsigned char* voxelCode); int numberOfOnes(unsigned char byte); bool oneAtBit(unsigned char byte, int bitIndex); void setAtBit(unsigned char& byte, int bitIndex); - +void clearAtBit(unsigned char& byte, int bitIndex); int getSemiNibbleAt(unsigned char& byte, int bitIndex); void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value); +int getNthBit(unsigned char byte, int ordinal); /// determines the bit placement 0-7 of the ordinal set bit + bool isInEnvironment(const char* environment); void switchToResourcesParentIfRequired(); @@ -109,4 +111,6 @@ public: static const char* valueOf(bool checkValue) { return checkValue ? "yes" : "no"; } }; +bool isBetween(int64_t value, int64_t max, int64_t min); + #endif /* defined(__hifi__SharedUtil__) */ diff --git a/libraries/voxel-server-library/src/VoxelPersistThread.cpp b/libraries/voxel-server-library/src/VoxelPersistThread.cpp index 8ad2bfaa4d..75954aacec 100644 --- a/libraries/voxel-server-library/src/VoxelPersistThread.cpp +++ b/libraries/voxel-server-library/src/VoxelPersistThread.cpp @@ -29,21 +29,38 @@ bool VoxelPersistThread::process() { _initialLoad = true; qDebug("loading voxels from file: %s...\n", _filename); - bool persistantFileRead = _tree->readFromSVOFile(_filename); + bool persistantFileRead; + + { + PerformanceWarning warn(true, "Loading Voxel File", true); + persistantFileRead = _tree->readFromSVOFile(_filename); + } + if (persistantFileRead) { - PerformanceWarning warn(true, "reaverageVoxelColors()", true); + PerformanceWarning warn(true, "Voxels Re-Averaging", true); // after done inserting all these voxels, then reaverage colors + qDebug("BEGIN Voxels Re-Averaging\n"); _tree->reaverageVoxelColors(_tree->rootNode); - qDebug("Voxels reAveraged\n"); + qDebug("DONE WITH Voxels Re-Averaging\n"); } _tree->clearDirtyBit(); // the tree is clean since we just loaded it qDebug("DONE loading voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead)); - unsigned long nodeCount = _tree->rootNode->getSubTreeNodeCount(); - unsigned long internalNodeCount = _tree->rootNode->getSubTreeInternalNodeCount(); - unsigned long leafNodeCount = _tree->rootNode->getSubTreeLeafNodeCount(); + + unsigned long nodeCount = VoxelNode::getNodeCount(); + unsigned long internalNodeCount = VoxelNode::getInternalNodeCount(); + unsigned long leafNodeCount = VoxelNode::getLeafNodeCount(); qDebug("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount); + + double usecPerGet = (double)VoxelNode::getGetChildAtIndexTime() / (double)VoxelNode::getGetChildAtIndexCalls(); + qDebug("getChildAtIndexCalls=%llu getChildAtIndexTime=%llu perGet=%lf \n", + VoxelNode::getGetChildAtIndexTime(), VoxelNode::getGetChildAtIndexCalls(), usecPerGet); + + double usecPerSet = (double)VoxelNode::getSetChildAtIndexTime() / (double)VoxelNode::getSetChildAtIndexCalls(); + qDebug("setChildAtIndexCalls=%llu setChildAtIndexTime=%llu perSet=%lf\n", + VoxelNode::getSetChildAtIndexTime(), VoxelNode::getSetChildAtIndexCalls(), usecPerSet); + } uint64_t MSECS_TO_USECS = 1000; diff --git a/libraries/voxel-server-library/src/VoxelServer.cpp b/libraries/voxel-server-library/src/VoxelServer.cpp index 1899eec02f..a24f13854b 100644 --- a/libraries/voxel-server-library/src/VoxelServer.cpp +++ b/libraries/voxel-server-library/src/VoxelServer.cpp @@ -138,18 +138,74 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) { mg_printf(connection, "%s", "HTTP/1.0 200 OK\r\n\r\n"); mg_printf(connection, "%s", "Your Voxel Server is running.\r\n"); mg_printf(connection, "%s", "Current Statistics\r\n"); - mg_printf(connection, "Voxel Node Memory Usage: %f MB\r\n", VoxelNode::getVoxelMemoryUsage() / 1000000.f); - mg_printf(connection, "Octcode Memory Usage: %f MB\r\n", VoxelNode::getOctcodeMemoryUsage() / 1000000.f); + mg_printf(connection, "%s", "\r\n"); + mg_printf(connection, "Voxel Node Memory Usage: %8.2f MB\r\n", VoxelNode::getVoxelMemoryUsage() / 1000000.f); + mg_printf(connection, "Octcode Memory Usage: %8.2f MB\r\n", VoxelNode::getOctcodeMemoryUsage() / 1000000.f); + mg_printf(connection, "External Children Memory Usage: %8.2f MB\r\n", + VoxelNode::getExternalChildrenMemoryUsage() / 1000000.f); + mg_printf(connection, "%s", " -----------\r\n"); + mg_printf(connection, " Total: %8.2f MB\r\n", VoxelNode::getTotalMemoryUsage() / 1000000.f); - VoxelTree* theTree = VoxelServer::GetInstance()->getTree(); - unsigned long nodeCount = theTree->rootNode->getSubTreeNodeCount(); - unsigned long internalNodeCount = theTree->rootNode->getSubTreeInternalNodeCount(); - unsigned long leafNodeCount = theTree->rootNode->getSubTreeLeafNodeCount(); + unsigned long nodeCount = VoxelNode::getNodeCount(); + unsigned long internalNodeCount = VoxelNode::getInternalNodeCount(); + unsigned long leafNodeCount = VoxelNode::getLeafNodeCount(); + const float AS_PERCENT = 100.0; + + mg_printf(connection, "%s", "\r\n"); mg_printf(connection, "%s", "Current Nodes in scene\r\n"); - mg_printf(connection, " Total Nodes: %lu nodes\r\n", nodeCount); - mg_printf(connection, " Internal Nodes: %lu nodes\r\n", internalNodeCount); - mg_printf(connection, " Leaf Nodes: %lu leaves\r\n", leafNodeCount); + mg_printf(connection, " Total Nodes: %10.lu nodes\r\n", nodeCount); + mg_printf(connection, " Internal Nodes: %10.lu nodes (%5.2f%%)\r\n", + internalNodeCount, ((float)internalNodeCount/(float)nodeCount) * AS_PERCENT); + mg_printf(connection, " Leaf Nodes: %10.lu nodes (%5.2f%%)\r\n", + leafNodeCount, ((float)leafNodeCount/(float)nodeCount) * AS_PERCENT); + + mg_printf(connection, "%s", "\r\n"); + mg_printf(connection, "%s", "VoxelNode Children Encoding Statistics...\r\n"); + mg_printf(connection, " Single or No Children: %10.llu nodes (%5.2f%%)\r\n", + VoxelNode::getSingleChildrenCount(), ((float)VoxelNode::getSingleChildrenCount()/(float)nodeCount) * AS_PERCENT); + mg_printf(connection, " Two Children as Offset: %10.llu nodes (%5.2f%%)\r\n", + VoxelNode::getTwoChildrenOffsetCount(), + ((float)VoxelNode::getTwoChildrenOffsetCount()/(float)nodeCount) * AS_PERCENT); + mg_printf(connection, " Two Children as External: %10.llu nodes (%5.2f%%)\r\n", + VoxelNode::getTwoChildrenExternalCount(), + ((float)VoxelNode::getTwoChildrenExternalCount()/(float)nodeCount) * AS_PERCENT); + mg_printf(connection, " Three Children as Offset: %10.llu nodes (%5.2f%%)\r\n", + VoxelNode::getThreeChildrenOffsetCount(), + ((float)VoxelNode::getThreeChildrenOffsetCount()/(float)nodeCount) * AS_PERCENT); + mg_printf(connection, " Three Children as External: %10.llu nodes (%5.2f%%)\r\n", + VoxelNode::getThreeChildrenExternalCount(), + ((float)VoxelNode::getThreeChildrenExternalCount()/(float)nodeCount) * AS_PERCENT); + mg_printf(connection, " Children as External Array: %10.llu nodes (%5.2f%%)\r\n", + VoxelNode::getExternalChildrenCount(), + ((float)VoxelNode::getExternalChildrenCount()/(float)nodeCount) * AS_PERCENT); + + uint64_t checkSum = VoxelNode::getSingleChildrenCount() + + VoxelNode::getTwoChildrenOffsetCount() + VoxelNode::getTwoChildrenExternalCount() + + VoxelNode::getThreeChildrenOffsetCount() + VoxelNode::getThreeChildrenExternalCount() + + VoxelNode::getExternalChildrenCount(); + + mg_printf(connection, "%s", " ----------------\r\n"); + mg_printf(connection, " Total: %10.llu nodes\r\n", checkSum); + mg_printf(connection, " Expected: %10.lu nodes\r\n", nodeCount); + + mg_printf(connection, "%s", "\r\n"); + mg_printf(connection, "%s", "VoxelNode Children Population Statistics...\r\n"); + checkSum = 0; + for (int i=0; i <= NUMBER_OF_CHILDREN; i++) { + checkSum += VoxelNode::getChildrenCount(i); + mg_printf(connection, " Nodes with %d children: %10.llu nodes (%5.2f%%)\r\n", i, + VoxelNode::getChildrenCount(i), ((float)VoxelNode::getChildrenCount(i)/(float)nodeCount) * AS_PERCENT); + } + mg_printf(connection, "%s", " ----------------\r\n"); + mg_printf(connection, " Total: %10.llu nodes\r\n", checkSum); + + mg_printf(connection, "%s", "\r\n"); + mg_printf(connection, "%s", "In other news....\r\n"); + mg_printf(connection, "could store 4 children internally: %10.llu nodes\r\n", + VoxelNode::getCouldStoreFourChildrenInternally()); + mg_printf(connection, "could NOT store 4 children internally: %10.llu nodes\r\n", + VoxelNode::getCouldNotStoreFourChildrenInternally()); return 1; } else { diff --git a/libraries/voxels/src/AABox.cpp b/libraries/voxels/src/AABox.cpp index 6a9dd68082..50b0958ae4 100644 --- a/libraries/voxels/src/AABox.cpp +++ b/libraries/voxels/src/AABox.cpp @@ -13,83 +13,66 @@ #include "AABox.h" #include "GeometryUtil.h" -AABox::AABox(const glm::vec3& corner, float size) : _corner(corner), _size(size, size, size), _topFarLeft(_corner + _size) -{ +AABox::AABox(const glm::vec3& corner, float size) : + _corner(corner), _scale(size) { }; -AABox::AABox(const glm::vec3& corner, float x, float y, float z) : _corner(corner), _size(x, y, z), _topFarLeft(_corner + _size) -{ +AABox::AABox() : _corner(0,0,0), _scale(0) { }; -AABox::AABox(const glm::vec3& corner, const glm::vec3& size) : _corner(corner), _size(size), _topFarLeft(_corner + _size) -{ -}; +glm::vec3 AABox::calcCenter() const { + glm::vec3 center(_corner); + center += (glm::vec3(_scale, _scale, _scale) * 0.5f); + return center; +} -AABox::AABox() : _corner(0,0,0), _size(0,0,0), _topFarLeft(0,0,0) -{ +glm::vec3 AABox::calcTopFarLeft() const { + glm::vec3 topFarLeft(_corner); + topFarLeft += glm::vec3(_scale, _scale, _scale); + return topFarLeft; }; - void AABox::scale(float scale) { _corner = _corner * scale; - _size = _size * scale; - _center = _center * scale; - _topFarLeft = _topFarLeft * scale; + _scale = _scale * scale; } - glm::vec3 AABox::getVertex(BoxVertex vertex) const { switch (vertex) { case BOTTOM_LEFT_NEAR: - return _corner + glm::vec3(_size.x, 0, 0); + return _corner + glm::vec3(_scale, 0, 0); case BOTTOM_RIGHT_NEAR: return _corner; case TOP_RIGHT_NEAR: - return _corner + glm::vec3(0, _size.y, 0); + return _corner + glm::vec3(0, _scale, 0); case TOP_LEFT_NEAR: - return _corner + glm::vec3(_size.x, _size.y, 0); + return _corner + glm::vec3(_scale, _scale, 0); case BOTTOM_LEFT_FAR: - return _corner + glm::vec3(_size.x, 0, _size.z); + return _corner + glm::vec3(_scale, 0, _scale); case BOTTOM_RIGHT_FAR: - return _corner + glm::vec3(0, 0, _size.z); + return _corner + glm::vec3(0, 0, _scale); case TOP_RIGHT_FAR: - return _corner + glm::vec3(0, _size.y, _size.z); + return _corner + glm::vec3(0, _scale, _scale); case TOP_LEFT_FAR: - return _corner + _size; + return _corner + glm::vec3(_scale, _scale, _scale); } } -void AABox::setBox(const glm::vec3& corner, const glm::vec3& size) { +void AABox::setBox(const glm::vec3& corner, float scale) { _corner = corner; - _size = size; - - // In the event that the caller gave us negative sizes, fix things up to be reasonable - if (_size.x < 0.0) { - _size.x = -size.x; - _corner.x -= _size.x; - } - if (_size.y < 0.0) { - _size.y = -size.y; - _corner.y -= _size.y; - } - if (_size.z < 0.0) { - _size.z = -size.z; - _corner.z -= _size.z; - } - _center = _corner + (_size * 0.5f); - _topFarLeft = _corner + _size; + _scale = scale; } glm::vec3 AABox::getVertexP(const glm::vec3& normal) const { glm::vec3 result = _corner; if (normal.x > 0) { - result.x += _size.x; + result.x += _scale; } if (normal.y > 0) { - result.y += _size.y; + result.y += _scale; } if (normal.z > 0) { - result.z += _size.z; + result.z += _scale; } return result; } @@ -98,15 +81,15 @@ glm::vec3 AABox::getVertexN(const glm::vec3& normal) const { glm::vec3 result = _corner; if (normal.x < 0) { - result.x += _size.x; + result.x += _scale; } if (normal.y < 0) { - result.y += _size.y; + result.y += _scale; } if (normal.z < 0) { - result.z += _size.z; + result.z += _scale; } return result; @@ -118,9 +101,9 @@ static bool isWithin(float value, float corner, float size) { } bool AABox::contains(const glm::vec3& point) const { - return isWithin(point.x, _corner.x, _size.x) && - isWithin(point.y, _corner.y, _size.y) && - isWithin(point.z, _corner.z, _size.z); + return isWithin(point.x, _corner.x, _scale) && + isWithin(point.y, _corner.y, _scale) && + isWithin(point.z, _corner.z, _scale); } bool AABox::contains(const AABox& otherBox) const { @@ -140,9 +123,9 @@ static bool isWithinExpanded(float value, float corner, float size, float expans } bool AABox::expandedContains(const glm::vec3& point, float expansion) const { - return isWithinExpanded(point.x, _corner.x, _size.x, expansion) && - isWithinExpanded(point.y, _corner.y, _size.y, expansion) && - isWithinExpanded(point.z, _corner.z, _size.z, expansion); + return isWithinExpanded(point.x, _corner.x, _scale, expansion) && + isWithinExpanded(point.y, _corner.y, _scale, expansion) && + isWithinExpanded(point.z, _corner.z, _scale, expansion); } // finds the intersection between a ray and the facing plane on one axis @@ -164,7 +147,7 @@ bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& e } // check each axis glm::vec3 expandedCorner = _corner - glm::vec3(expansion, expansion, expansion); - glm::vec3 expandedSize = _size + glm::vec3(expansion, expansion, expansion) * 2.0f; + glm::vec3 expandedSize = glm::vec3(_scale, _scale, _scale) + glm::vec3(expansion, expansion, expansion) * 2.0f; glm::vec3 direction = end - start; float axisDistance; return (findIntersection(start.x, direction.x, expandedCorner.x, expandedSize.x, axisDistance) && @@ -189,23 +172,23 @@ bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direct } // check each axis float axisDistance; - if ((findIntersection(origin.x, direction.x, _corner.x, _size.x, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) && - isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z))) { + if ((findIntersection(origin.x, direction.x, _corner.x, _scale, axisDistance) && axisDistance >= 0 && + isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) && + isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) { distance = axisDistance; face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE; return true; } - if ((findIntersection(origin.y, direction.y, _corner.y, _size.y, axisDistance) && axisDistance >= 0 && - isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x) && - isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z))) { + if ((findIntersection(origin.y, direction.y, _corner.y, _scale, axisDistance) && axisDistance >= 0 && + isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale) && + isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) { distance = axisDistance; face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE; return true; } - if ((findIntersection(origin.z, direction.z, _corner.z, _size.z, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) && - isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x))) { + if ((findIntersection(origin.z, direction.z, _corner.z, _scale, axisDistance) && axisDistance >= 0 && + isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) && + isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale))) { distance = axisDistance; face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE; return true; @@ -268,27 +251,27 @@ glm::vec3 AABox::getClosestPointOnFace(const glm::vec3& point, BoxFace face) con switch (face) { case MIN_X_FACE: return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z), - glm::vec3(_corner.x, _corner.y + _size.y, _corner.z + _size.z)); + glm::vec3(_corner.x, _corner.y + _scale, _corner.z + _scale)); case MAX_X_FACE: - return glm::clamp(point, glm::vec3(_corner.x + _size.x, _corner.y, _corner.z), - glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z)); + return glm::clamp(point, glm::vec3(_corner.x + _scale, _corner.y, _corner.z), + glm::vec3(_corner.x + _scale, _corner.y + _scale, _corner.z + _scale)); case MIN_Y_FACE: return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z), - glm::vec3(_corner.x + _size.x, _corner.y, _corner.z + _size.z)); + glm::vec3(_corner.x + _scale, _corner.y, _corner.z + _scale)); case MAX_Y_FACE: - return glm::clamp(point, glm::vec3(_corner.x, _corner.y + _size.y, _corner.z), - glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z)); + return glm::clamp(point, glm::vec3(_corner.x, _corner.y + _scale, _corner.z), + glm::vec3(_corner.x + _scale, _corner.y + _scale, _corner.z + _scale)); case MIN_Z_FACE: return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z), - glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z)); + glm::vec3(_corner.x + _scale, _corner.y + _scale, _corner.z)); case MAX_Z_FACE: - return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z + _size.z), - glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z)); + return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z + _scale), + glm::vec3(_corner.x + _scale, _corner.y + _scale, _corner.z + _scale)); } } @@ -338,7 +321,7 @@ glm::vec3 AABox::getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& glm::vec4 thirdAxisMaxPlane = getPlane((BoxFace)(thirdAxis * 2 + 1)); glm::vec4 offset = glm::vec4(0.0f, 0.0f, 0.0f, - glm::dot(glm::vec3(secondAxisMaxPlane + thirdAxisMaxPlane), _size) * 0.5f); + glm::dot(glm::vec3(secondAxisMaxPlane + thirdAxisMaxPlane), glm::vec3(_scale, _scale, _scale)) * 0.5f); glm::vec4 diagonals[] = { secondAxisMinPlane + thirdAxisMaxPlane + offset, secondAxisMaxPlane + thirdAxisMaxPlane + offset }; @@ -362,11 +345,11 @@ glm::vec3 AABox::getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& glm::vec4 AABox::getPlane(BoxFace face) const { switch (face) { case MIN_X_FACE: return glm::vec4(-1.0f, 0.0f, 0.0f, _corner.x); - case MAX_X_FACE: return glm::vec4(1.0f, 0.0f, 0.0f, -_corner.x - _size.x); + case MAX_X_FACE: return glm::vec4(1.0f, 0.0f, 0.0f, -_corner.x - _scale); case MIN_Y_FACE: return glm::vec4(0.0f, -1.0f, 0.0f, _corner.y); - case MAX_Y_FACE: return glm::vec4(0.0f, 1.0f, 0.0f, -_corner.y - _size.y); + case MAX_Y_FACE: return glm::vec4(0.0f, 1.0f, 0.0f, -_corner.y - _scale); case MIN_Z_FACE: return glm::vec4(0.0f, 0.0f, -1.0f, _corner.z); - case MAX_Z_FACE: return glm::vec4(0.0f, 0.0f, 1.0f, -_corner.z - _size.z); + case MAX_Z_FACE: return glm::vec4(0.0f, 0.0f, 1.0f, -_corner.z - _scale); } } diff --git a/libraries/voxels/src/AABox.h b/libraries/voxels/src/AABox.h index 7bb203aaf4..aec0fff450 100644 --- a/libraries/voxels/src/AABox.h +++ b/libraries/voxels/src/AABox.h @@ -36,19 +36,14 @@ enum BoxVertex { const int FACE_COUNT = 6; -class AABox -{ +class AABox { public: - AABox(const glm::vec3& corner, float size); - AABox(const glm::vec3& corner, float x, float y, float z); - AABox(const glm::vec3& corner, const glm::vec3& size); AABox(); ~AABox() {}; - void setBox(const glm::vec3& corner, float x, float y, float z) { setBox(corner,glm::vec3(x,y,z)); }; - void setBox(const glm::vec3& corner, const glm::vec3& size); + void setBox(const glm::vec3& corner, float scale); // for use in frustum computations glm::vec3 getVertexP(const glm::vec3& normal) const; @@ -57,9 +52,10 @@ public: void scale(float scale); const glm::vec3& getCorner() const { return _corner; }; - const glm::vec3& getSize() const { return _size; }; - const glm::vec3& getCenter() const { return _center; }; - const glm::vec3& getTopFarLeft() const { return _topFarLeft; }; + float getScale() const { return _scale; } + + glm::vec3 calcCenter() const; + glm::vec3 calcTopFarLeft() const; glm::vec3 getVertex(BoxVertex vertex) const; @@ -72,7 +68,6 @@ public: bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const; private: - glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const; glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const; glm::vec4 getPlane(BoxFace face) const; @@ -80,10 +75,7 @@ private: static BoxFace getOppositeFace(BoxFace face); glm::vec3 _corner; - glm::vec3 _center; - glm::vec3 _size; - glm::vec3 _topFarLeft; + float _scale; }; - #endif diff --git a/libraries/voxels/src/JurisdictionMap.cpp b/libraries/voxels/src/JurisdictionMap.cpp index 94e589c801..3da1467f46 100644 --- a/libraries/voxels/src/JurisdictionMap.cpp +++ b/libraries/voxels/src/JurisdictionMap.cpp @@ -132,7 +132,7 @@ void JurisdictionMap::init(unsigned char* rootOctalCode, const std::vector topFarLeft.x ) << 1) // 2 = left | code to + ((_position.y < bottomNearRight.y) << 2) // 4 = bottom | classify camera @@ -596,7 +597,7 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { ***/ } // set the distance from our camera position, to the closest vertex - float distance = glm::distance(getPosition(), box.getCenter()); + float distance = glm::distance(getPosition(), box.calcCenter()); projectedPolygon.setDistance(distance); projectedPolygon.setAnyInView(anyPointsInView); projectedPolygon.setAllInView(allPointsInView); @@ -609,9 +610,9 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { // axis-aligned voxels to determine which of the voxels vertices must be the furthest. No need for // squares and square-roots. Just compares. glm::vec3 ViewFrustum::getFurthestPointFromCamera(const AABox& box) const { - const glm::vec3& center = box.getCenter(); const glm::vec3& bottomNearRight = box.getCorner(); - const glm::vec3& topFarLeft = box.getTopFarLeft(); + glm::vec3 center = box.calcCenter(); + glm::vec3 topFarLeft = box.calcTopFarLeft(); glm::vec3 furthestPoint; if (_position.x < center.x) { diff --git a/libraries/voxels/src/ViewFrustum.h b/libraries/voxels/src/ViewFrustum.h index 803e52908e..9a3c69ca7c 100644 --- a/libraries/voxels/src/ViewFrustum.h +++ b/libraries/voxels/src/ViewFrustum.h @@ -97,7 +97,6 @@ public: glm::vec3 getFurthestPointFromCamera(const AABox& box) const; private: - // Used for keyhole calculations ViewFrustum::location pointInKeyhole(const glm::vec3& point) const; ViewFrustum::location sphereInKeyhole(const glm::vec3& center, float radius) const; diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index 3f3397fedb..efbbc23774 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "AABox.h" #include "OctalCode.h" @@ -23,19 +24,36 @@ uint64_t VoxelNode::_voxelMemoryUsage = 0; uint64_t VoxelNode::_octcodeMemoryUsage = 0; +uint64_t VoxelNode::_externalChildrenMemoryUsage = 0; +uint64_t VoxelNode::_voxelNodeCount = 0; +uint64_t VoxelNode::_voxelNodeLeafCount = 0; VoxelNode::VoxelNode() { unsigned char* rootCode = new unsigned char[1]; *rootCode = 0; init(rootCode); + + _voxelNodeCount++; + _voxelNodeLeafCount++; // all nodes start as leaf nodes } VoxelNode::VoxelNode(unsigned char * octalCode) { init(octalCode); + _voxelNodeCount++; + _voxelNodeLeafCount++; // all nodes start as leaf nodes } void VoxelNode::init(unsigned char * octalCode) { - _octalCode = octalCode; + int octalCodeLength = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(octalCode)); + if (octalCodeLength > sizeof(_octalCode)) { + _octalCode.pointer = octalCode; + _octcodePointer = true; + _octcodeMemoryUsage += octalCodeLength; + } else { + _octcodePointer = false; + memcpy(_octalCode.buffer, octalCode, octalCodeLength); + delete[] octalCode; + } #ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color _falseColored = false; // assume true color @@ -43,17 +61,25 @@ void VoxelNode::init(unsigned char * octalCode) { #endif _trueColor[0] = _trueColor[1] = _trueColor[2] = _trueColor[3] = 0; _density = 0.0f; + + // set up the _children union + _childBitmask = 0; + _childrenExternal = false; + _children.external = NULL; + _singleChildrenCount++; + _childrenCount[0]++; // default pointers to child nodes to NULL +#ifdef HAS_AUDIT_CHILDREN for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - _children[i] = NULL; + _childrenArray[i] = NULL; } - _childCount = 0; - _subtreeNodeCount = 1; // that's me - _subtreeLeafNodeCount = 0; // that's me +#endif // def HAS_AUDIT_CHILDREN - _glBufferIndex = GLBUFFER_INDEX_UNKNOWN; - _voxelSystem = NULL; + + _unknownBufferIndex = true; + setBufferIndex(GLBUFFER_INDEX_UNKNOWN); + setVoxelSystem(NULL); _isDirty = true; _shouldRender = false; _sourceID = 0; // hardcoded to 0 for removal of 16 bit node ID @@ -61,21 +87,29 @@ void VoxelNode::init(unsigned char * octalCode) { markWithChangedTime(); _voxelMemoryUsage += sizeof(VoxelNode); - _octcodeMemoryUsage += bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(_octalCode)); } VoxelNode::~VoxelNode() { notifyDeleteHooks(); _voxelMemoryUsage -= sizeof(VoxelNode); - _octcodeMemoryUsage -= bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(_octalCode)); - delete[] _octalCode; + _voxelNodeCount--; + if (isLeaf()) { + _voxelNodeLeafCount--; + } + + if (_octcodePointer) { + _octcodeMemoryUsage -= bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(getOctalCode())); + delete[] _octalCode.pointer; + } // delete all of this node's children for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - if (_children[i]) { - delete _children[i]; + VoxelNode* childAt = getChildAtIndex(i); + if (childAt) { + delete childAt; + setChildAtIndex(i, NULL); } } } @@ -97,24 +131,38 @@ void VoxelNode::handleSubtreeChanged(VoxelTree* myTree) { setColorFromAverageOfChildren(); } - recalculateSubTreeNodeCount(); markWithChangedTime(); } -void VoxelNode::recalculateSubTreeNodeCount() { - // Assuming the tree below me as changed, I need to recalculate my node count - _subtreeNodeCount = 1; // that's me - if (isLeaf()) { - _subtreeLeafNodeCount = 1; - } else { - _subtreeLeafNodeCount = 0; - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - if (_children[i]) { - _subtreeNodeCount += _children[i]->_subtreeNodeCount; - _subtreeLeafNodeCount += _children[i]->_subtreeLeafNodeCount; - } +const uint8_t INDEX_FOR_NULL = 0; +uint8_t VoxelNode::_nextIndex = INDEX_FOR_NULL + 1; // start at 1, 0 is reserved for NULL +std::map VoxelNode::_mapVoxelSystemPointersToIndex; +std::map VoxelNode::_mapIndexToVoxelSystemPointers; + +VoxelSystem* VoxelNode::getVoxelSystem() const { + if (_voxelSystemIndex > INDEX_FOR_NULL) { + if (_mapIndexToVoxelSystemPointers.end() != _mapIndexToVoxelSystemPointers.find(_voxelSystemIndex)) { + return _mapIndexToVoxelSystemPointers[_voxelSystemIndex]; } } + return NULL; +} + +void VoxelNode::setVoxelSystem(VoxelSystem* voxelSystem) { + if (voxelSystem == NULL) { + _voxelSystemIndex = INDEX_FOR_NULL; + } else { + uint8_t index; + if (_mapVoxelSystemPointersToIndex.end() != _mapVoxelSystemPointersToIndex.find(voxelSystem)) { + index = _mapVoxelSystemPointersToIndex[voxelSystem]; + } else { + index = _nextIndex; + _nextIndex++; + _mapVoxelSystemPointersToIndex[voxelSystem] = index; + _mapIndexToVoxelSystemPointers[index] = voxelSystem; + } + _voxelSystemIndex = index; + } } @@ -128,51 +176,749 @@ void VoxelNode::setShouldRender(bool shouldRender) { } void VoxelNode::calculateAABox() { - glm::vec3 corner; - glm::vec3 size; // copy corner into box - copyFirstVertexForCode(_octalCode,(float*)&corner); + copyFirstVertexForCode(getOctalCode(),(float*)&corner); // this tells you the "size" of the voxel - float voxelScale = 1 / powf(2, *_octalCode); - size = glm::vec3(voxelScale,voxelScale,voxelScale); - - _box.setBox(corner,size); + float voxelScale = 1 / powf(2, numberOfThreeBitSectionsInCode(getOctalCode())); + _box.setBox(corner,voxelScale); } void VoxelNode::deleteChildAtIndex(int childIndex) { - if (_children[childIndex]) { - delete _children[childIndex]; - _children[childIndex] = NULL; + VoxelNode* childAt = getChildAtIndex(childIndex); + if (childAt) { + delete childAt; + setChildAtIndex(childIndex, NULL); _isDirty = true; - _childCount--; markWithChangedTime(); + + // after deleting the child, check to see if we're a leaf + if (isLeaf()) { + _voxelNodeLeafCount++; + } } +#ifdef HAS_AUDIT_CHILDREN + auditChildren("deleteChildAtIndex()"); +#endif // def HAS_AUDIT_CHILDREN } // does not delete the node! VoxelNode* VoxelNode::removeChildAtIndex(int childIndex) { - VoxelNode* returnedChild = _children[childIndex]; - if (_children[childIndex]) { - _children[childIndex] = NULL; + VoxelNode* returnedChild = getChildAtIndex(childIndex); + if (returnedChild) { + setChildAtIndex(childIndex, NULL); _isDirty = true; - _childCount--; markWithChangedTime(); + + // after removing the child, check to see if we're a leaf + if (isLeaf()) { + _voxelNodeLeafCount++; + } } + +#ifdef HAS_AUDIT_CHILDREN + auditChildren("removeChildAtIndex()"); +#endif // def HAS_AUDIT_CHILDREN return returnedChild; } +#ifdef HAS_AUDIT_CHILDREN +void VoxelNode::auditChildren(const char* label) const { + bool auditFailed = false; + for (int childIndex = 0; childIndex < NUMBER_OF_CHILDREN; childIndex++) { + VoxelNode* testChildNew = getChildAtIndex(childIndex); + VoxelNode* testChildOld = _childrenArray[childIndex]; + + if (testChildNew != testChildOld) { + auditFailed = true; + } + } + + if (auditFailed) { + qDebug("%s... auditChildren() %s <<<< \n", label, (auditFailed ? "FAILED" : "PASSED")); + qDebug(" _childrenExternal=%s\n", debug::valueOf(_childrenExternal)); + qDebug(" childCount=%d\n", getChildCount()); + qDebug(" _childBitmask="); + outputBits(_childBitmask); + + + for (int childIndex = 0; childIndex < NUMBER_OF_CHILDREN; childIndex++) { + VoxelNode* testChildNew = getChildAtIndex(childIndex); + VoxelNode* testChildOld = _childrenArray[childIndex]; + + qDebug("child at index %d... testChildOld=%p testChildNew=%p %s \n", + childIndex, testChildOld, testChildNew , + ((testChildNew != testChildOld) ? " DOES NOT MATCH <<<< BAD <<<<" : " - OK ") + ); + } + qDebug("%s... auditChildren() <<<< DONE <<<< \n", label); + } +} +#endif // def HAS_AUDIT_CHILDREN + + +uint64_t VoxelNode::_getChildAtIndexTime = 0; +uint64_t VoxelNode::_getChildAtIndexCalls = 0; +uint64_t VoxelNode::_setChildAtIndexTime = 0; +uint64_t VoxelNode::_setChildAtIndexCalls = 0; + +uint64_t VoxelNode::_singleChildrenCount = 0; +uint64_t VoxelNode::_twoChildrenOffsetCount = 0; +uint64_t VoxelNode::_twoChildrenExternalCount = 0; +uint64_t VoxelNode::_threeChildrenOffsetCount = 0; +uint64_t VoxelNode::_threeChildrenExternalCount = 0; +uint64_t VoxelNode::_externalChildrenCount = 0; +uint64_t VoxelNode::_childrenCount[NUMBER_OF_CHILDREN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +uint64_t VoxelNode::_couldStoreFourChildrenInternally = 0; +uint64_t VoxelNode::_couldNotStoreFourChildrenInternally = 0; + +VoxelNode* VoxelNode::getChildAtIndex(int childIndex) const { + PerformanceWarning warn(false,"getChildAtIndex",false,&_getChildAtIndexTime,&_getChildAtIndexCalls); + VoxelNode* result = NULL; + int childCount = getChildCount(); + + const char* caseStr = NULL; + switch (childCount) { + case 0: + caseStr = "0 child case"; + break; + case 1: { + caseStr = "1 child case"; + int indexOne = getNthBit(_childBitmask, 1); + if (indexOne == childIndex) { + result = _children.single; + } + } break; + case 2: { + caseStr = "2 child case"; + int indexOne = getNthBit(_childBitmask, 1); + int indexTwo = getNthBit(_childBitmask, 2); + + if (_childrenExternal) { + if (indexOne == childIndex) { + result = _children.external[0]; + } else if (indexTwo == childIndex) { + result = _children.external[1]; + } + } else { + if (indexOne == childIndex) { + int32_t offset = _children.offsetsTwoChildren[0]; + result = (VoxelNode*)((uint8_t*)this + offset); + } else if (indexTwo == childIndex) { + int32_t offset = _children.offsetsTwoChildren[1]; + result = (VoxelNode*)((uint8_t*)this + offset); + } + } + } break; + case 3: { + caseStr = "3 child case"; + int indexOne = getNthBit(_childBitmask, 1); + int indexTwo = getNthBit(_childBitmask, 2); + int indexThree = getNthBit(_childBitmask, 3); + + if (_childrenExternal) { + if (indexOne == childIndex) { + result = _children.external[0]; + } else if (indexTwo == childIndex) { + result = _children.external[1]; + } else if (indexThree == childIndex) { + result = _children.external[2]; + } else { + } + } else { + int64_t offsetOne, offsetTwo, offsetThree; + decodeThreeOffsets(offsetOne, offsetTwo, offsetThree); + + if (indexOne == childIndex) { + result = (VoxelNode*)((uint8_t*)this + offsetOne); + } else if (indexTwo == childIndex) { + result = (VoxelNode*)((uint8_t*)this + offsetTwo); + } else if (indexThree == childIndex) { + result = (VoxelNode*)((uint8_t*)this + offsetThree); + } + } + } break; + default: { + caseStr = "default"; + // if we have 4 or more, we know we're in external mode, so we just need to figure out which + // slot in our external array this child is. + if (oneAtBit(_childBitmask, childIndex)) { + childCount = getChildCount(); + for (int ordinal = 1; ordinal <= childCount; ordinal++) { + int index = getNthBit(_childBitmask, ordinal); + if (index == childIndex) { + result = _children.external[ordinal-1]; + break; + } + } + } + } break; + } +#ifdef HAS_AUDIT_CHILDREN + if (result != _childrenArray[childIndex]) { + qDebug("getChildAtIndex() case:%s result<%p> != _childrenArray[childIndex]<%p> <<<<<<<<<< WARNING!!! \n", + caseStr, result,_childrenArray[childIndex]); + } +#endif // def HAS_AUDIT_CHILDREN + return result; +} + +void VoxelNode::storeTwoChildren(VoxelNode* childOne, VoxelNode* childTwo) { + int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this; + int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this; + + const int64_t minOffset = std::numeric_limits::min(); + const int64_t maxOffset = std::numeric_limits::max(); + + if (isBetween(offsetOne, maxOffset, minOffset) && isBetween(offsetTwo, maxOffset, minOffset)) { + // if previously external, then clean it up... + if (_childrenExternal) { + const int previousChildCount = 2; + _externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*); + delete[] _children.external; + _children.external = NULL; // probably not needed! + _childrenExternal = false; + } + + // encode in union + _children.offsetsTwoChildren[0] = offsetOne; + _children.offsetsTwoChildren[1] = offsetTwo; + + _twoChildrenOffsetCount++; + } else { + // encode in array + + // if not previously external, then allocate appropriately + if (!_childrenExternal) { + _childrenExternal = true; + const int newChildCount = 2; + _externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*); + _children.external = new VoxelNode*[newChildCount]; + } + _children.external[0] = childOne; + _children.external[1] = childTwo; + _twoChildrenExternalCount++; + } +} + +void VoxelNode::retrieveTwoChildren(VoxelNode*& childOne, VoxelNode*& childTwo) { + // If we previously had an external array, then get the + if (_childrenExternal) { + childOne = _children.external[0]; + childTwo = _children.external[1]; + delete[] _children.external; + _children.external = NULL; // probably not needed! + _childrenExternal = false; + _twoChildrenExternalCount--; + const int newChildCount = 2; + _externalChildrenMemoryUsage -= newChildCount * sizeof(VoxelNode*); + } else { + int64_t offsetOne = _children.offsetsTwoChildren[0]; + int64_t offsetTwo = _children.offsetsTwoChildren[1]; + childOne = (VoxelNode*)((uint8_t*)this + offsetOne); + childTwo = (VoxelNode*)((uint8_t*)this + offsetTwo); + _twoChildrenOffsetCount--; + } +} + +void VoxelNode::decodeThreeOffsets(int64_t& offsetOne, int64_t& offsetTwo, int64_t& offsetThree) const { + const uint64_t ENCODE_BITS = 21; + const uint64_t ENCODE_MASK = 0xFFFFF; + const uint64_t ENCODE_MASK_SIGN = 0x100000; + + uint64_t offsetEncodedOne = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 2)) & ENCODE_MASK; + uint64_t offsetEncodedTwo = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 1)) & ENCODE_MASK; + uint64_t offsetEncodedThree = (_children.offsetsThreeChildrenEncoded & ENCODE_MASK); + + uint64_t signEncodedOne = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 2)) & ENCODE_MASK_SIGN; + uint64_t signEncodedTwo = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 1)) & ENCODE_MASK_SIGN; + uint64_t signEncodedThree = (_children.offsetsThreeChildrenEncoded & ENCODE_MASK_SIGN); + + bool oneNegative = signEncodedOne == ENCODE_MASK_SIGN; + bool twoNegative = signEncodedTwo == ENCODE_MASK_SIGN; + bool threeNegative = signEncodedThree == ENCODE_MASK_SIGN; + + offsetOne = oneNegative ? -offsetEncodedOne : offsetEncodedOne; + offsetTwo = twoNegative ? -offsetEncodedTwo : offsetEncodedTwo; + offsetThree = threeNegative ? -offsetEncodedThree : offsetEncodedThree; +} + +void VoxelNode::encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int64_t offsetThree) { + const uint64_t ENCODE_BITS = 21; + const uint64_t ENCODE_MASK = 0xFFFFF; + const uint64_t ENCODE_MASK_SIGN = 0x100000; + + uint64_t offsetEncodedOne, offsetEncodedTwo, offsetEncodedThree; + if (offsetOne < 0) { + offsetEncodedOne = ((-offsetOne & ENCODE_MASK) | ENCODE_MASK_SIGN); + } else { + offsetEncodedOne = offsetOne & ENCODE_MASK; + } + offsetEncodedOne = offsetEncodedOne << (ENCODE_BITS * 2); + + if (offsetTwo < 0) { + offsetEncodedTwo = ((-offsetTwo & ENCODE_MASK) | ENCODE_MASK_SIGN); + } else { + offsetEncodedTwo = offsetTwo & ENCODE_MASK; + } + offsetEncodedTwo = offsetEncodedTwo << ENCODE_BITS; + + if (offsetThree < 0) { + offsetEncodedThree = ((-offsetThree & ENCODE_MASK) | ENCODE_MASK_SIGN); + } else { + offsetEncodedThree = offsetThree & ENCODE_MASK; + } + _children.offsetsThreeChildrenEncoded = offsetEncodedOne | offsetEncodedTwo | offsetEncodedThree; +} + +void VoxelNode::storeThreeChildren(VoxelNode* childOne, VoxelNode* childTwo, VoxelNode* childThree) { + int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this; + int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this; + int64_t offsetThree = (uint8_t*)childThree - (uint8_t*)this; + + const int64_t minOffset = -1048576; // what can fit in 20 bits // std::numeric_limits::min(); + const int64_t maxOffset = 1048576; // what can fit in 20 bits // std::numeric_limits::max(); + + if (isBetween(offsetOne, maxOffset, minOffset) && + isBetween(offsetTwo, maxOffset, minOffset) && + isBetween(offsetThree, maxOffset, minOffset)) { + // if previously external, then clean it up... + if (_childrenExternal) { + delete[] _children.external; + _children.external = NULL; // probably not needed! + _childrenExternal = false; + const int previousChildCount = 3; + _externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*); + } + // encode in union + encodeThreeOffsets(offsetOne, offsetTwo, offsetThree); + _threeChildrenOffsetCount++; + } else { + // encode in array + + // if not previously external, then allocate appropriately + if (!_childrenExternal) { + _childrenExternal = true; + const int newChildCount = 3; + _externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*); + _children.external = new VoxelNode*[newChildCount]; + } + _children.external[0] = childOne; + _children.external[1] = childTwo; + _children.external[2] = childThree; + _threeChildrenExternalCount++; + } +} + +void VoxelNode::retrieveThreeChildren(VoxelNode*& childOne, VoxelNode*& childTwo, VoxelNode*& childThree) { + // If we previously had an external array, then get the + if (_childrenExternal) { + childOne = _children.external[0]; + childTwo = _children.external[1]; + childThree = _children.external[2]; + delete[] _children.external; + _children.external = NULL; // probably not needed! + _childrenExternal = false; + _threeChildrenExternalCount--; + _externalChildrenMemoryUsage -= 3 * sizeof(VoxelNode*); + } else { + int64_t offsetOne, offsetTwo, offsetThree; + decodeThreeOffsets(offsetOne, offsetTwo, offsetThree); + + childOne = (VoxelNode*)((uint8_t*)this + offsetOne); + childTwo = (VoxelNode*)((uint8_t*)this + offsetTwo); + childThree = (VoxelNode*)((uint8_t*)this + offsetThree); + _threeChildrenOffsetCount--; + } +} + +void VoxelNode::checkStoreFourChildren(VoxelNode* childOne, VoxelNode* childTwo, VoxelNode* childThree, VoxelNode* childFour) { + int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this; + int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this; + int64_t offsetThree = (uint8_t*)childThree - (uint8_t*)this; + int64_t offsetFour = (uint8_t*)childFour - (uint8_t*)this; + + const int64_t minOffset = std::numeric_limits::min(); + const int64_t maxOffset = std::numeric_limits::max(); + + if (isBetween(offsetOne, maxOffset, minOffset) && + isBetween(offsetTwo, maxOffset, minOffset) && + isBetween(offsetThree, maxOffset, minOffset) && + isBetween(offsetFour, maxOffset, minOffset) + ) { + _couldStoreFourChildrenInternally++; + } else { + _couldNotStoreFourChildrenInternally++; + } +} + +void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) { + PerformanceWarning warn(false,"setChildAtIndex",false,&_setChildAtIndexTime,&_setChildAtIndexCalls); + + // Here's how we store things... + // If we have 0 or 1 children, then we just store them in the _children.single; + // If we have 2 children, + // then if we can we store them as 32 bit signed offsets from our own this pointer, + // _children.offsetsTwoChildren[0]-[1] + // these are 32 bit offsets + + unsigned char previousChildMask = _childBitmask; + int previousChildCount = getChildCount(); + if (child) { + setAtBit(_childBitmask, childIndex); + } else { + clearAtBit(_childBitmask, childIndex); + } + int newChildCount = getChildCount(); + + // track our population data + if (previousChildCount != newChildCount) { + _childrenCount[previousChildCount]--; + _childrenCount[newChildCount]++; + } + + // If we had 0 children and we still have 0 children, then there is nothing to do. + if (previousChildCount == 0 && newChildCount == 0) { + // nothing to do... + } else if ((previousChildCount == 0 || previousChildCount == 1) && newChildCount == 1) { + // If we had 0 children, and we're setting our first child or if we had 1 child, or we're resetting the same child, + // then we can just store it in _children.single + _children.single = child; + } else if (previousChildCount == 1 && newChildCount == 0) { + // If we had 1 child, and we've removed our last child, then we can just store NULL in _children.single + _children.single = NULL; + } else if (previousChildCount == 1 && newChildCount == 2) { + // If we had 1 child, and we're adding a second child, then we need to determine + // if we can use offsets to store them + + VoxelNode* childOne; + VoxelNode* childTwo; + + if (getNthBit(previousChildMask, 1) < childIndex) { + childOne = _children.single; + childTwo = child; + } else { + childOne = child; + childTwo = _children.single; + } + + _singleChildrenCount--; + storeTwoChildren(childOne, childTwo); + } else if (previousChildCount == 2 && newChildCount == 1) { + // If we had 2 children, and we're removing one, then we know we can go down to single mode + assert(child == NULL); // this is the only logical case + + int indexTwo = getNthBit(previousChildMask, 2); + bool keepChildOne = indexTwo == childIndex; + + VoxelNode* childOne; + VoxelNode* childTwo; + + retrieveTwoChildren(childOne, childTwo); + + _singleChildrenCount++; + + if (keepChildOne) { + _children.single = childOne; + } else { + _children.single = childTwo; + } + } else if (previousChildCount == 2 && newChildCount == 2) { + // If we had 2 children, and still have 2, then we know we are resetting one of our existing children + + int indexOne = getNthBit(previousChildMask, 1); + bool replaceChildOne = indexOne == childIndex; + + // If we previously had an external array, then just replace the right one... that's easy. + if (_childrenExternal) { + // technically, we could look to see if these are now in the offsets to handle be encoded, but + // we're going to go ahead and keep this as an array. + if (replaceChildOne) { + _children.external[0] = child; + } else { + _children.external[1] = child; + } + } else { + // If we were previously encoded as offsets, then we need to see if we can still encode as offsets + VoxelNode* childOne; + VoxelNode* childTwo; + + if (replaceChildOne) { + childOne = child; + childTwo = (VoxelNode*)((uint8_t*)this + _children.offsetsTwoChildren[1]); + } else { + childOne = (VoxelNode*)((uint8_t*)this + _children.offsetsTwoChildren[0]); + childTwo = child; + } + + _twoChildrenOffsetCount--; // will end up one or the other + storeTwoChildren(childOne, childTwo); + } + } else if (previousChildCount == 2 && newChildCount == 3) { + // If we had 2 children, and now have 3, then we know we are going to an external case... + + // First, decode the children... + VoxelNode* childOne; + VoxelNode* childTwo; + VoxelNode* childThree; + + // Get the existing two children out of their encoding... + retrieveTwoChildren(childOne, childTwo); + + // determine order of the existing children + int indexOne = getNthBit(previousChildMask, 1); + int indexTwo = getNthBit(previousChildMask, 2); + + if (childIndex < indexOne) { + childThree = childTwo; + childTwo = childOne; + childOne = child; + } else if (childIndex < indexTwo) { + childThree = childTwo; + childTwo = child; + } else { + childThree = child; + } + storeThreeChildren(childOne, childTwo, childThree); + } else if (previousChildCount == 3 && newChildCount == 2) { + // If we had 3 children, and now have 2, then we know we are going from an external case to a potential internal case + + // We need to determine which children we had, and which one we got rid of... + int indexOne = getNthBit(previousChildMask, 1); + int indexTwo = getNthBit(previousChildMask, 2); + + bool removeChildOne = indexOne == childIndex; + bool removeChildTwo = indexTwo == childIndex; + + VoxelNode* childOne; + VoxelNode* childTwo; + VoxelNode* childThree; + + // Get the existing two children out of their encoding... + retrieveThreeChildren(childOne, childTwo, childThree); + + if (removeChildOne) { + childOne = childTwo; + childTwo = childThree; + } else if (removeChildTwo) { + childTwo = childThree; + } else { + // removing child three, nothing to do. + } + + storeTwoChildren(childOne, childTwo); + } else if (previousChildCount == 3 && newChildCount == 3) { + // If we had 3 children, and now have 3, then we need to determine which item we're replacing... + + // We need to determine which children we had, and which one we got rid of... + int indexOne = getNthBit(previousChildMask, 1); + int indexTwo = getNthBit(previousChildMask, 2); + + bool replaceChildOne = indexOne == childIndex; + bool replaceChildTwo = indexTwo == childIndex; + + VoxelNode* childOne; + VoxelNode* childTwo; + VoxelNode* childThree; + + // Get the existing two children out of their encoding... + retrieveThreeChildren(childOne, childTwo, childThree); + + if (replaceChildOne) { + childOne = child; + } else if (replaceChildTwo) { + childTwo = child; + } else { + childThree = child; + } + + storeThreeChildren(childOne, childTwo, childThree); + } else if (previousChildCount == 3 && newChildCount == 4) { + // If we had 3 children, and now have 4, then we know we are going to an external case... + + // First, decode the children... + VoxelNode* childOne; + VoxelNode* childTwo; + VoxelNode* childThree; + VoxelNode* childFour; + + // Get the existing two children out of their encoding... + retrieveThreeChildren(childOne, childTwo, childThree); + + // determine order of the existing children + int indexOne = getNthBit(previousChildMask, 1); + int indexTwo = getNthBit(previousChildMask, 2); + int indexThree = getNthBit(previousChildMask, 3); + + if (childIndex < indexOne) { + childFour = childThree; + childThree = childTwo; + childTwo = childOne; + childOne = child; + } else if (childIndex < indexTwo) { + childFour = childThree; + childThree = childTwo; + childTwo = child; + } else if (childIndex < indexThree) { + childFour = childThree; + childThree = child; + } else { + childFour = child; + } + + // now, allocate the external... + _childrenExternal = true; + const int newChildCount = 4; + _children.external = new VoxelNode*[newChildCount]; + _externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*); + + _children.external[0] = childOne; + _children.external[1] = childTwo; + _children.external[2] = childThree; + _children.external[3] = childFour; + _externalChildrenCount++; + } else if (previousChildCount == 4 && newChildCount == 3) { + // If we had 4 children, and now have 3, then we know we are going from an external case to a potential internal case + assert(_childrenExternal); + + // We need to determine which children we had, and which one we got rid of... + int indexOne = getNthBit(previousChildMask, 1); + int indexTwo = getNthBit(previousChildMask, 2); + int indexThree = getNthBit(previousChildMask, 3); + + bool removeChildOne = indexOne == childIndex; + bool removeChildTwo = indexTwo == childIndex; + bool removeChildThree = indexThree == childIndex; + + VoxelNode* childOne = _children.external[0]; + VoxelNode* childTwo = _children.external[1]; + VoxelNode* childThree = _children.external[2]; + VoxelNode* childFour = _children.external[3]; + + if (removeChildOne) { + childOne = childTwo; + childTwo = childThree; + childThree = childFour; + } else if (removeChildTwo) { + childTwo = childThree; + childThree = childFour; + } else if (removeChildThree) { + childThree = childFour; + } else { + // removing child four, nothing to do. + } + + // clean up the external children... + _childrenExternal = false; + delete[] _children.external; + _children.external = NULL; + _externalChildrenCount--; + _externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*); + + + storeThreeChildren(childOne, childTwo, childThree); + } else if (previousChildCount == newChildCount) { + // 4 or more children, one item being replaced, we know we're stored externally, we just need to find the one + // that needs to be replaced and replace it. + for (int ordinal = 1; ordinal <= 8; ordinal++) { + int index = getNthBit(previousChildMask, ordinal); + if (index == childIndex) { + // this is our child to be replaced + int nthChild = ordinal-1; + _children.external[nthChild] = child; + break; + } + } + } else if (previousChildCount < newChildCount) { + // 4 or more children, one item being added, we know we're stored externally, we just figure out where to insert + // this child pointer into our external list + VoxelNode** newExternalList = new VoxelNode*[newChildCount]; + + int copiedCount = 0; + for (int ordinal = 1; ordinal <= newChildCount; ordinal++) { + int index = getNthBit(previousChildMask, ordinal); + if (index != -1 && index < childIndex) { + newExternalList[ordinal - 1] = _children.external[ordinal - 1]; + copiedCount++; + } else { + + // insert our new child here... + newExternalList[ordinal - 1] = child; + + // if we didn't copy all of our previous children, then we need to + if (copiedCount < previousChildCount) { + // our child needs to be inserted before this index, and everything else pushed out... + for (int oldOrdinal = ordinal; oldOrdinal <= previousChildCount; oldOrdinal++) { + newExternalList[oldOrdinal] = _children.external[oldOrdinal - 1]; + } + } + break; + } + } + delete[] _children.external; + _children.external = newExternalList; + _externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*); + _externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*); + + } else if (previousChildCount > newChildCount) { + // 4 or more children, one item being removed, we know we're stored externally, we just figure out which + // item to remove from our external list + VoxelNode** newExternalList = new VoxelNode*[newChildCount]; + + for (int ordinal = 1; ordinal <= previousChildCount; ordinal++) { + int index = getNthBit(previousChildMask, ordinal); + assert(index != -1); + if (index < childIndex) { + newExternalList[ordinal - 1] = _children.external[ordinal - 1]; + } else { + // our child needs to be removed from here, and everything else pulled in... + for (int moveOrdinal = ordinal; moveOrdinal <= newChildCount; moveOrdinal++) { + newExternalList[moveOrdinal - 1] = _children.external[moveOrdinal]; + } + break; + } + } + delete[] _children.external; + _children.external = newExternalList; + _externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*); + _externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*); + } else { + assert(false); + qDebug("THIS SHOULD NOT HAPPEN previousChildCount == %d && newChildCount == %d\n",previousChildCount, newChildCount); + } + + // check to see if we could store these 4 children locally + if (getChildCount() == 4 && _childrenExternal && _children.external) { + checkStoreFourChildren(_children.external[0], _children.external[1], _children.external[2], _children.external[3]); + } + + +#ifdef HAS_AUDIT_CHILDREN + _childrenArray[childIndex] = child; + auditChildren("setChildAtIndex()"); +#endif // def HAS_AUDIT_CHILDREN +} + + VoxelNode* VoxelNode::addChildAtIndex(int childIndex) { - if (!_children[childIndex]) { - _children[childIndex] = new VoxelNode(childOctalCode(_octalCode, childIndex)); - _children[childIndex]->setVoxelSystem(_voxelSystem); // our child is always part of our voxel system NULL ok + VoxelNode* childAt = getChildAtIndex(childIndex); + if (!childAt) { + // before adding a child, see if we're currently a leaf + if (isLeaf()) { + _voxelNodeLeafCount--; + } + + childAt = new VoxelNode(childOctalCode(getOctalCode(), childIndex)); + childAt->setVoxelSystem(getVoxelSystem()); // our child is always part of our voxel system NULL ok + + setChildAtIndex(childIndex, childAt); + _isDirty = true; - _childCount++; markWithChangedTime(); } - return _children[childIndex]; + return childAt; } // handles staging or deletion of all deep children @@ -197,14 +943,15 @@ void VoxelNode::setColorFromAverageOfChildren() { int colorArray[4] = {0,0,0,0}; float density = 0.0f; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - if (_children[i] && _children[i]->isColored()) { + VoxelNode* childAt = getChildAtIndex(i); + if (childAt && childAt->isColored()) { for (int j = 0; j < 3; j++) { - colorArray[j] += _children[i]->getTrueColor()[j]; // color averaging should always be based on true colors + colorArray[j] += childAt->getTrueColor()[j]; // color averaging should always be based on true colors } colorArray[3]++; } - if (_children[i]) { - density += _children[i]->getDensity(); + if (childAt) { + density += childAt->getDensity(); } } density /= (float) NUMBER_OF_CHILDREN; @@ -284,18 +1031,19 @@ bool VoxelNode::collapseIdenticalLeaves() { bool allChildrenMatch = true; // assume the best (ottimista) int red,green,blue; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + VoxelNode* childAt = getChildAtIndex(i); // if no child, child isn't a leaf, or child doesn't have a color - if (!_children[i] || !_children[i]->isLeaf() || !_children[i]->isColored()) { + if (!childAt || !childAt->isLeaf() || !childAt->isColored()) { allChildrenMatch=false; //qDebug("SADNESS child missing or not colored! i=%d\n",i); break; } else { if (i==0) { - red = _children[i]->getColor()[0]; - green = _children[i]->getColor()[1]; - blue = _children[i]->getColor()[2]; - } else if (red != _children[i]->getColor()[0] || - green != _children[i]->getColor()[1] || blue != _children[i]->getColor()[2]) { + red = childAt->getColor()[0]; + green = childAt->getColor()[1]; + blue = childAt->getColor()[2]; + } else if (red != childAt->getColor()[0] || + green != childAt->getColor()[1] || blue != childAt->getColor()[2]) { allChildrenMatch=false; break; } @@ -306,10 +1054,10 @@ bool VoxelNode::collapseIdenticalLeaves() { if (allChildrenMatch) { //qDebug("allChildrenMatch: pruning tree\n"); for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - delete _children[i]; // delete all the child nodes - _children[i]=NULL; // set it to NULL + VoxelNode* childAt = getChildAtIndex(i); + delete childAt; // delete all the child nodes + setChildAtIndex(i, NULL); // set it to NULL } - _childCount = 0; nodeColor collapsedColor; collapsedColor[0]=red; collapsedColor[1]=green; @@ -333,19 +1081,20 @@ void VoxelNode::setRandomColor(int minimumBrightness) { void VoxelNode::printDebugDetails(const char* label) const { unsigned char childBits = 0; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - if (_children[i]) { + VoxelNode* childAt = getChildAtIndex(i); + if (childAt) { setAtBit(childBits,i); } } qDebug("%s - Voxel at corner=(%f,%f,%f) size=%f\n isLeaf=%s isColored=%s (%d,%d,%d,%d) isDirty=%s shouldRender=%s\n children=", label, - _box.getCorner().x, _box.getCorner().y, _box.getCorner().z, _box.getSize().x, + _box.getCorner().x, _box.getCorner().y, _box.getCorner().z, _box.getScale(), debug::valueOf(isLeaf()), debug::valueOf(isColored()), getColor()[0], getColor()[1], getColor()[2], getColor()[3], debug::valueOf(isDirty()), debug::valueOf(getShouldRender())); outputBits(childBits, false); qDebug("\n octalCode="); - printOctalCode(_octalCode); + printOctalCode(getOctalCode()); } float VoxelNode::getEnclosingRadius() const { @@ -398,20 +1147,20 @@ float VoxelNode::furthestDistanceToCamera(const ViewFrustum& viewFrustum) const } float VoxelNode::distanceToCamera(const ViewFrustum& viewFrustum) const { - glm::vec3 center = _box.getCenter() * (float)TREE_SCALE; + glm::vec3 center = _box.calcCenter() * (float)TREE_SCALE; glm::vec3 temp = viewFrustum.getPosition() - center; float distanceToVoxelCenter = sqrtf(glm::dot(temp, temp)); return distanceToVoxelCenter; } float VoxelNode::distanceSquareToPoint(const glm::vec3& point) const { - glm::vec3 temp = point - _box.getCenter(); + glm::vec3 temp = point - _box.calcCenter(); float distanceSquare = glm::dot(temp, temp); return distanceSquare; } float VoxelNode::distanceToPoint(const glm::vec3& point) const { - glm::vec3 temp = point - _box.getCenter(); + glm::vec3 temp = point - _box.calcCenter(); float distance = sqrtf(glm::dot(temp, temp)); return distance; } diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index 41196c81b5..1734bb5c87 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -41,8 +41,8 @@ public: VoxelNode(unsigned char * octalCode); // regular constructor ~VoxelNode(); - unsigned char* getOctalCode() const { return _octalCode; } - VoxelNode* getChildAtIndex(int childIndex) const { return _children[childIndex]; } + const unsigned char* getOctalCode() const { return (_octcodePointer) ? _octalCode.pointer : &_octalCode.buffer[0]; } + VoxelNode* getChildAtIndex(int childIndex) const; void deleteChildAtIndex(int childIndex); VoxelNode* removeChildAtIndex(int childIndex); VoxelNode* addChildAtIndex(int childIndex); @@ -53,10 +53,9 @@ public: bool collapseIdenticalLeaves(); const AABox& getAABox() const { return _box; } - const glm::vec3& getCenter() const { return _box.getCenter(); } const glm::vec3& getCorner() const { return _box.getCorner(); } - float getScale() const { return _box.getSize().x; } // voxelScale = (1 / powf(2, *node->getOctalCode())); } - int getLevel() const { return *_octalCode + 1; } // one based or zero based? this doesn't correctly handle 2 byte case + float getScale() const { return _box.getScale(); } + int getLevel() const { return numberOfThreeBitSectionsInCode(getOctalCode()) + 1; } float getEnclosingRadius() const; @@ -72,8 +71,8 @@ public: float distanceSquareToPoint(const glm::vec3& point) const; // when you don't need the actual distance, use this. float distanceToPoint(const glm::vec3& point) const; - bool isLeaf() const { return _childCount == 0; } - int getChildCount() const { return _childCount; } + bool isLeaf() const { return getChildCount() == 0; } + int getChildCount() const { return numberOfOnes(_childBitmask); } void printDebugDetails(const char* label) const; bool isDirty() const { return _isDirty; } void clearDirtyBit() { _isDirty = false; } @@ -84,32 +83,21 @@ public: void handleSubtreeChanged(VoxelTree* myTree); glBufferIndex getBufferIndex() const { return _glBufferIndex; } - bool isKnownBufferIndex() const { return (_glBufferIndex != GLBUFFER_INDEX_UNKNOWN); } - void setBufferIndex(glBufferIndex index) { _glBufferIndex = index; } - VoxelSystem* getVoxelSystem() const { return _voxelSystem; } - void setVoxelSystem(VoxelSystem* voxelSystem) { _voxelSystem = voxelSystem; } - + bool isKnownBufferIndex() const { return !_unknownBufferIndex; } + void setBufferIndex(glBufferIndex index) { _glBufferIndex = index; _unknownBufferIndex =(index == GLBUFFER_INDEX_UNKNOWN);} + VoxelSystem* getVoxelSystem() const; + void setVoxelSystem(VoxelSystem* voxelSystem); // Used by VoxelSystem for rendering in/out of view and LOD void setShouldRender(bool shouldRender); bool getShouldRender() const { return _shouldRender; } -#ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color void setFalseColor(colorPart red, colorPart green, colorPart blue); void setFalseColored(bool isFalseColored); bool getFalseColored() { return _falseColored; } void setColor(const nodeColor& color); const nodeColor& getTrueColor() const { return _trueColor; } const nodeColor& getColor() const { return _currentColor; } -#else - void setFalseColor(colorPart red, colorPart green, colorPart blue) { /* no op */ }; - void setFalseColored(bool isFalseColored) { /* no op */ }; - bool getFalseColored() { return false; }; - void setColor(const nodeColor& color) { memcpy(_trueColor,color,sizeof(nodeColor)); }; - void setDensity(const float density) { _density = density; }; - const nodeColor& getTrueColor() const { return _trueColor; }; - const nodeColor& getColor() const { return _trueColor; }; -#endif void setDensity(float density) { _density = density; } float getDensity() const { return _density; } @@ -122,49 +110,123 @@ public: static void addUpdateHook(VoxelNodeUpdateHook* hook); static void removeUpdateHook(VoxelNodeUpdateHook* hook); - void recalculateSubTreeNodeCount(); - unsigned long getSubTreeNodeCount() const { return _subtreeNodeCount; } - unsigned long getSubTreeInternalNodeCount() const { return _subtreeNodeCount - _subtreeLeafNodeCount; } - unsigned long getSubTreeLeafNodeCount() const { return _subtreeLeafNodeCount; } + static unsigned long getNodeCount() { return _voxelNodeCount; } + static unsigned long getInternalNodeCount() { return _voxelNodeCount - _voxelNodeLeafCount; } + static unsigned long getLeafNodeCount() { return _voxelNodeLeafCount; } static uint64_t getVoxelMemoryUsage() { return _voxelMemoryUsage; } static uint64_t getOctcodeMemoryUsage() { return _octcodeMemoryUsage; } + static uint64_t getExternalChildrenMemoryUsage() { return _externalChildrenMemoryUsage; } + static uint64_t getTotalMemoryUsage() { return _voxelMemoryUsage + _octcodeMemoryUsage + _externalChildrenMemoryUsage; } + + static uint64_t getGetChildAtIndexTime() { return _getChildAtIndexTime; } + static uint64_t getGetChildAtIndexCalls() { return _getChildAtIndexCalls; } + static uint64_t getSetChildAtIndexTime() { return _setChildAtIndexTime; } + static uint64_t getSetChildAtIndexCalls() { return _setChildAtIndexCalls; } + + static uint64_t getSingleChildrenCount() { return _singleChildrenCount; } + static uint64_t getTwoChildrenOffsetCount() { return _twoChildrenOffsetCount; } + static uint64_t getTwoChildrenExternalCount() { return _twoChildrenExternalCount; } + static uint64_t getThreeChildrenOffsetCount() { return _threeChildrenOffsetCount; } + static uint64_t getThreeChildrenExternalCount() { return _threeChildrenExternalCount; } + static uint64_t getExternalChildrenCount() { return _externalChildrenCount; } + static uint64_t getChildrenCount(int childCount) { return _childrenCount[childCount]; } + + static uint64_t getCouldStoreFourChildrenInternally() { return _couldStoreFourChildrenInternally; } + static uint64_t getCouldNotStoreFourChildrenInternally() { return _couldNotStoreFourChildrenInternally; } + +#ifdef HAS_AUDIT_CHILDREN + void auditChildren(const char* label) const; +#endif // def HAS_AUDIT_CHILDREN private: + void setChildAtIndex(int childIndex, VoxelNode* child); + void storeTwoChildren(VoxelNode* childOne, VoxelNode* childTwo); + void retrieveTwoChildren(VoxelNode*& childOne, VoxelNode*& childTwo); + void storeThreeChildren(VoxelNode* childOne, VoxelNode* childTwo, VoxelNode* childThree); + void retrieveThreeChildren(VoxelNode*& childOne, VoxelNode*& childTwo, VoxelNode*& childThree); + void decodeThreeOffsets(int64_t& offsetOne, int64_t& offsetTwo, int64_t& offsetThree) const; + void encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int64_t offsetThree); + void checkStoreFourChildren(VoxelNode* childOne, VoxelNode* childTwo, VoxelNode* childThree, VoxelNode* childFour); + void calculateAABox(); void init(unsigned char * octalCode); void notifyDeleteHooks(); void notifyUpdateHooks(); - VoxelNode* _children[8]; /// Client and server, pointers to child nodes, 64 bytes AABox _box; /// Client and server, axis aligned box for bounds of this voxel, 48 bytes - unsigned char* _octalCode; /// Client and server, pointer to octal code for this node, 8 bytes + + /// Client and server, buffer containing the octal code or a pointer to octal code for this node, 8 bytes + union octalCode_t { + unsigned char buffer[8]; + unsigned char* pointer; + } _octalCode; uint64_t _lastChanged; /// Client and server, timestamp this node was last changed, 8 bytes - unsigned long _subtreeNodeCount; /// Client and server, nodes below this node, 8 bytes - unsigned long _subtreeLeafNodeCount; /// Client and server, leaves below this node, 8 bytes - glBufferIndex _glBufferIndex; /// Client only, vbo index for this voxel if being rendered, 8 bytes - VoxelSystem* _voxelSystem; /// Client only, pointer to VoxelSystem rendering this voxel, 8 bytes + /// Client and server, pointers to child nodes, various encodings + union children_t { + VoxelNode* single; + int32_t offsetsTwoChildren[2]; + uint64_t offsetsThreeChildrenEncoded; + VoxelNode** external; + } _children; + +#ifdef HAS_AUDIT_CHILDREN + VoxelNode* _childrenArray[8]; /// Only used when HAS_AUDIT_CHILDREN is enabled to help debug children encoding +#endif // def HAS_AUDIT_CHILDREN + + uint32_t _glBufferIndex : 24, /// Client only, vbo index for this voxel if being rendered, 3 bytes + _voxelSystemIndex : 8; /// Client only, index to the VoxelSystem rendering this voxel, 1 bytes + + // Support for _voxelSystemIndex, we use these static member variables to track the VoxelSystems that are + // in use by various voxel nodes. We map the VoxelSystem pointers into an 1 byte key, this limits us to at + // most 255 voxel systems in use at a time within the client. Which is far more than we need. + static uint8_t _nextIndex; + static std::map _mapVoxelSystemPointersToIndex; + static std::map _mapIndexToVoxelSystemPointers; float _density; /// Client and server, If leaf: density = 1, if internal node: 0-1 density of voxels inside, 4 bytes - int _childCount; /// Client and server, current child nodes set to non-null in _children, 4 bytes nodeColor _trueColor; /// Client and server, true color of this voxel, 4 bytes -#ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color nodeColor _currentColor; /// Client only, false color of this voxel, 4 bytes - bool _falseColored; /// Client only, is this voxel false colored, 1 bytes -#endif - bool _isDirty; /// Client only, has this voxel changed since being rendered, 1 byte - bool _shouldRender; /// Client only, should this voxel render at this time, 1 byte uint16_t _sourceID; /// Client only, stores node id of voxel server that sent his voxel, 2 bytes + unsigned char _childBitmask; // 1 byte + + bool _falseColored : 1, /// Client only, is this voxel false colored, 1 bit + _isDirty : 1, /// Client only, has this voxel changed since being rendered, 1 bit + _shouldRender : 1, /// Client only, should this voxel render at this time, 1 bit + _octcodePointer : 1, /// Client and Server only, is this voxel's octal code a pointer or buffer, 1 bit + _unknownBufferIndex : 1, + _childrenExternal : 1; /// Client only, is this voxel's VBO buffer the unknown buffer index, 1 bit static std::vector _deleteHooks; static std::vector _updateHooks; + + static uint64_t _voxelNodeCount; + static uint64_t _voxelNodeLeafCount; + static uint64_t _voxelMemoryUsage; static uint64_t _octcodeMemoryUsage; + static uint64_t _externalChildrenMemoryUsage; + + static uint64_t _getChildAtIndexTime; + static uint64_t _getChildAtIndexCalls; + static uint64_t _setChildAtIndexTime; + static uint64_t _setChildAtIndexCalls; + + static uint64_t _singleChildrenCount; + static uint64_t _twoChildrenOffsetCount; + static uint64_t _twoChildrenExternalCount; + static uint64_t _threeChildrenOffsetCount; + static uint64_t _threeChildrenExternalCount; + static uint64_t _externalChildrenCount; + static uint64_t _childrenCount[NUMBER_OF_CHILDREN + 1]; + + static uint64_t _couldStoreFourChildrenInternally; + static uint64_t _couldNotStoreFourChildrenInternally; }; #endif /* defined(__hifi__VoxelNode__) */ \ No newline at end of file diff --git a/libraries/voxels/src/VoxelSceneStats.cpp b/libraries/voxels/src/VoxelSceneStats.cpp index 1ebc4015e0..f4570d93a2 100644 --- a/libraries/voxels/src/VoxelSceneStats.cpp +++ b/libraries/voxels/src/VoxelSceneStats.cpp @@ -33,9 +33,10 @@ void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* r reset(); // resets packet and voxel stats _isStarted = true; _start = usecTimestampNow(); - _totalVoxels = root->getSubTreeNodeCount(); - _totalInternal = root->getSubTreeInternalNodeCount(); - _totalLeaves = root->getSubTreeLeafNodeCount(); + + _totalVoxels = VoxelNode::getNodeCount(); + _totalInternal = VoxelNode::getInternalNodeCount(); + _totalLeaves = VoxelNode::getLeafNodeCount(); _isFullScene = isFullScene; _isMoving = isMoving; diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 6b3623ebe3..283469b32a 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -134,7 +134,7 @@ void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseV VoxelNode* VoxelTree::nodeForOctalCode(VoxelNode* ancestorNode, - unsigned char* needleCode, VoxelNode** parentOfFoundNode) const { + const unsigned char* needleCode, VoxelNode** parentOfFoundNode) const { // find the appropriate branch index based on this ancestorNode if (*needleCode > 0) { int branchForNeedle = branchIndexWithDescendant(ancestorNode->getOctalCode(), needleCode); @@ -630,9 +630,22 @@ void VoxelTree::printTreeForDebugging(VoxelNode *startNode) { } // Note: this is an expensive call. Don't call it unless you really need to reaverage the entire tree (from startNode) -void VoxelTree::reaverageVoxelColors(VoxelNode *startNode) { +void VoxelTree::reaverageVoxelColors(VoxelNode* startNode) { // if our tree is a reaveraging tree, then we do this, otherwise we don't do anything if (_shouldReaverage) { + static int recursionCount; + if (startNode == rootNode) { + recursionCount = 0; + } else { + recursionCount++; + } + const int UNREASONABLY_DEEP_RECURSION = 20; + if (recursionCount > UNREASONABLY_DEEP_RECURSION) { + qDebug("VoxelTree::reaverageVoxelColors()... bailing out of UNREASONABLY_DEEP_RECURSION\n"); + recursionCount--; + return; + } + bool hasChildren = false; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { @@ -647,9 +660,7 @@ void VoxelTree::reaverageVoxelColors(VoxelNode *startNode) { if (hasChildren && !startNode->collapseIdenticalLeaves()) { startNode->setColorFromAverageOfChildren(); } - - // this is also a good time to recalculateSubTreeNodeCount() - startNode->recalculateSubTreeNodeCount(); + recursionCount--; } } @@ -713,6 +724,11 @@ VoxelNode* VoxelTree::getVoxelAt(float x, float y, float z, float s) const { node = NULL; } delete[] octalCode; // cleanup memory +#ifdef HAS_AUDIT_CHILDREN + if (node) { + node->auditChildren("VoxelTree::getVoxelAt()"); + } +#endif // def HAS_AUDIT_CHILDREN return node; } @@ -1981,7 +1997,7 @@ bool VoxelTree::nudgeCheck(VoxelNode* node, void* extraData) { NodeChunkArgs* args = (NodeChunkArgs*)extraData; // get octal code of this node - unsigned char* octalCode = node->getOctalCode(); + const unsigned char* octalCode = node->getOctalCode(); // get voxel position/size VoxelPositionSize unNudgedDetails; @@ -2020,7 +2036,7 @@ void VoxelTree::nudgeLeaf(VoxelNode* node, void* extraData) { NodeChunkArgs* args = (NodeChunkArgs*)extraData; // get octal code of this node - unsigned char* octalCode = node->getOctalCode(); + const unsigned char* octalCode = node->getOctalCode(); // get voxel position/size VoxelPositionSize unNudgedDetails; diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 2e42269440..0f96e45edc 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -214,7 +214,7 @@ private: static bool countVoxelsOperation(VoxelNode* node, void* extraData); - VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const; + VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, const unsigned char* needleCode, VoxelNode** parentOfFoundNode) const; VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate); int readNodeData(VoxelNode *destinationNode, unsigned char* nodeData, int bufferSizeBytes, ReadBitstreamToTreeParams& args); @@ -225,7 +225,7 @@ private: /// Octal Codes of any subtrees currently being encoded. While any of these codes is being encoded, ancestors and /// descendants of them can not be deleted. - std::set _codesBeingEncoded; + std::set _codesBeingEncoded; /// mutex lock to protect the encoding set pthread_mutex_t _encodeSetLock; diff --git a/voxel-edit/src/main.cpp b/voxel-edit/src/main.cpp index 11242452dd..244db390df 100644 --- a/voxel-edit/src/main.cpp +++ b/voxel-edit/src/main.cpp @@ -1,4 +1,4 @@ -// + // // main.cpp // Voxel Edit // @@ -233,7 +233,7 @@ void processFillSVOFile(const char* fillSVOFile) { } -int main(int argc, const char * argv[]) +int old_main(int argc, const char * argv[]) { qInstallMessageHandler(sharedMessageHandler); @@ -294,4 +294,76 @@ int main(int argc, const char * argv[]) } return 0; +} + +void unitTest(VoxelTree * tree) { + printf("unit tests...\n"); + + // We want our corner voxels to be about 1/2 meter high, and our TREE_SCALE is in meters, so... + float voxelSize = 0.5f; + + // Here's an example of how to create a voxel. + printf("creating corner points...\n"); + tree->createVoxel(0, 0, 0, voxelSize, 255, 255 ,255); + + // Here's an example of how to test if a voxel exists + VoxelNode* node = tree->getVoxelAt(0, 0, 0, voxelSize); + if (node) { + // and how to access it's color + printf("corner point 0,0,0 exists... color is (%d,%d,%d) \n", + node->getColor()[0], node->getColor()[1], node->getColor()[2]); + } + + // here's an example of how to delete a voxel + printf("attempting to delete corner point 0,0,0\n"); + tree->deleteVoxelAt(0, 0, 0, voxelSize); + + // Test to see that the delete worked... it should be FALSE... + if (tree->getVoxelAt(0, 0, 0, voxelSize)) { + printf("corner point 0,0,0 exists...\n"); + } else { + printf("corner point 0,0,0 does not exists...\n"); + } + + tree->createVoxel(0, 0, 0, voxelSize, 255, 255 ,255); + if (tree->getVoxelAt(0, 0, 0, voxelSize)) { + printf("corner point 0,0,0 exists...\n"); + } else { + printf("corner point 0,0,0 does not exists...\n"); + } + + tree->createVoxel(voxelSize, 0, 0, voxelSize, 255, 255 ,255); + if (tree->getVoxelAt(voxelSize, 0, 0, voxelSize)) { + printf("corner point voxelSize,0,0 exists...\n"); + } else { + printf("corner point voxelSize,0,0 does not exists...\n"); + } + + tree->createVoxel(0, 0, voxelSize, voxelSize, 255, 255 ,255); + if (tree->getVoxelAt(0, 0, voxelSize, voxelSize)) { + printf("corner point 0, 0, voxelSize exists...\n"); + } else { + printf("corner point 0, 0, voxelSize does not exists...\n"); + } + + tree->createVoxel(voxelSize, 0, voxelSize, voxelSize, 255, 255 ,255); + if (tree->getVoxelAt(voxelSize, 0, voxelSize, voxelSize)) { + printf("corner point voxelSize, 0, voxelSize exists...\n"); + } else { + printf("corner point voxelSize, 0, voxelSize does not exists...\n"); + } + + printf("check root voxel exists...\n"); + if (tree->getVoxelAt(0,0,0,1.0)) { + printf("of course it does\n"); + } else { + printf("WTH!?!\n"); + } + +} + + +int main(int argc, const char * argv[]) { + unitTest(&myTree); + return 0; } \ No newline at end of file