diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index e96dc5ef8b..24f7665e50 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -18,15 +18,10 @@ #include #include #include "Log.h" +#include "VoxelConstants.h" #include "VoxelSystem.h" -const int MAX_VOXELS_PER_SYSTEM = 250000; - -const int VERTICES_PER_VOXEL = 24; -const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL; -const int INDICES_PER_VOXEL = 3 * 12; - float identityVertices[] = { 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1, 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1, 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1 }; @@ -60,10 +55,6 @@ VoxelSystem::~VoxelSystem() { pthread_mutex_destroy(&bufferWriteLock); } -void VoxelSystem::setViewerAvatar(Avatar *newViewerAvatar) { - viewerAvatar = newViewerAvatar; -} - ////////////////////////////////////////////////////////////////////////////////////////// // Method: VoxelSystem::loadVoxelsFile() // Description: Loads HiFidelity encoded Voxels from a binary file. The current file @@ -115,7 +106,6 @@ float VoxelSystem::getVoxelsBytesReadPerSecondAverage() { return tree->voxelsBytesReadStats.getAverageSampleValuePerSecond(); } - int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { unsigned char command = *sourceBuffer; @@ -183,31 +173,21 @@ void VoxelSystem::copyWrittenDataToReadArrays() { pthread_mutex_unlock(&bufferWriteLock); } -int VoxelSystem::treeToArrays(VoxelNode *currentNode, const glm::vec3& nodePosition) { +int VoxelSystem::treeToArrays(VoxelNode* currentNode, const glm::vec3& nodePosition) { int voxelsAdded = 0; - float halfUnitForVoxel = powf(0.5, *currentNode->octalCode) * (0.5 * TREE_SCALE); - glm::vec3 viewerPosition = viewerAvatar->getPosition(); + glm::vec3 viewerPosition = _camera->getPosition(); //_viewerAvatar->getPosition(); // debug LOD code glm::vec3 debugNodePosition; copyFirstVertexForCode(currentNode->octalCode,(float*)&debugNodePosition); - //printf("-----------------\n"); - //printf("halfUnitForVoxel=%f\n",halfUnitForVoxel); - //printf("viewer.x=%f y=%f z=%f \n", viewerPosition.x, viewerPosition.y, viewerPosition.z); - //printf("node.x=%f y=%f z=%f \n", nodePosition[0], nodePosition[1], nodePosition[2]); - //printf("debugNodePosition.x=%f y=%f z=%f \n", debugNodePosition[0], debugNodePosition[1], debugNodePosition[2]); - float distanceToVoxelCenter = sqrtf(powf(viewerPosition.x - nodePosition[0] - halfUnitForVoxel, 2) + powf(viewerPosition.y - nodePosition[1] - halfUnitForVoxel, 2) + powf(viewerPosition.z - nodePosition[2] - halfUnitForVoxel, 2)); int renderLevel = *currentNode->octalCode + 1; int boundaryPosition = boundaryDistanceForRenderLevel(renderLevel); - //printLog("treeToArrays() renderLevel=%d distanceToVoxelCenter=%f boundaryPosition=%d\n", - // renderLevel,distanceToVoxelCenter,boundaryPosition); - bool alwaysDraw = false; // XXXBHG - temporary debug code. Flip this to true to disable LOD blurring if (alwaysDraw || distanceToVoxelCenter < boundaryPosition) { @@ -218,22 +198,6 @@ int VoxelSystem::treeToArrays(VoxelNode *currentNode, const glm::vec3& nodePosi glm::vec3 childNodePosition; copyFirstVertexForCode(currentNode->children[i]->octalCode,(float*)&childNodePosition); childNodePosition *= (float)TREE_SCALE; // scale it up - - /**** disabled ************************************************************************************************ - // Note: Stephen, I intentionally left this in so you would talk to me about it. Here's the deal, this code - // doesn't seem to work correctly. It returns X and Z flipped and the values are negative. Since we use the - // firstVertexForCode() function below to calculate the child vertex and that DOES work, I've decided to use - // that function to calculate our position for LOD handling. - // - // calculate the child's position based on the parent position - for (int j = 0; j < 3; j++) { - childNodePosition[j] = nodePosition[j]; - - if (oneAtBit(branchIndexWithDescendant(currentNode->octalCode,currentNode->children[i]->octalCode),(7 - j))) { - childNodePosition[j] -= (powf(0.5, *currentNode->children[i]->octalCode) * TREE_SCALE); - } - } - **** disabled ************************************************************************************************/ voxelsAdded += treeToArrays(currentNode->children[i], childNodePosition); } } @@ -399,13 +363,7 @@ bool VoxelSystem::randomColorOperation(VoxelNode* node, bool down, void* extraDa newColor[0] = randomColorValue(150); newColor[1] = randomColorValue(150); newColor[1] = randomColorValue(150); - - //printf("randomize color node %d was %x,%x,%x NOW %x,%x,%x\n", - // _nodeCount,node->getTrueColor()[0],node->getTrueColor()[1],node->getTrueColor()[2], - // newColor[0],newColor[1],newColor[2]); node->setColor(newColor); - } else { - //printf("not randomizing color node of %d since it has no color\n",_nodeCount); } return true; } @@ -413,7 +371,7 @@ bool VoxelSystem::randomColorOperation(VoxelNode* node, bool down, void* extraDa void VoxelSystem::randomizeVoxelColors() { _nodeCount = 0; tree->recurseTreeWithOperation(randomColorOperation); - printf("setting randomized true color for %d nodes\n",_nodeCount); + printLog("setting randomized true color for %d nodes\n",_nodeCount); setupNewVoxelsForDrawing(); } @@ -430,10 +388,6 @@ bool VoxelSystem::falseColorizeRandomOperation(VoxelNode* node, bool down, void* unsigned char newR = randomColorValue(150); unsigned char newG = randomColorValue(150); unsigned char newB = randomColorValue(150); - - printf("randomize FALSE color node %d was %x,%x,%x NOW %x,%x,%x\n", - _nodeCount,node->getTrueColor()[0],node->getTrueColor()[1],node->getTrueColor()[2], - newR,newG,newB); node->setFalseColor(newR,newG,newB); return true; // keep going! @@ -442,7 +396,7 @@ bool VoxelSystem::falseColorizeRandomOperation(VoxelNode* node, bool down, void* void VoxelSystem::falseColorizeRandom() { _nodeCount = 0; tree->recurseTreeWithOperation(falseColorizeRandomOperation); - printf("setting randomized false color for %d nodes\n",_nodeCount); + printLog("setting randomized false color for %d nodes\n",_nodeCount); setupNewVoxelsForDrawing(); } @@ -455,14 +409,13 @@ bool VoxelSystem::trueColorizeOperation(VoxelNode* node, bool down, void* extraD _nodeCount++; node->setFalseColored(false); - //printf("setting true color for node %d\n",_nodeCount); return true; } void VoxelSystem::trueColorize() { _nodeCount = 0; tree->recurseTreeWithOperation(trueColorizeOperation); - printf("setting true color for %d nodes\n",_nodeCount); + printLog("setting true color for %d nodes\n",_nodeCount); setupNewVoxelsForDrawing(); } @@ -474,37 +427,20 @@ bool VoxelSystem::falseColorizeInViewOperation(VoxelNode* node, bool down, void* return true; } - ViewFrustum* viewFrustum = (ViewFrustum*) extraData; + const ViewFrustum* viewFrustum = (const ViewFrustum*) extraData; _nodeCount++; // only do this for truely colored voxels... if (node->isColored()) { - // first calculate the AAbox for the voxel - AABox voxelBox; - node->getAABox(voxelBox); - - voxelBox.scale(TREE_SCALE); - - printf("voxelBox corner=(%f,%f,%f) x=%f\n", - voxelBox.getCorner().x, voxelBox.getCorner().y, voxelBox.getCorner().z, - voxelBox.getSize().x); - // If the voxel is outside of the view frustum, then false color it red - if (ViewFrustum::OUTSIDE == viewFrustum->boxInFrustum(voxelBox)) { + if (!node->isInView(*viewFrustum)) { // Out of view voxels are colored RED unsigned char newR = 255; unsigned char newG = 0; unsigned char newB = 0; - - //printf("voxel OUTSIDE view - FALSE colorizing node %d TRUE color is %x,%x,%x \n", - // _nodeCount,node->getTrueColor()[0],node->getTrueColor()[1],node->getTrueColor()[2]); node->setFalseColor(newR,newG,newB); - } else { - printf("voxel NOT OUTSIDE view\n"); } - } else { - printf("voxel not colored, don't consider it\n"); } return true; // keep going! @@ -513,15 +449,13 @@ bool VoxelSystem::falseColorizeInViewOperation(VoxelNode* node, bool down, void* void VoxelSystem::falseColorizeInView(ViewFrustum* viewFrustum) { _nodeCount = 0; tree->recurseTreeWithOperation(falseColorizeInViewOperation,(void*)viewFrustum); - printf("setting in view false color for %d nodes\n",_nodeCount); + printLog("setting in view false color for %d nodes\n",_nodeCount); setupNewVoxelsForDrawing(); } // Will false colorize voxels based on distance from view bool VoxelSystem::falseColorizeDistanceFromViewOperation(VoxelNode* node, bool down, void* extraData) { - //printf("falseColorizeDistanceFromViewOperation() down=%s\n",(down ? "TRUE" : "FALSE")); - // we do our operations on the way up! if (down) { return true; @@ -546,10 +480,6 @@ bool VoxelSystem::falseColorizeDistanceFromViewOperation(VoxelNode* node, bool d float halfUnitForVoxel = powf(0.5, *node->octalCode) * (0.5 * TREE_SCALE); glm::vec3 viewerPosition = viewFrustum->getPosition(); - //printf("halfUnitForVoxel=%f\n",halfUnitForVoxel); - //printf("viewer.x=%f y=%f z=%f \n", viewerPosition.x, viewerPosition.y, viewerPosition.z); - //printf("node.x=%f y=%f z=%f \n", nodePosition.x, nodePosition.y, nodePosition.z); - float distance = sqrtf(powf(viewerPosition.x - nodePosition.x - halfUnitForVoxel, 2) + powf(viewerPosition.y - nodePosition.y - halfUnitForVoxel, 2) + powf(viewerPosition.z - nodePosition.z - halfUnitForVoxel, 2)); @@ -567,12 +497,7 @@ bool VoxelSystem::falseColorizeDistanceFromViewOperation(VoxelNode* node, bool d unsigned char newR = (colorBand*(gradientOver/colorBands))+(maxColor-gradientOver); unsigned char newG = 0; unsigned char newB = 0; - //printf("Setting color down=%s distance=%f min=%f max=%f distanceRatio=%f color=%d \n", - // (down ? "TRUE" : "FALSE"), distance, _minDistance, _maxDistance, distanceRatio, (int)newR); - node->setFalseColor(newR,newG,newB); - } else { - //printf("voxel not colored, don't consider it - down=%s\n",(down ? "TRUE" : "FALSE")); } return true; // keep going! } @@ -590,8 +515,6 @@ bool VoxelSystem::getDistanceFromViewRangeOperation(VoxelNode* node, bool down, return true; } - //printf("getDistanceFromViewRangeOperation() down=%s\n",(down ? "TRUE" : "FALSE")); - ViewFrustum* viewFrustum = (ViewFrustum*) extraData; // only do this for truly colored voxels... @@ -618,11 +541,9 @@ bool VoxelSystem::getDistanceFromViewRangeOperation(VoxelNode* node, bool down, // on way down, calculate the range of distances if (distance > _maxDistance) { _maxDistance = distance; - //printf("new maxDistance=%f down=%s\n",_maxDistance, (down ? "TRUE" : "FALSE")); } if (distance < _minDistance) { _minDistance = distance; - //printf("new minDistance=%f down=%s\n",_minDistance, (down ? "TRUE" : "FALSE")); } _nodeCount++; @@ -636,11 +557,11 @@ void VoxelSystem::falseColorizeDistanceFromView(ViewFrustum* viewFrustum) { _maxDistance = 0.0; _minDistance = FLT_MAX; tree->recurseTreeWithOperation(getDistanceFromViewRangeOperation,(void*)viewFrustum); - printf("determining distance range for %d nodes\n",_nodeCount); + printLog("determining distance range for %d nodes\n",_nodeCount); _nodeCount = 0; tree->recurseTreeWithOperation(falseColorizeDistanceFromViewOperation,(void*)viewFrustum); - printf("setting in distance false color for %d nodes\n",_nodeCount); + printLog("setting in distance false color for %d nodes\n",_nodeCount); setupNewVoxelsForDrawing(); } diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index ca4825121b..f12cc7521a 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -16,6 +16,7 @@ #include #include #include "Avatar.h" +#include "Camera.h" #include "Util.h" #include "world.h" @@ -34,7 +35,8 @@ public: void render(); void setVoxelsRendered(int v) {voxelsRendered = v;}; int getVoxelsRendered() {return voxelsRendered;}; - void setViewerAvatar(Avatar *newViewerAvatar); + void setViewerAvatar(Avatar *newViewerAvatar) { _viewerAvatar = newViewerAvatar; }; + void setCamera(Camera* newCamera) { _camera = newCamera; }; void loadVoxelsFile(const char* fileName,bool wantColorRandomizer); void createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer); @@ -67,7 +69,8 @@ private: static float _minDistance; int voxelsRendered; - Avatar *viewerAvatar; + Avatar* _viewerAvatar; + Camera* _camera; VoxelTree *tree; GLfloat *readVerticesArray; GLubyte *readColorsArray; diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 634b883e08..822192d3c9 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -305,6 +305,7 @@ void init(void) { voxels.init(); voxels.setViewerAvatar(&myAvatar); + voxels.setCamera(&myCamera); handControl.setScreenDimensions(WIDTH, HEIGHT); @@ -1624,12 +1625,6 @@ int main(int argc, const char * argv[]) // we need to create a QApplication instance in order to use Qt's font rendering app = new QApplication(argc, const_cast(argv)); - // Quick test of the Orientation class on startup! - if (cmdOptionExists(argc, argv, "--testOrientation")) { - testOrientationClass(); - return EXIT_SUCCESS; - } - unsigned int listenPort = AGENT_SOCKET_LISTEN_PORT; const char* portStr = getCmdOption(argc, argv, "--listenPort"); if (portStr) { diff --git a/libraries/shared/src/OctalCode.cpp b/libraries/shared/src/OctalCode.cpp index 898eaed377..da2b017875 100644 --- a/libraries/shared/src/OctalCode.cpp +++ b/libraries/shared/src/OctalCode.cpp @@ -7,9 +7,13 @@ // #include +#include // std:min #include #include "SharedUtil.h" #include "OctalCode.h" +#include "shared_Log.h" + +using shared_lib::printLog; int numberOfThreeBitSectionsInCode(unsigned char * octalCode) { if (*octalCode == 255) { @@ -20,8 +24,13 @@ int numberOfThreeBitSectionsInCode(unsigned char * octalCode) { } void printOctalCode(unsigned char * octalCode) { - for (int i = 0; i < bytesRequiredForCodeLength(*octalCode); i++) { - outputBits(octalCode[i]); + if (!octalCode) { + printLog("NULL\n"); + } else { + for (int i = 0; i < bytesRequiredForCodeLength(*octalCode); i++) { + outputBits(octalCode[i],false); + } + printLog("\n"); } } @@ -126,3 +135,38 @@ float * firstVertexForCode(unsigned char * octalCode) { return firstVertex; } +OctalCodeComparison compareOctalCodes(unsigned char* codeA, unsigned char* codeB) { + if (!codeA || !codeB) { + return ILLEGAL_CODE; + } + + OctalCodeComparison result = LESS_THAN; // assume it's shallower + + int numberOfBytes = std::min(bytesRequiredForCodeLength(*codeA), bytesRequiredForCodeLength(*codeB)); + int compare = memcmp(codeA, codeB, numberOfBytes); + + if (compare < 0) { + result = LESS_THAN; + } else if (compare > 0) { + result = GREATER_THAN; + } else { + int codeLengthA = numberOfThreeBitSectionsInCode(codeA); + int codeLengthB = numberOfThreeBitSectionsInCode(codeB); + + if (codeLengthA == codeLengthB) { + // if the memcmp matched exactly, and they were the same length, + // then these must be the same code! + result = EXACT_MATCH; + } else { + // if the memcmp matched exactly, but they aren't the same length, + // then they have a matching common parent, but they aren't the same + if (codeLengthA < codeLengthB) { + result = LESS_THAN; + } else { + result = GREATER_THAN; + } + } + } + return result; +} + diff --git a/libraries/shared/src/OctalCode.h b/libraries/shared/src/OctalCode.h index 7569e99868..bf4a6ef699 100644 --- a/libraries/shared/src/OctalCode.h +++ b/libraries/shared/src/OctalCode.h @@ -23,4 +23,12 @@ unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber float * firstVertexForCode(unsigned char * octalCode); void copyFirstVertexForCode(unsigned char * octalCode, float* output); +typedef enum { + ILLEGAL_CODE = -2, + LESS_THAN = -1, + EXACT_MATCH = 0, + GREATER_THAN = 1 +} OctalCodeComparison; + +OctalCodeComparison compareOctalCodes(unsigned char* code1, unsigned char* code2); #endif /* defined(__hifi__OctalCode__) */ diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 533135d3ac..7c8f7f5e9b 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #ifdef _WIN32 #include "Syssocket.h" #endif @@ -52,14 +53,30 @@ bool randomBoolean() { return rand() % 2; } -void outputBits(unsigned char byte) { - printLog("%d: ", byte); +void outputBufferBits(unsigned char* buffer, int length, bool withNewLine) { + for (int i = 0; i < length; i++) { + outputBits(buffer[i], false); + } + if (withNewLine) { + printLog("\n"); + } +} + +void outputBits(unsigned char byte, bool withNewLine) { + if (isalnum(byte)) { + printLog("[ %d (%c): ", byte, byte); + } else { + printLog("[ %d (0x%x): ", byte, byte); + } for (int i = 0; i < 8; i++) { printLog("%d", byte >> (7 - i) & 1); } + printLog(" ] "); - printLog("\n"); + if (withNewLine) { + printLog("\n"); + } } int numberOfOnes(unsigned char byte) { @@ -356,3 +373,35 @@ void printVoxelCode(unsigned char* voxelCode) { } #endif + +// Inserts the value and key into three arrays sorted by the key array, the first array is the value, +// the second array is a sorted key for the value, the third array is the index for the value in it original +// non-sorted array +// returns -1 if size exceeded +int insertIntoSortedArrays(void* value, float key, int originalIndex, + void** valueArray, float* keyArray, int* originalIndexArray, + int currentCount, int maxCount) { + + if (currentCount < maxCount) { + int i = 0; + if (currentCount > 0) { + while (i < currentCount && key > keyArray[i]) { + i++; + } + // i is our desired location + // shift array elements to the right + if (i < currentCount && i+1 < maxCount) { + memcpy(&valueArray[i + 1], &valueArray[i], sizeof(void*) * (currentCount - i)); + memcpy(&keyArray[i + 1], &keyArray[i], sizeof(float) * (currentCount - i)); + memcpy(&originalIndexArray[i + 1], &originalIndexArray[i], sizeof(int) * (currentCount - i)); + } + } + // place new element at i + valueArray[i] = value; + keyArray[i] = key; + originalIndexArray[i] = originalIndex; + return currentCount + 1; + } + return -1; // error case +} + diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index e70229637a..1b40de5448 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -42,7 +42,8 @@ float randFloatInRange (float min,float max); unsigned char randomColorValue(int minimum); bool randomBoolean(); -void outputBits(unsigned char byte); +void outputBufferBits(unsigned char* buffer, int length, bool withNewLine = true); +void outputBits(unsigned char byte, bool withNewLine = true); void printVoxelCode(unsigned char* voxelCode); int numberOfOnes(unsigned char byte); bool oneAtBit(unsigned char byte, int bitIndex); @@ -70,5 +71,8 @@ bool createVoxelEditMessage(unsigned char command, short int sequence, void usleep(int waitTime); #endif +int insertIntoSortedArrays(void* value, float key, int originalIndex, + void** valueArray, float* keyArray, int* originalIndexArray, + int currentCount, int maxCount); #endif /* defined(__hifi__SharedUtil__) */ diff --git a/libraries/voxels/src/MarkerNode.cpp b/libraries/voxels/src/MarkerNode.cpp deleted file mode 100644 index f3199f4a32..0000000000 --- a/libraries/voxels/src/MarkerNode.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// -// MarkerNode.cpp -// hifi -// -// Created by Stephen Birarda on 3/26/13. -// -// - -#include "MarkerNode.h" -#include - -MarkerNode::MarkerNode() { - for (int i = 0; i < 8; i++) { - children[i] = NULL; - } - - childrenVisitedMask = 0; -} - -MarkerNode::~MarkerNode() { - for (int i = 0; i < 8; i++) { - delete children[i]; - } -} - -MarkerNode::MarkerNode(const MarkerNode &otherMarkerNode) { - childrenVisitedMask = otherMarkerNode.childrenVisitedMask; - - // recursively copy the children marker nodes - for (int i = 0; i < 8; i++) { - if (children[i] != NULL) { - children[i] = new MarkerNode(*otherMarkerNode.children[i]); - } - } -} diff --git a/libraries/voxels/src/MarkerNode.h b/libraries/voxels/src/MarkerNode.h deleted file mode 100644 index b411830325..0000000000 --- a/libraries/voxels/src/MarkerNode.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// MarkerNode.h -// hifi -// -// Created by Stephen Birarda on 3/26/13. -// -// - -#ifndef __hifi__MarkerNode__ -#define __hifi__MarkerNode__ - -class MarkerNode { -public: - MarkerNode(); - ~MarkerNode(); - MarkerNode(const MarkerNode &otherMarkerNode); - - unsigned char childrenVisitedMask; - MarkerNode *children[8]; -}; - -#endif /* defined(__hifi__MarkerNode__) */ diff --git a/libraries/voxels/src/ViewFrustum.h b/libraries/voxels/src/ViewFrustum.h index 9ebfba6e9a..d05bb9e1cf 100644 --- a/libraries/voxels/src/ViewFrustum.h +++ b/libraries/voxels/src/ViewFrustum.h @@ -92,7 +92,7 @@ public: void dump() const; - enum {OUTSIDE, INTERSECT, INSIDE}; + typedef enum {OUTSIDE, INTERSECT, INSIDE} location; int pointInFrustum(const glm::vec3& point) const; int sphereInFrustum(const glm::vec3& center, float radius) const; diff --git a/libraries/voxels/src/VoxelConstants.h b/libraries/voxels/src/VoxelConstants.h new file mode 100644 index 0000000000..66ddbcb915 --- /dev/null +++ b/libraries/voxels/src/VoxelConstants.h @@ -0,0 +1,24 @@ +// +// VoxelConstants.h +// hifi +// +// Created by Brad Hefta-Gaub on 4/29/13. +// +// +// Various important constants used throughout the system related to voxels +// +// + +#ifndef __hifi_VoxelConstants_h__ +#define __hifi_VoxelConstants_h__ + +const int MAX_VOXEL_PACKET_SIZE = 1492; +const int MAX_TREE_SLICE_BYTES = 26; +const int TREE_SCALE = 10; +const int MAX_VOXELS_PER_SYSTEM = 250000; +const int VERTICES_PER_VOXEL = 24; +const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL; +const int INDICES_PER_VOXEL = 3 * 12; +const int COLOR_VALUES_PER_VOXEL = 3 * VERTICES_PER_VOXEL; + +#endif diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index 4278d8d75e..c1527d3334 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -7,12 +7,15 @@ // #include +#include #include #include "SharedUtil.h" -//#include "voxels_Log.h" +#include "voxels_Log.h" #include "VoxelNode.h" +#include "VoxelConstants.h" #include "OctalCode.h" #include "AABox.h" +using voxels_lib::printLog; // using voxels_lib::printLog; @@ -118,7 +121,6 @@ void VoxelNode::setFalseColored(bool isFalseColored) { void VoxelNode::setColor(const nodeColor& color) { - //printf("VoxelNode::setColor() isFalseColored=%s\n",_falseColored ? "Yes" : "No"); memcpy(&_trueColor,&color,sizeof(nodeColor)); if (!_falseColored) { memcpy(&_currentColor,&color,sizeof(nodeColor)); @@ -179,3 +181,38 @@ void VoxelNode::setRandomColor(int minimumBrightness) { newColor[3] = 1; setColor(newColor); } + +bool VoxelNode::isLeaf() const { + for (int i = 0; i < 8; i++) { + if (children[i]) { + return false; + } + } + return true; +} + +void VoxelNode::printDebugDetails(const char* label) const { + AABox box; + getAABox(box); + printLog("%s - Voxel at corner=(%f,%f,%f) size=%f octcode=", label, + box.getCorner().x, box.getCorner().y, box.getCorner().z, box.getSize().x); + printOctalCode(octalCode); +} + + +bool VoxelNode::isInView(const ViewFrustum& viewFrustum) const { + AABox box; + getAABox(box); + box.scale(TREE_SCALE); + bool inView = (ViewFrustum::OUTSIDE != viewFrustum.boxInFrustum(box)); + return inView; +} + +float VoxelNode::distanceToCamera(const ViewFrustum& viewFrustum) const { + AABox box; + getAABox(box); + float distanceToVoxelCenter = sqrtf(powf(viewFrustum.getPosition().x - (box.getCorner().x + box.getSize().x), 2) + + powf(viewFrustum.getPosition().y - (box.getCorner().y + box.getSize().y), 2) + + powf(viewFrustum.getPosition().z - (box.getCorner().z + box.getSize().z), 2)); + return distanceToVoxelCenter; +} diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index cf644a1fd1..4cda55f8a7 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -10,6 +10,7 @@ #define __hifi__VoxelNode__ #include "AABox.h" +#include "ViewFrustum.h" typedef unsigned char colorPart; typedef unsigned char nodeColor[4]; @@ -34,6 +35,11 @@ public: VoxelNode *children[8]; bool isColored() const { return (_trueColor[3]==1); }; + bool isInView(const ViewFrustum& viewFrustum) const; + float distanceToCamera(const ViewFrustum& viewFrustum) const; + bool isLeaf() const; + void getAABox(AABox& box) const; + void printDebugDetails(const char* label) const; #ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color void setFalseColor(colorPart red, colorPart green, colorPart blue); @@ -50,8 +56,6 @@ public: const nodeColor& getTrueColor() const { return _trueColor; }; const nodeColor& getColor() const { return _trueColor; }; #endif - - void getAABox(AABox& box) const; }; #endif /* defined(__hifi__VoxelNode__) */ diff --git a/libraries/voxels/src/VoxelNodeBag.cpp b/libraries/voxels/src/VoxelNodeBag.cpp new file mode 100644 index 0000000000..e6b20cbf4b --- /dev/null +++ b/libraries/voxels/src/VoxelNodeBag.cpp @@ -0,0 +1,90 @@ +// +// VoxelNodeBag.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 4/25/2013 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include "VoxelNodeBag.h" +#include + +VoxelNodeBag::~VoxelNodeBag() { + deleteAll(); +} + +void VoxelNodeBag::deleteAll() { + if (_bagElements) { + delete[] _bagElements; + } + _bagElements = NULL; + _elementsInUse = 0; + _sizeOfElementsArray = 0; +} + + +const int GROW_BAG_BY = 100; + +// put a node into the bag +void VoxelNodeBag::insert(VoxelNode* node) { + + // Search for where we should live in the bag (sorted) + // Note: change this to binary search... instead of linear! + int insertAt = _elementsInUse; + for (int i = 0; i < _elementsInUse; i++) { + + // compare the newNode to the elements already in the bag + OctalCodeComparison comparison = compareOctalCodes(_bagElements[i]->octalCode, node->octalCode); + + // If we found a code in the bag that matches, then just return, since the element is already in the bag. + if (comparison == EXACT_MATCH) { + return; // exit early!! + } + + // if we found a node "greater than" the inserted node, then + // we want to insert our node here. + if (comparison == GREATER_THAN) { + insertAt = i; + break; + } + } + // at this point, inserAt will be the location we want to insert at. + + // If we don't have room in our bag, then grow the bag + if (_sizeOfElementsArray < _elementsInUse + 1) { + VoxelNode** oldBag = _bagElements; + _bagElements = new VoxelNode * [_sizeOfElementsArray + GROW_BAG_BY]; + _sizeOfElementsArray += GROW_BAG_BY; + + // If we had an old bag... + if (oldBag) { + // copy old elements into the new bag, but leave a space where we need to + // insert the new node + memcpy(_bagElements, oldBag, insertAt * sizeof(VoxelNode*)); + memcpy(&_bagElements[insertAt + 1], &oldBag[insertAt], (_elementsInUse - insertAt) * sizeof(VoxelNode*)); + delete[] oldBag; + } + } else { + // move existing elements further back in the bag array, leave a space where we need to + // insert the new node + memmove(&_bagElements[insertAt + 1], &_bagElements[insertAt], (_elementsInUse - insertAt) * sizeof(VoxelNode*)); + } + _bagElements[insertAt] = node; + _elementsInUse++; +} + +// pull a node out of the bag (could come in any order) +VoxelNode* VoxelNodeBag::extract() { + // pull the last node out, and shrink our list... + if (_elementsInUse) { + + // get the last element + VoxelNode* node = _bagElements[_elementsInUse - 1]; + + // reduce the count + _elementsInUse--; + + return node; + } + return NULL; +} diff --git a/libraries/voxels/src/VoxelNodeBag.h b/libraries/voxels/src/VoxelNodeBag.h new file mode 100644 index 0000000000..9eb6a91e6a --- /dev/null +++ b/libraries/voxels/src/VoxelNodeBag.h @@ -0,0 +1,44 @@ +// +// VoxelNodeBag.h +// hifi +// +// Created by Brad Hefta-Gaub on 4/25/2013 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// This class is used by the VoxelTree:encodeTreeBitstream() functions to store extra nodes that need to be sent +// it's a generic bag style storage mechanism. But It has the property that you can't put the same node into the bag +// more than once (in other words, it de-dupes automatically), also, it supports collapsing it's several peer nodes +// into a parent node in cases where you add enough peers that it makes more sense to just add the parent. +// + +#ifndef __hifi__VoxelNodeBag__ +#define __hifi__VoxelNodeBag__ + +#include "VoxelNode.h" + +class VoxelNodeBag { + +public: + VoxelNodeBag() : + _bagElements(NULL), + _elementsInUse(0), + _sizeOfElementsArray(0) {}; + + ~VoxelNodeBag(); + + void insert(VoxelNode* node); // put a node into the bag + VoxelNode* extract(); // pull a node out of the bag (could come in any order) + + bool isEmpty() const { return (_elementsInUse == 0); }; + int count() const { return _elementsInUse; }; + + void deleteAll(); + +private: + + VoxelNode** _bagElements; + int _elementsInUse; + int _sizeOfElementsArray; +}; + +#endif /* defined(__hifi__VoxelNodeBag__) */ diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index db69535e14..cbbccedb59 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -17,33 +17,16 @@ #include "PacketHeaders.h" #include "OctalCode.h" #include "VoxelTree.h" +#include "VoxelNodeBag.h" #include "ViewFrustum.h" #include // to load voxels from file +#include "VoxelConstants.h" using voxels_lib::printLog; int boundaryDistanceForRenderLevel(unsigned int renderLevel) { - switch (renderLevel) { - case 1: - case 2: - case 3: - return 100; - case 4: - return 75; - break; - case 5: - return 50; - break; - case 6: - return 25; - break; - case 7: - return 12; - break; - default: - return 6; - break; - } + float voxelSizeScale = 5000.0; + return voxelSizeScale / powf(2, renderLevel); } VoxelTree::VoxelTree() : @@ -117,12 +100,19 @@ VoxelNode * VoxelTree::nodeForOctalCode(VoxelNode *ancestorNode, unsigned char * return ancestorNode; } -VoxelNode * VoxelTree::createMissingNode(VoxelNode *lastParentNode, unsigned char *codeToReach) { +// returns the node created! +VoxelNode* VoxelTree::createMissingNode(VoxelNode* lastParentNode, unsigned char* codeToReach) { + int indexOfNewChild = branchIndexWithDescendant(lastParentNode->octalCode, codeToReach); - lastParentNode->addChildAtIndex(indexOfNewChild); + // we could be coming down a branch that was already created, so don't stomp on it. + if (lastParentNode->children[indexOfNewChild] == NULL) { + lastParentNode->addChildAtIndex(indexOfNewChild); + } + + // This works because we know we traversed down the same tree so if the length is the same, then the whole code is the same if (*lastParentNode->children[indexOfNewChild]->octalCode == *codeToReach) { - return lastParentNode; + return lastParentNode->children[indexOfNewChild]; } else { return createMissingNode(lastParentNode->children[indexOfNewChild], codeToReach); } @@ -131,23 +121,21 @@ VoxelNode * VoxelTree::createMissingNode(VoxelNode *lastParentNode, unsigned cha // BHG Notes: We appear to call this function for every Voxel Node getting created. // This is recursive in nature. So, for example, if we are given an octal code for // a 1/256th size voxel, we appear to call this function 8 times. Maybe?? -int VoxelTree::readNodeData(VoxelNode *destinationNode, - unsigned char * nodeData, +int VoxelTree::readNodeData(VoxelNode* destinationNode, + unsigned char* nodeData, int bytesLeftToRead) { - // instantiate variable for bytes already read int bytesRead = 1; for (int i = 0; i < 8; i++) { // check the colors mask to see if we have a child to color in if (oneAtBit(*nodeData, i)) { - // create the child if it doesn't exist if (destinationNode->children[i] == NULL) { destinationNode->addChildAtIndex(i); this->voxelsCreated++; this->voxelsCreatedStats.updateAverage(1); } - + // pull the color for this child nodeColor newColor; memcpy(newColor, nodeData + bytesRead, 3); @@ -159,10 +147,9 @@ int VoxelTree::readNodeData(VoxelNode *destinationNode, bytesRead += 3; } } - // average node's color based on color of children destinationNode->setColorFromAverageOfChildren(); - + // give this destination node the child mask from the packet unsigned char childMask = *(nodeData + bytesRead); @@ -193,68 +180,83 @@ int VoxelTree::readNodeData(VoxelNode *destinationNode, } void VoxelTree::readBitstreamToTree(unsigned char * bitstream, int bufferSizeBytes) { - VoxelNode *bitstreamRootNode = nodeForOctalCode(rootNode, (unsigned char *)bitstream, NULL); - - if (*bitstream != *bitstreamRootNode->octalCode) { - // if the octal code returned is not on the same level as - // the code being searched for, we have VoxelNodes to create - bitstreamRootNode = createMissingNode(bitstreamRootNode, (unsigned char *)bitstream); + int bytesRead = 0; + unsigned char* bitstreamAt = bitstream; + + // Keep looping through the buffer calling readNodeData() this allows us to pack multiple root-relative Octal codes + // into a single network packet. readNodeData() basically goes down a tree from the root, and fills things in from there + // if there are more bytes after that, it's assumed to be another root relative tree + + while (bitstreamAt < bitstream + bufferSizeBytes) { + VoxelNode* bitstreamRootNode = nodeForOctalCode(rootNode, (unsigned char *)bitstreamAt, NULL); + if (*bitstreamAt != *bitstreamRootNode->octalCode) { + // if the octal code returned is not on the same level as + // the code being searched for, we have VoxelNodes to create + + // Note: we need to create this node relative to root, because we're assuming that the bitstream for the initial + // octal code is always relative to root! + bitstreamRootNode = createMissingNode(rootNode, (unsigned char*) bitstreamAt); + } + + int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt); + int theseBytesRead = 0; + theseBytesRead += octalCodeBytes; + theseBytesRead += readNodeData(bitstreamRootNode, bitstreamAt + octalCodeBytes, + bufferSizeBytes - (bytesRead + octalCodeBytes)); + + // skip bitstream to new startPoint + bitstreamAt += theseBytesRead; + bytesRead += theseBytesRead; } - - int octalCodeBytes = bytesRequiredForCodeLength(*bitstream); - readNodeData(bitstreamRootNode, bitstream + octalCodeBytes, bufferSizeBytes - octalCodeBytes); - + this->voxelsBytesRead += bufferSizeBytes; - this->voxelsBytesReadStats.updateAverage(bufferSizeBytes); + this->voxelsBytesReadStats.updateAverage(bufferSizeBytes); } // Note: uses the codeColorBuffer format, but the color's are ignored, because // this only finds and deletes the node from the tree. void VoxelTree::deleteVoxelCodeFromTree(unsigned char *codeBuffer) { - VoxelNode* parentNode = NULL; + VoxelNode* parentNode = NULL; VoxelNode* nodeToDelete = nodeForOctalCode(rootNode, codeBuffer, &parentNode); - + // If the node exists... - int lengthInBytes = bytesRequiredForCodeLength(*codeBuffer); // includes octet count, not color! + int lengthInBytes = bytesRequiredForCodeLength(*codeBuffer); // includes octet count, not color! if (0 == memcmp(nodeToDelete->octalCode,codeBuffer,lengthInBytes)) { - float* vertices = firstVertexForCode(nodeToDelete->octalCode); - delete []vertices; + float* vertices = firstVertexForCode(nodeToDelete->octalCode); + delete[] vertices; - if (parentNode) { - float* vertices = firstVertexForCode(parentNode->octalCode); - delete []vertices; - - int childNDX = branchIndexWithDescendant(parentNode->octalCode, codeBuffer); + if (parentNode) { + float* vertices = firstVertexForCode(parentNode->octalCode); + delete[] vertices; + + int childIndex = branchIndexWithDescendant(parentNode->octalCode, codeBuffer); - delete parentNode->children[childNDX]; // delete the child nodes - parentNode->children[childNDX]=NULL; // set it to NULL + delete parentNode->children[childIndex]; // delete the child nodes + parentNode->children[childIndex] = NULL; // set it to NULL - reaverageVoxelColors(rootNode); // Fix our colors!! Need to call it on rootNode - } + reaverageVoxelColors(rootNode); // Fix our colors!! Need to call it on rootNode + } } } void VoxelTree::eraseAllVoxels() { - - // XXXBHG Hack attack - is there a better way to erase the voxel tree? - - delete rootNode; // this will recurse and delete all children - rootNode = new VoxelNode(); - rootNode->octalCode = new unsigned char[1]; - *rootNode->octalCode = 0; + // XXXBHG Hack attack - is there a better way to erase the voxel tree? + delete rootNode; // this will recurse and delete all children + rootNode = new VoxelNode(); + rootNode->octalCode = new unsigned char[1]; + *rootNode->octalCode = 0; } void VoxelTree::readCodeColorBufferToTree(unsigned char *codeColorBuffer) { - VoxelNode *lastCreatedNode = nodeForOctalCode(rootNode, codeColorBuffer, NULL); - + VoxelNode* lastCreatedNode = nodeForOctalCode(rootNode, codeColorBuffer, NULL); + // create the node if it does not exist if (*lastCreatedNode->octalCode != *codeColorBuffer) { - VoxelNode *parentNode = createMissingNode(lastCreatedNode, codeColorBuffer); - lastCreatedNode = parentNode->children[branchIndexWithDescendant(parentNode->octalCode, codeColorBuffer)]; + lastCreatedNode = createMissingNode(lastCreatedNode, codeColorBuffer); } - + // give this node its color int octalCodeBytes = bytesRequiredForCodeLength(*codeColorBuffer); @@ -264,273 +266,6 @@ void VoxelTree::readCodeColorBufferToTree(unsigned char *codeColorBuffer) { lastCreatedNode->setColor(newColor); } -unsigned char * VoxelTree::loadBitstreamBuffer(unsigned char *& bitstreamBuffer, - VoxelNode *currentVoxelNode, - MarkerNode *currentMarkerNode, - const glm::vec3& agentPosition, - float thisNodePosition[3], - const ViewFrustum& viewFrustum, - bool viewFrustumCulling, - unsigned char * stopOctalCode) -{ - static unsigned char *initialBitstreamPos = bitstreamBuffer; - - unsigned char * childStopOctalCode = NULL; - - if (stopOctalCode == NULL) { - stopOctalCode = rootNode->octalCode; - } - - // check if we have any children - bool hasAtLeastOneChild; - - for (int i = 0; i < 8; i++) { - if (currentVoxelNode->children[i] != NULL) { - hasAtLeastOneChild = true; - } - } - - // if we have at least one child, check if it will be worth recursing into our children - if (hasAtLeastOneChild) { - - int firstIndexToCheck = 0; - unsigned char * childMaskPointer = NULL; - - float halfUnitForVoxel = powf(0.5, *currentVoxelNode->octalCode) * (0.5 * TREE_SCALE); - float distanceToVoxelCenter = sqrtf(powf(agentPosition[0] - thisNodePosition[0] - halfUnitForVoxel, 2) + - powf(agentPosition[1] - thisNodePosition[1] - halfUnitForVoxel, 2) + - powf(agentPosition[2] - thisNodePosition[2] - halfUnitForVoxel, 2)); - - // If the voxel is outside of the view frustum, then don't bother sending or recursing - bool voxelInView = true; - - /**** not yet working properly at this level! ************************************************************************** - if (viewFrustumCulling) { - float fullUnitForVoxel = halfUnitForVoxel * 2.0f; - AABox voxelBox; - voxelBox.setBox(glm::vec3(thisNodePosition[0],thisNodePosition[1],thisNodePosition[2]), - fullUnitForVoxel,fullUnitForVoxel,fullUnitForVoxel); - - //printf("VoxelTree::loadBitstreamBuffer() voxelBox.corner=(%f,%f,%f) x=%f \n", - // voxelBox.getCorner().x,voxelBox.getCorner().y,voxelBox.getCorner().z, voxelBox.getSize().x); - - voxelInView = (ViewFrustum::OUTSIDE != viewFrustum.pointInFrustum(voxelBox.getCorner())); - } else { - voxelInView = true; - } - **********************************************************************************************************************/ - - // if the distance to this voxel's center is less than the threshold - // distance for its children, we should send the children - bool voxelIsClose = (distanceToVoxelCenter < boundaryDistanceForRenderLevel(*currentVoxelNode->octalCode + 1)); - bool sendVoxel = voxelIsClose && voxelInView; - - //printf("VoxelTree::loadBitstreamBuffer() sendVoxel=%d, voxelIsClose=%d, voxelInView=%d, viewFrustumCulling=%d\n", - // sendVoxel, voxelIsClose, voxelInView, viewFrustumCulling); - - if (sendVoxel) { - - // write this voxel's data if we're below or at - // or at the same level as the stopOctalCode - - if (*currentVoxelNode->octalCode >= *stopOctalCode) { - if ((bitstreamBuffer - initialBitstreamPos) + MAX_TREE_SLICE_BYTES > MAX_VOXEL_PACKET_SIZE) { - // we can't send this packet, not enough room - // return our octal code as the stop - return currentVoxelNode->octalCode; - } - - if (strcmp((char *)stopOctalCode, (char *)currentVoxelNode->octalCode) == 0) { - // this is is the root node for this packet - // add the leading V - *(bitstreamBuffer++) = PACKET_HEADER_VOXEL_DATA; - - // add its octal code to the packet - int octalCodeBytes = bytesRequiredForCodeLength(*currentVoxelNode->octalCode); - - memcpy(bitstreamBuffer, currentVoxelNode->octalCode, octalCodeBytes); - bitstreamBuffer += octalCodeBytes; - } - - // default color mask is 0, increment pointer for colors - *bitstreamBuffer = 0; - - // keep a colorPointer so we can check how many colors were added - unsigned char *colorPointer = bitstreamBuffer + 1; - - for (int i = 0; i < 8; i++) { - - // Rules for including a child: - // 1) child must exists - if ((currentVoxelNode->children[i] != NULL)) { - // 2) child must have a color... - if (currentVoxelNode->children[i]->isColored()) { - - unsigned char* childOctalCode = currentVoxelNode->children[i]->octalCode; - - float childPosition[3]; - copyFirstVertexForCode(childOctalCode,(float*)&childPosition); - childPosition[0] *= TREE_SCALE; // scale it up - childPosition[1] *= TREE_SCALE; // scale it up - childPosition[2] *= TREE_SCALE; // scale it up - - float halfChildVoxel = powf(0.5, *childOctalCode) * (0.5 * TREE_SCALE); - float distanceToChildCenter = sqrtf(powf(agentPosition[0] - childPosition[0] - halfChildVoxel, 2) + - powf(agentPosition[1] - childPosition[1] - halfChildVoxel, 2) + - powf(agentPosition[2] - childPosition[2] - halfChildVoxel, 2)); - - float fullChildVoxel = halfChildVoxel * 2.0f; - AABox childBox; - childBox.setBox(glm::vec3(childPosition[0], childPosition[1], childPosition[2]), - fullChildVoxel, fullChildVoxel, fullChildVoxel); - - //printf("VoxelTree::loadBitstreamBuffer() childBox.corner=(%f,%f,%f) x=%f \n", - // childBox.getCorner().x,childBox.getCorner().y,childBox.getCorner().z, childBox.getSize().x); - - // XXXBHG - not sure we want to do this "distance/LOD culling" at this level. - //bool childIsClose = (distanceToChildCenter < boundaryDistanceForRenderLevel(*childOctalCode + 1)); - - bool childIsClose = true; // for now, assume we're close enough - bool childInView = !viewFrustumCulling || - (ViewFrustum::OUTSIDE != viewFrustum.boxInFrustum(childBox)); - - /// XXXBHG - debug code, switch this to true, and we'll send everything but include false coloring - // on voxels based on whether or not they match these rules. - bool falseColorInsteadOfCulling = false; - - // removed childIsClose - until we determine if we want to include that - bool sendChild = (childInView) || falseColorInsteadOfCulling; - - //printf("VoxelTree::loadBitstreamBuffer() childIsClose=%d, childInView=%d\n", - // childIsClose, childInView); - - // if we sendAnyway, we'll do false coloring of the voxels based on childIsClose && childInView - if (sendChild) { - - // copy in the childs color to bitstreamBuffer - if (childIsClose && childInView) { - // true color - memcpy(colorPointer, currentVoxelNode->children[i]->getTrueColor(), 3); - } else { - unsigned char red[3] = {255,0,0}; - unsigned char green[3] = {0,255,0}; - unsigned char blue[3] = {0,0,255}; - if (!childIsClose && !childInView) { - // If both too far, and not in view, color them red - memcpy(colorPointer, red, 3); - } else if (!childIsClose) { - // If too far, but in view, color them blue - memcpy(colorPointer, blue, 3); - } else { - // If close, but out of view, color them green - memcpy(colorPointer, green, 3); - } - } - colorPointer += 3; - - // set the colorMask by bitshifting the value of childExists - *bitstreamBuffer += (1 << (7 - i)); - } - } - } - } - - // push the bitstreamBuffer forwards for the number of added colors - bitstreamBuffer += (colorPointer - bitstreamBuffer); - - // maintain a pointer to this spot in the buffer so we can set our child mask - // depending on the results of the recursion below - childMaskPointer = bitstreamBuffer++; - - // reset the childMaskPointer for this node to 0 - *childMaskPointer = 0; - } else { - firstIndexToCheck = *stopOctalCode > 0 - ? branchIndexWithDescendant(currentVoxelNode->octalCode, stopOctalCode) - : 0; - } - - unsigned char * arrBufferBeforeChild = bitstreamBuffer; - - for (int i = firstIndexToCheck; i < 8; i ++) { - - // ask the child to load this bitstream buffer - // if they or their descendants fill the MTU we will receive the childStopOctalCode back - if (currentVoxelNode->children[i] != NULL) { - - if (!oneAtBit(currentMarkerNode->childrenVisitedMask, i)) { - - // create the marker node for this child if it does not yet exist - if (currentMarkerNode->children[i] == NULL) { - currentMarkerNode->children[i] = new MarkerNode(); - } - - float childNodePosition[3]; - copyFirstVertexForCode(currentVoxelNode->children[i]->octalCode,(float*)&childNodePosition); - childNodePosition[0] *= TREE_SCALE; // scale it up - childNodePosition[1] *= TREE_SCALE; // scale it up - childNodePosition[2] *= TREE_SCALE; // scale it up - - /**** disabled ***************************************************************************************** - // Note: Stephen, I intentionally left this in so you would talk to me about it. Here's the deal, this - // code doesn't seem to work correctly. It returns X and Z flipped and the values are negative. Since - // we use the firstVertexForCode() function in VoxelSystem to calculate the child vertex and that DOES - // work, I've decided to use that function to calculate our position for LOD handling. - // - // calculate the child's position based on the parent position - for (int j = 0; j < 3; j++) { - childNodePosition[j] = thisNodePosition[j]; - - if (oneAtBit(branchIndexWithDescendant(currentVoxelNode->octalCode, - currentVoxelNode->children[i]->octalCode), - (7 - j))) { - childNodePosition[j] -= (powf(0.5, *currentVoxelNode->children[i]->octalCode) * TREE_SCALE); - } - } - **** disabled *****************************************************************************************/ - - // ask the child to load the bitstream buffer with their data - childStopOctalCode = loadBitstreamBuffer(bitstreamBuffer, - currentVoxelNode->children[i], - currentMarkerNode->children[i], - agentPosition, - childNodePosition, - viewFrustum, - viewFrustumCulling, - stopOctalCode); - - if (bitstreamBuffer - arrBufferBeforeChild > 0) { - // this child added data to the packet - add it to our child mask - if (childMaskPointer != NULL) { - *childMaskPointer += (1 << (7 - i)); - } - - arrBufferBeforeChild = bitstreamBuffer; - } - } - } - - if (childStopOctalCode != NULL) { - break; - } else { - // this child node has been covered - // add the appropriate bit to the childrenVisitedMask for the current marker node - currentMarkerNode->childrenVisitedMask += 1 << (7 - i); - - // if we are above the stopOctal and we got a NULL code - // we cannot go to the next child - // so break and return the NULL stop code - if (*currentVoxelNode->octalCode < *stopOctalCode) { - break; - } - } - } - } - } - - return childStopOctalCode; -} - void VoxelTree::processRemoveVoxelBitstream(unsigned char * bitstream, int bufferSizeBytes) { // XXXBHG: validate buffer is at least 4 bytes long? other guards?? unsigned short int itemNumber = (*((unsigned short int*)&bitstream[1])); @@ -554,35 +289,39 @@ void VoxelTree::processRemoveVoxelBitstream(unsigned char * bitstream, int buffe void VoxelTree::printTreeForDebugging(VoxelNode *startNode) { int colorMask = 0; - + // create the color mask for (int i = 0; i < 8; i++) { if (startNode->children[i] != NULL && startNode->children[i]->isColored()) { colorMask += (1 << (7 - i)); } } - + + printLog("color mask: "); outputBits(colorMask); - + // output the colors we have for (int j = 0; j < 8; j++) { if (startNode->children[j] != NULL && startNode->children[j]->isColored()) { + printLog("color %d : ",j); for (int c = 0; c < 3; c++) { - outputBits(startNode->children[j]->getTrueColor()[c]); + outputBits(startNode->children[j]->getTrueColor()[c],false); } + startNode->children[j]->printDebugDetails(""); } } - + unsigned char childMask = 0; - + for (int k = 0; k < 8; k++) { if (startNode->children[k] != NULL) { childMask += (1 << (7 - k)); } } - + + printLog("child mask: "); outputBits(childMask); - + if (childMask > 0) { // ask children to recursively output their trees // if they aren't a leaf @@ -596,28 +335,23 @@ void VoxelTree::printTreeForDebugging(VoxelNode *startNode) { void VoxelTree::reaverageVoxelColors(VoxelNode *startNode) { bool hasChildren = false; - + for (int i = 0; i < 8; i++) { if (startNode->children[i] != NULL) { reaverageVoxelColors(startNode->children[i]); hasChildren = true; } } - + if (hasChildren) { - bool childrenCollapsed = startNode->collapseIdenticalLeaves(); - - if (!childrenCollapsed) { - startNode->setColorFromAverageOfChildren(); - } - } + bool childrenCollapsed = startNode->collapseIdenticalLeaves(); + if (!childrenCollapsed) { + startNode->setColorFromAverageOfChildren(); + } + } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: VoxelTree::loadVoxelsFile() -// Description: Loads HiFidelity encoded Voxels from a binary file. The current file -// format is a stream of single voxels with color data. -// Complaints: Brad :) + void VoxelTree::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) { int vCount = 0; @@ -672,11 +406,6 @@ void VoxelTree::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) { } } -////////////////////////////////////////////////////////////////////////////////////////// -// Method: VoxelTree::createSphere() -// Description: Creates a sphere of voxels in the local system at a given location/radius -// To Do: Move this function someplace better? -// Complaints: Brad :) void VoxelTree::createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer) { // About the color of the sphere... we're going to make this sphere be a gradient // between two RGB colors. We will do the gradient along the phi spectrum @@ -748,11 +477,321 @@ void VoxelTree::createSphere(float r,float xc, float yc, float zc, float s, bool unsigned char* voxelData = pointToVoxel(x,y,z,s,red,green,blue); this->readCodeColorBufferToTree(voxelData); - //printLog("voxel data for x:%f y:%f z:%f s:%f\n",x,y,z,s); - //printVoxelCode(voxelData); delete voxelData; } } } this->reaverageVoxelColors(this->rootNode); } + +int VoxelTree::searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag) { + + // call the recursive version, this will add all found colored node roots to the bag + int currentSearchLevel = 0; + + int levelReached = searchForColoredNodesRecursion(maxSearchLevel, currentSearchLevel, rootNode, viewFrustum, bag); + return levelReached; +} + + + +int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel, + VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag) { + + // Keep track of how deep we've searched. + currentSearchLevel++; + + // If we've reached our max Search Level, then stop searching. + if (currentSearchLevel >= maxSearchLevel) { + return currentSearchLevel; + } + + // If we're at a node that is out of view, then we can return, because no nodes below us will be in view! + if (!node->isInView(viewFrustum)) { + return currentSearchLevel; + } + + // Ok, this is a little tricky, each child may have been deeper than the others, so we need to track + // how deep each child went. And we actually return the maximum of each child. We use these variables below + // when we recurse the children. + int thisLevel = currentSearchLevel; + int maxChildLevel = thisLevel; + + const int MAX_CHILDREN = 8; + VoxelNode* inViewChildren[MAX_CHILDREN]; + float distancesToChildren[MAX_CHILDREN]; + int positionOfChildren[MAX_CHILDREN]; + int inViewCount = 0; + int inViewNotLeafCount = 0; + int inViewWithColorCount = 0; + + // for each child node, check to see if they exist, are colored, and in view, and if so + // add them to our distance ordered array of children + for (int i = 0; i < MAX_CHILDREN; i++) { + VoxelNode* childNode = node->children[i]; + bool childExists = (childNode != NULL); + bool childIsColored = (childExists && childNode->isColored()); + bool childIsInView = (childExists && childNode->isInView(viewFrustum)); + bool childIsLeaf = (childExists && childNode->isLeaf()); + + if (childIsInView) { + + // track children in view as existing and not a leaf + if (!childIsLeaf) { + inViewNotLeafCount++; + } + + // track children with actual color + if (childIsColored) { + inViewWithColorCount++; + } + + float distance = childNode->distanceToCamera(viewFrustum); + + if (distance < boundaryDistanceForRenderLevel(*childNode->octalCode + 1)) { + inViewCount = insertIntoSortedArrays((void*)childNode, distance, i, + (void**)&inViewChildren, (float*)&distancesToChildren, + (int*)&positionOfChildren, inViewCount, MAX_CHILDREN); + } + } + } + + // If we have children with color, then we do want to add this node (and it's descendants) to the bag to be written + // we don't need to dig deeper. + // + // XXXBHG - this might be a good time to look at colors and add them to a dictionary? But we're not planning + // on scanning the whole tree, so we won't actually see all the colors, so maybe no point in that. + if (inViewWithColorCount) { + bag.insert(node); + } else { + // at this point, we need to iterate the children who are in view, even if not colored + // and we need to determine if there's a deeper tree below them that we care about. We will iterate + // these based on which tree is closer. + for (int i = 0; i < inViewCount; i++) { + VoxelNode* childNode = inViewChildren[i]; + thisLevel = currentSearchLevel; // reset this, since the children will munge it up + int childLevelReached = searchForColoredNodesRecursion(maxSearchLevel, thisLevel, childNode, viewFrustum, bag); + maxChildLevel = std::max(maxChildLevel, childLevelReached); + } + } + return maxChildLevel; +} + +int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, const ViewFrustum& viewFrustum, + unsigned char* outputBuffer, int availableBytes, + VoxelNodeBag& bag) { + + // How many bytes have we written so far at this level; + int bytesWritten = 0; + + // If we're at a node that is out of view, then we can return, because no nodes below us will be in view! + if (!node->isInView(viewFrustum)) { + return bytesWritten; + } + + // write the octal code + int codeLength = bytesRequiredForCodeLength(*node->octalCode); + memcpy(outputBuffer,node->octalCode,codeLength); + + outputBuffer += codeLength; // move the pointer + bytesWritten += codeLength; // keep track of byte count + availableBytes -= codeLength; // keep track or remaining space + + int currentEncodeLevel = 0; + int childBytesWritten = encodeTreeBitstreamRecursion(maxEncodeLevel, currentEncodeLevel, + node, viewFrustum, + outputBuffer, availableBytes, bag); + + // if childBytesWritten == 1 then something went wrong... that's not possible + assert(childBytesWritten != 1); + + // if childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some reason + // couldn't be written... so reset them here... + if (childBytesWritten == 2) { + childBytesWritten = 0; + } + + // if we wrote child bytes, then return our result of all bytes written + if (childBytesWritten) { + bytesWritten += childBytesWritten; + } else { + // otherwise... if we didn't write any child bytes, then pretend like we also didn't write our octal code + bytesWritten = 0; + } + return bytesWritten; +} + +int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel, + VoxelNode* node, const ViewFrustum& viewFrustum, + unsigned char* outputBuffer, int availableBytes, + VoxelNodeBag& bag) const { + // How many bytes have we written so far at this level; + int bytesAtThisLevel = 0; + + // Keep track of how deep we've encoded. + currentEncodeLevel++; + + // If we've reached our max Search Level, then stop searching. + if (currentEncodeLevel >= maxEncodeLevel) { + return bytesAtThisLevel; + } + + float distance = node->distanceToCamera(viewFrustum); + float boundaryDistance = boundaryDistanceForRenderLevel(*node->octalCode + 1); + + // If we're too far away for our render level, then just return + if (distance >= boundaryDistance) { + return bytesAtThisLevel; + } + + // If we're at a node that is out of view, then we can return, because no nodes below us will be in view! + // although technically, we really shouldn't ever be here, because our callers shouldn't be calling us if + // we're out of view + if (!node->isInView(viewFrustum)) { + return bytesAtThisLevel; + } + + bool keepDiggingDeeper = true; // Assuming we're in view we have a great work ethic, we're always ready for more! + + // At any given point in writing the bitstream, the largest minimum we might need to flesh out the current level + // is 1 byte for child colors + 3*8 bytes for the actual colors + 1 byte for child trees. There could be sub trees + // below this point, which might take many more bytes, but that's ok, because we can always mark our subtrees as + // not existing and stop the packet at this point, then start up with a new packet for the remaining sub trees. + const int CHILD_COLOR_MASK_BYTES = 1; + const int MAX_CHILDREN = 8; + const int BYTES_PER_COLOR = 3; + const int CHILD_TREE_EXISTS_BYTES = 1; + const int MAX_LEVEL_BYTES = CHILD_COLOR_MASK_BYTES + MAX_CHILDREN * BYTES_PER_COLOR + CHILD_TREE_EXISTS_BYTES; + + // Make our local buffer large enough to handle writing at this level in case we need to. + unsigned char thisLevelBuffer[MAX_LEVEL_BYTES]; + unsigned char* writeToThisLevelBuffer = &thisLevelBuffer[0]; + + unsigned char childrenExistBits = 0; + unsigned char childrenColoredBits = 0; + int inViewCount = 0; + int inViewNotLeafCount = 0; + int inViewWithColorCount = 0; + + // for each child node, check to see if they exist, are colored, and in view, and if so + // add them to our distance ordered array of children + for (int i = 0; i < MAX_CHILDREN; i++) { + VoxelNode* childNode = node->children[i]; + bool childExists = (childNode != NULL); + bool childIsInView = (childExists && childNode->isInView(viewFrustum)); + if (childIsInView) { + // Before we determine consider this further, let's see if it's in our LOD scope... + float distance = childNode->distanceToCamera(viewFrustum); + float boundaryDistance = boundaryDistanceForRenderLevel(*childNode->octalCode + 1); + + if (distance < boundaryDistance) { + inViewCount++; + + // track children in view as existing and not a leaf, if they're a leaf, + // we don't care about recursing deeper on them, and we don't consider their + // subtree to exist + if (!(childExists && childNode->isLeaf())) { + childrenExistBits += (1 << (7 - i)); + inViewNotLeafCount++; + } + + // track children with actual color + if (childExists && childNode->isColored()) { + childrenColoredBits += (1 << (7 - i)); + inViewWithColorCount++; + } + } + } + } + *writeToThisLevelBuffer = childrenColoredBits; + writeToThisLevelBuffer += sizeof(childrenColoredBits); // move the pointer + bytesAtThisLevel += sizeof(childrenColoredBits); // keep track of byte count + + // write the color data... + for (int i = 0; i < MAX_CHILDREN; i++) { + if (oneAtBit(childrenColoredBits, i)) { + memcpy(writeToThisLevelBuffer, &node->children[i]->getColor(), BYTES_PER_COLOR); + writeToThisLevelBuffer += BYTES_PER_COLOR; // move the pointer for color + bytesAtThisLevel += BYTES_PER_COLOR; // keep track of byte count for color + } + } + // write the child exist bits + *writeToThisLevelBuffer = childrenExistBits; + + writeToThisLevelBuffer += sizeof(childrenExistBits); // move the pointer + bytesAtThisLevel += sizeof(childrenExistBits); // keep track of byte count + + // We only need to keep digging, if there is at least one child that is inView, and not a leaf. + keepDiggingDeeper = (inViewNotLeafCount > 0); + + // If we have enough room to copy our local results into the buffer, then do so... + if (availableBytes >= bytesAtThisLevel) { + memcpy(outputBuffer, &thisLevelBuffer[0], bytesAtThisLevel); + + outputBuffer += bytesAtThisLevel; + availableBytes -= bytesAtThisLevel; + } else { + bag.insert(node); + return 0; + } + + if (keepDiggingDeeper) { + // at this point, we need to iterate the children who are in view, even if not colored + // and we need to determine if there's a deeper tree below them that we care about. + // + // Since this recursive function assumes we're already writing, we know we've already written our + // childrenExistBits. But... we don't really know how big the child tree will be. And we don't know if + // we'll have room in our buffer to actually write all these child trees. What we kinda would like to do is + // write our childExistsBits as a place holder. Then let each potential tree have a go at it. If they + // write something, we keep them in the bits, if they don't, we take them out. + // + // we know the last thing we wrote to the outputBuffer was our childrenExistBits. Let's remember where that was! + unsigned char* childExistsPlaceHolder = outputBuffer-sizeof(childrenExistBits); + + for (int i = 0; i < MAX_CHILDREN; i++) { + + if (oneAtBit(childrenExistBits, i)) { + VoxelNode* childNode = node->children[i]; + + int thisLevel = currentEncodeLevel; + int childTreeBytesOut = encodeTreeBitstreamRecursion(maxEncodeLevel, thisLevel, childNode, + viewFrustum, outputBuffer, availableBytes, bag); + + // if the child wrote 0 bytes, it means that nothing below exists or was in view, or we ran out of space, + // basically, the children below don't contain any info. + + // if the child tree wrote 1 byte??? something must have gone wrong... because it must have at least the color + // byte and the child exist byte. + // + assert(childTreeBytesOut != 1); + + // if the child tree wrote just 2 bytes, then it means: it had no colors and no child nodes, because... + // if it had colors it would write 1 byte for the color mask, + // and at least a color's worth of bytes for the node of colors. + // if it had child trees (with something in them) then it would have the 1 byte for child mask + // and some number of bytes of lower children... + // so, if the child returns 2 bytes out, we can actually consider that an empty tree also!! + // + // we can make this act like no bytes out, by just resetting the bytes out in this case + if (childTreeBytesOut == 2) { + childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees + } + + bytesAtThisLevel += childTreeBytesOut; + availableBytes -= childTreeBytesOut; + outputBuffer += childTreeBytesOut; + + // If we had previously started writing, and if the child DIDN'T write any bytes, + // then we want to remove their bit from the childExistsPlaceHolder bitmask + if (childTreeBytesOut == 0) { + // remove this child's bit... + childrenExistBits -= (1 << (7 - i)); + // repair the child exists mask + *childExistsPlaceHolder = childrenExistBits; + // Note: no need to move the pointer, cause we already stored this + } // end if (childTreeBytesOut == 0) + } // end if (oneAtBit(childrenExistBits, i)) + } // end for + } // end keepDiggingDeeper + return bytesAtThisLevel; +} diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 60c66925ec..ac3c762fcf 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -13,11 +13,7 @@ #include "ViewFrustum.h" #include "VoxelNode.h" -#include "MarkerNode.h" - -const int MAX_VOXEL_PACKET_SIZE = 1492; -const int MAX_TREE_SLICE_BYTES = 26; -const int TREE_SCALE = 10; +#include "VoxelNodeBag.h" // Callback function, for recuseTreeWithOperation typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, bool down, void* extraData); @@ -48,25 +44,30 @@ public: void deleteVoxelCodeFromTree(unsigned char *codeBuffer); void printTreeForDebugging(VoxelNode *startNode); void reaverageVoxelColors(VoxelNode *startNode); - unsigned char * loadBitstreamBuffer(unsigned char *& bitstreamBuffer, - VoxelNode *currentVoxelNode, - MarkerNode *currentMarkerNode, - const glm::vec3& agentPosition, - float thisNodePosition[3], - const ViewFrustum& viewFrustum, - bool viewFrustumCulling, - unsigned char * octalCode = NULL); - void loadVoxelsFile(const char* fileName, bool wantColorRandomizer); void createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer); void recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData=NULL); - + + int encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, const ViewFrustum& viewFrustum, + unsigned char* outputBuffer, int availableBytes, + VoxelNodeBag& bag); + + int searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag); + private: - void recurseNodeWithOperation(VoxelNode* node,RecurseVoxelTreeOperation operation, void* extraData); - VoxelNode * nodeForOctalCode(VoxelNode *ancestorNode, unsigned char * needleCode, VoxelNode** parentOfFoundNode); - VoxelNode * createMissingNode(VoxelNode *lastParentNode, unsigned char *deepestCodeToCreate); - int readNodeData(VoxelNode *destinationNode, unsigned char * nodeData, int bufferSizeBytes); + int encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel, + VoxelNode* node, const ViewFrustum& viewFrustum, + unsigned char* outputBuffer, int availableBytes, + VoxelNodeBag& bag) const; + + int searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel, + VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag); + + void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData); + VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode); + VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate); + int readNodeData(VoxelNode *destinationNode, unsigned char* nodeData, int bufferSizeBytes); }; int boundaryDistanceForRenderLevel(unsigned int renderLevel); diff --git a/voxel-server/src/VoxelAgentData.cpp b/voxel-server/src/VoxelAgentData.cpp index e01ae90a69..7eba95364b 100644 --- a/voxel-server/src/VoxelAgentData.cpp +++ b/voxel-server/src/VoxelAgentData.cpp @@ -6,21 +6,45 @@ // // +#include "PacketHeaders.h" #include "VoxelAgentData.h" #include #include VoxelAgentData::VoxelAgentData() { - rootMarkerNode = new MarkerNode(); + init(); +} + +void VoxelAgentData::init() { + _voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE]; + _voxelPacketAvailableBytes = MAX_VOXEL_PACKET_SIZE; + _voxelPacketAt = _voxelPacket; + _maxSearchLevel = 1; + _maxLevelReachedInLastSearch = 1; + resetVoxelPacket(); +} + +void VoxelAgentData::resetVoxelPacket() { + _voxelPacket[0] = PACKET_HEADER_VOXEL_DATA; + _voxelPacketAt = &_voxelPacket[1]; + _voxelPacketAvailableBytes = MAX_VOXEL_PACKET_SIZE - 1; + _voxelPacketWaiting = false; +} + +void VoxelAgentData::writeToPacket(unsigned char* buffer, int bytes) { + memcpy(_voxelPacketAt, buffer, bytes); + _voxelPacketAvailableBytes -= bytes; + _voxelPacketAt += bytes; + _voxelPacketWaiting = true; } VoxelAgentData::~VoxelAgentData() { - delete rootMarkerNode; + delete[] _voxelPacket; } VoxelAgentData::VoxelAgentData(const VoxelAgentData &otherAgentData) { memcpy(&_position, &otherAgentData._position, sizeof(_position)); - rootMarkerNode = new MarkerNode(); + init(); } VoxelAgentData* VoxelAgentData::clone() const { diff --git a/voxel-server/src/VoxelAgentData.h b/voxel-server/src/VoxelAgentData.h index 4ca7949b92..74a402bb78 100644 --- a/voxel-server/src/VoxelAgentData.h +++ b/voxel-server/src/VoxelAgentData.h @@ -12,17 +12,41 @@ #include #include #include -#include "MarkerNode.h" +#include "VoxelNodeBag.h" +#include "VoxelConstants.h" class VoxelAgentData : public AvatarData { public: - MarkerNode *rootMarkerNode; - VoxelAgentData(); ~VoxelAgentData(); VoxelAgentData(const VoxelAgentData &otherAgentData); VoxelAgentData* clone() const; + + void init(); // sets up data internals + void resetVoxelPacket(); // resets voxel packet to after "V" header + + void writeToPacket(unsigned char* buffer, int bytes); // writes to end of packet + + const unsigned char* getPacket() const { return _voxelPacket; } + int getPacketLength() const { return (MAX_VOXEL_PACKET_SIZE - _voxelPacketAvailableBytes); } + bool isPacketWaiting() const { return _voxelPacketWaiting; } + int getAvailable() const { return _voxelPacketAvailableBytes; } + int getMaxSearchLevel() const { return _maxSearchLevel; }; + void resetMaxSearchLevel() { _maxSearchLevel = 1; }; + void incrementMaxSearchLevel() { _maxSearchLevel++; }; + + int getMaxLevelReached() const { return _maxLevelReachedInLastSearch; }; + void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; } + + VoxelNodeBag nodeBag; +private: + unsigned char* _voxelPacket; + unsigned char* _voxelPacketAt; + int _voxelPacketAvailableBytes; + bool _voxelPacketWaiting; + int _maxSearchLevel; + int _maxLevelReachedInLastSearch; }; #endif /* defined(__hifi__VoxelAgentData__) */ diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index a3c018765a..716a4e9d7b 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -29,9 +29,6 @@ const int VOXEL_LISTEN_PORT = 40106; -const int VERTICES_PER_VOXEL = 8; -const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL; -const int COLOR_VALUES_PER_VOXEL = 3 * VERTICES_PER_VOXEL; const int VOXEL_SIZE_BYTES = 3 + (3 * sizeof(float)); const int VOXELS_PER_PACKET = (MAX_PACKET_SIZE - 1) / VOXEL_SIZE_BYTES; @@ -70,14 +67,54 @@ void addSphere(VoxelTree * tree,bool random, bool wantColorRandomizer) { tree->createSphere(r,xc,yc,zc,s,solid,wantColorRandomizer); } +int _nodeCount=0; +bool countVoxelsOperation(VoxelNode* node, bool down, void* extraData) { + if (down) { + if (node->isColored()){ + _nodeCount++; + } + } + return true; // keep going +} + void addSphereScene(VoxelTree * tree, bool wantColorRandomizer) { printf("adding scene of spheres...\n"); - tree->createSphere(0.25,0.5,0.5,0.5,(1.0/256),true,wantColorRandomizer); - tree->createSphere(0.030625,0.5,0.5,(0.25-0.06125),(1.0/512),true,true); - tree->createSphere(0.030625,(1.0-0.030625),(1.0-0.030625),(1.0-0.06125),(1.0/512),true,true); - tree->createSphere(0.030625,(1.0-0.030625),(1.0-0.030625),0.06125,(1.0/512),true,true); - tree->createSphere(0.030625,(1.0-0.030625),0.06125,(1.0-0.06125),(1.0/512),true,true); - tree->createSphere(0.06125,0.125,0.125,(1.0-0.125),(1.0/512),true,true); + + int sphereBaseSize = 256; + + tree->createSphere(0.25, 0.5, 0.5, 0.5, (1.0 / sphereBaseSize), true, wantColorRandomizer); + printf("one sphere added...\n"); + tree->createSphere(0.030625, 0.5, 0.5, (0.25-0.06125), (1.0 / (sphereBaseSize * 2)), true, true); + + + printf("two spheres added...\n"); + tree->createSphere(0.030625, (1.0 - 0.030625), (1.0 - 0.030625), (1.0 - 0.06125), (1.0 / (sphereBaseSize * 2)), true, true); + printf("three spheres added...\n"); + tree->createSphere(0.030625, (1.0 - 0.030625), (1.0 - 0.030625), 0.06125, (1.0 / (sphereBaseSize * 2)), true, true); + printf("four spheres added...\n"); + tree->createSphere(0.030625, (1.0 - 0.030625), 0.06125, (1.0 - 0.06125), (1.0 / (sphereBaseSize * 2)), true, true); + printf("five spheres added...\n"); + tree->createSphere(0.06125, 0.125, 0.125, (1.0 - 0.125), (1.0 / (sphereBaseSize * 2)), true, true); + + float radius = 0.0125f; + printf("6 spheres added...\n"); + tree->createSphere(radius, 0.25, radius * 5.0f, 0.25, (1.0 / 4096), true, true); + printf("7 spheres added...\n"); + tree->createSphere(radius, 0.125, radius * 5.0f, 0.25, (1.0 / 4096), true, true); + printf("8 spheres added...\n"); + tree->createSphere(radius, 0.075, radius * 5.0f, 0.25, (1.0 / 4096), true, true); + printf("9 spheres added...\n"); + tree->createSphere(radius, 0.05, radius * 5.0f, 0.25, (1.0 / 4096), true, true); + printf("10 spheres added...\n"); + tree->createSphere(radius, 0.025, radius * 5.0f, 0.25, (1.0 / 4096), true, true); + printf("11 spheres added...\n"); + + _nodeCount=0; + tree->recurseTreeWithOperation(countVoxelsOperation); + printf("Nodes after adding scene %d nodes\n", _nodeCount); + + + printf("DONE adding scene of spheres...\n"); } @@ -118,21 +155,76 @@ void randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) { void eraseVoxelTreeAndCleanupAgentVisitData() { - // As our tree to erase all it's voxels - ::randomTree.eraseAllVoxels(); + // As our tree to erase all it's voxels + ::randomTree.eraseAllVoxels(); + // enumerate the agents clean up their marker nodes + for (AgentList::iterator agent = AgentList::getInstance()->begin(); agent != AgentList::getInstance()->end(); agent++) { + VoxelAgentData* agentData = (VoxelAgentData*) agent->getLinkedData(); + if (agentData) { + // clean up the agent visit data + agentData->nodeBag.deleteAll(); + } + } +} - // enumerate the agents clean up their marker nodes - - for (AgentList::iterator agent = AgentList::getInstance()->begin(); agent != AgentList::getInstance()->end(); agent++) { - //printf("eraseVoxelTreeAndCleanupAgentVisitData() agent[%d]\n",i); +void voxelDistributor(AgentList* agentList, AgentList::iterator& agent, VoxelAgentData* agentData, ViewFrustum& viewFrustum) { + // If the bag is empty, fill it... + if (agentData->nodeBag.isEmpty()) { + int maxLevelReached = randomTree.searchForColoredNodes(agentData->getMaxSearchLevel(), randomTree.rootNode, + viewFrustum, agentData->nodeBag); + agentData->setMaxLevelReached(maxLevelReached); - VoxelAgentData *agentData = (VoxelAgentData *)agent->getLinkedData(); + // If nothing got added, then we bump our levels. + if (agentData->nodeBag.isEmpty()) { + if (agentData->getMaxLevelReached() < agentData->getMaxSearchLevel()) { + agentData->resetMaxSearchLevel(); + } else { + agentData->incrementMaxSearchLevel(); + } + } + } - // clean up the agent visit data - delete agentData->rootMarkerNode; - agentData->rootMarkerNode = new MarkerNode(); - } + // If we have something in our nodeBag, then turn them into packets and send them out... + if (!agentData->nodeBag.isEmpty()) { + static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static + int bytesWritten = 0; + int packetsSentThisInterval = 0; + while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL) { + if (!agentData->nodeBag.isEmpty()) { + VoxelNode* subTree = agentData->nodeBag.extract(); + bytesWritten = randomTree.encodeTreeBitstream(agentData->getMaxSearchLevel(), subTree, viewFrustum, + &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, + agentData->nodeBag); + + if (agentData->getAvailable() >= bytesWritten) { + agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten); + } else { + agentList->getAgentSocket().send(agent->getActiveSocket(), + agentData->getPacket(), agentData->getPacketLength()); + packetsSentThisInterval++; + agentData->resetVoxelPacket(); + agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten); + } + } else { + if (agentData->isPacketWaiting()) { + agentList->getAgentSocket().send(agent->getActiveSocket(), + agentData->getPacket(), agentData->getPacketLength()); + agentData->resetVoxelPacket(); + + } + packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left + } + } + // if during this last pass, we emptied our bag, then we want to move to the next level. + if (agentData->nodeBag.isEmpty()) { + if (agentData->getMaxLevelReached() < agentData->getMaxSearchLevel()) { + agentData->resetMaxSearchLevel(); + } else { + agentData->incrementMaxSearchLevel(); + } + } + } } void *distributeVoxelsToListeners(void *args) { @@ -140,77 +232,29 @@ void *distributeVoxelsToListeners(void *args) { AgentList* agentList = AgentList::getInstance(); timeval lastSendTime; - unsigned char *stopOctal; - int packetCount; - - int totalBytesSent; - - unsigned char *voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE]; - unsigned char *voxelPacketEnd; - - float treeRoot[3] = {0, 0, 0}; - while (true) { gettimeofday(&lastSendTime, NULL); // enumerate the agents to send 3 packets to each for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) { - VoxelAgentData *agentData = (VoxelAgentData *)agent->getLinkedData(); - - ViewFrustum viewFrustum; - // get position and orientation details from the camera - viewFrustum.setPosition(agentData->getCameraPosition()); - viewFrustum.setOrientation(agentData->getCameraDirection(), agentData->getCameraUp(), agentData->getCameraRight()); - - // Also make sure it's got the correct lens details from the camera - viewFrustum.setFieldOfView(agentData->getCameraFov()); - viewFrustum.setAspectRatio(agentData->getCameraAspectRatio()); - viewFrustum.setNearClip(agentData->getCameraNearClip()); - viewFrustum.setFarClip(agentData->getCameraFarClip()); - - viewFrustum.calculate(); + VoxelAgentData* agentData = (VoxelAgentData*) agent->getLinkedData(); - // debug for fun!! - if (::debugViewFrustum) { - viewFrustum.dump(); - } + // Sometimes the agent data has not yet been linked, in which case we can't really do anything + if (agentData) { + ViewFrustum viewFrustum; + // get position and orientation details from the camera + viewFrustum.setPosition(agentData->getCameraPosition()); + viewFrustum.setOrientation(agentData->getCameraDirection(), agentData->getCameraUp(), agentData->getCameraRight()); + + // Also make sure it's got the correct lens details from the camera + viewFrustum.setFieldOfView(agentData->getCameraFov()); + viewFrustum.setAspectRatio(agentData->getCameraAspectRatio()); + viewFrustum.setNearClip(agentData->getCameraNearClip()); + viewFrustum.setFarClip(agentData->getCameraFarClip()); - stopOctal = NULL; - packetCount = 0; - totalBytesSent = 0; - randomTree.leavesWrittenToBitstream = 0; - - for (int j = 0; j < PACKETS_PER_CLIENT_PER_INTERVAL; j++) { - voxelPacketEnd = voxelPacket; - stopOctal = randomTree.loadBitstreamBuffer(voxelPacketEnd, - randomTree.rootNode, - agentData->rootMarkerNode, - agentData->getPosition(), - treeRoot, - viewFrustum, - ::viewFrustumCulling, - stopOctal); - - agentList->getAgentSocket().send(agent->getActiveSocket(), voxelPacket, voxelPacketEnd - voxelPacket); - - packetCount++; - totalBytesSent += voxelPacketEnd - voxelPacket; - - // XXXBHG Hack Attack: This is temporary code to help debug an issue. - // Normally we use this break to prevent resending voxels that an agent has - // already visited. But since we might be modifying the voxel tree we might - // want to always send. This is a hack to test the behavior - bool alwaysSend = true; - if (!alwaysSend && agentData->rootMarkerNode->childrenVisitedMask == 255) { - break; - } - } - - // for any agent that has a root marker node with 8 visited children - // recursively delete its marker nodes so we can revisit - if (agentData->rootMarkerNode->childrenVisitedMask == 255) { - delete agentData->rootMarkerNode; - agentData->rootMarkerNode = new MarkerNode(); + viewFrustum.calculate(); + + voxelDistributor(agentList, agent, agentData, viewFrustum); } } @@ -262,7 +306,7 @@ int main(int argc, const char * argv[]) ::viewFrustumCulling = !cmdOptionExists(argc, argv, NO_VIEW_FRUSTUM_CULLING); printf("viewFrustumCulling=%s\n", (::viewFrustumCulling ? "yes" : "no")); - const char* WANT_COLOR_RANDOMIZER = "--WantColorRandomizer"; + const char* WANT_COLOR_RANDOMIZER = "--wantColorRandomizer"; ::wantColorRandomizer = cmdOptionExists(argc, argv, WANT_COLOR_RANDOMIZER); printf("wantColorRandomizer=%s\n", (::wantColorRandomizer ? "yes" : "no"));