diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9d90fee9ed..aacd9f0667 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1392,6 +1392,10 @@ void Application::doFalseColorizeOccludedV2() { _voxels.falseColorizeOccludedV2(); } +void Application::doFalseColorizeBySource() { + _voxels.falseColorizeBySource(); +} + void Application::doTrueVoxelColors() { _voxels.trueColorize(); } @@ -1928,6 +1932,7 @@ void Application::initMenu() { renderDebugMenu->addAction("FALSE Color Voxel Out of View", this, SLOT(doFalseColorizeInView())); renderDebugMenu->addAction("FALSE Color Occluded Voxels", this, SLOT(doFalseColorizeOccluded()), Qt::CTRL | Qt::Key_O); renderDebugMenu->addAction("FALSE Color Occluded V2 Voxels", this, SLOT(doFalseColorizeOccludedV2()), Qt::CTRL | Qt::Key_P); + renderDebugMenu->addAction("FALSE Color By Source", this, SLOT(doFalseColorizeBySource()), Qt::CTRL | Qt::SHIFT | Qt::Key_S); renderDebugMenu->addAction("Show TRUE Colors", this, SLOT(doTrueVoxelColors()), Qt::CTRL | Qt::Key_T); (_shouldLowPassFilter = debugMenu->addAction("Test: LowPass filter"))->setCheckable(true); @@ -3025,19 +3030,35 @@ void Application::displayStats() { drawtext(10, statsVerticalOffset + 15, 0.10f, 0, 1.0, 0, stats); if (_testPing->isChecked()) { - int pingAudio = 0, pingAvatar = 0, pingVoxel = 0; + int pingAudio = 0, pingAvatar = 0, pingVoxel = 0, pingVoxelMax = 0; - NodeList *nodeList = NodeList::getInstance(); - Node *audioMixerNode = nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); - Node *avatarMixerNode = nodeList->soloNodeOfType(NODE_TYPE_AVATAR_MIXER); - Node *voxelServerNode = nodeList->soloNodeOfType(NODE_TYPE_VOXEL_SERVER); + NodeList* nodeList = NodeList::getInstance(); + Node* audioMixerNode = nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); + Node* avatarMixerNode = nodeList->soloNodeOfType(NODE_TYPE_AVATAR_MIXER); pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : 0; pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : 0; - pingVoxel = voxelServerNode ? voxelServerNode->getPingMs() : 0; + + + // Now handle voxel servers, since there could be more than one, we average their ping times + unsigned long totalPingVoxel = 0; + int voxelServerCount = 0; + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + if (node->getType() == NODE_TYPE_VOXEL_SERVER) { + totalPingVoxel += node->getPingMs(); + voxelServerCount++; + if (pingVoxelMax < node->getPingMs()) { + pingVoxelMax = node->getPingMs(); + } + } + } + if (voxelServerCount) { + pingVoxel = totalPingVoxel/voxelServerCount; + } + char pingStats[200]; - sprintf(pingStats, "Ping audio/avatar/voxel: %d / %d / %d ", pingAudio, pingAvatar, pingVoxel); + sprintf(pingStats, "Ping audio/avatar/voxel: %d / %d / %d avg %d max ", pingAudio, pingAvatar, pingVoxel, pingVoxelMax); drawtext(10, statsVerticalOffset + 35, 0.10f, 0, 1.0, 0, pingStats); } @@ -3658,13 +3679,15 @@ void* Application::networkReceive(void* args) { } // fall through to piggyback message if (app->_renderVoxels->isChecked()) { - Node* voxelServer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_VOXEL_SERVER); + Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress); if (voxelServer && socketMatch(voxelServer->getActiveSocket(), &senderAddress)) { voxelServer->lock(); if (messageData[0] == PACKET_TYPE_ENVIRONMENT_DATA) { app->_environment.parseData(&senderAddress, messageData, messageLength); } else { + app->_voxels.setDataSourceID(voxelServer->getNodeID()); app->_voxels.parseData(messageData, messageLength); + app->_voxels.setDataSourceID(UNKNOWN_NODE_ID); } voxelServer->unlock(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index ab15138759..dc2476cc86 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -150,6 +150,7 @@ private slots: void doFalseColorizeByDistance(); void doFalseColorizeOccluded(); void doFalseColorizeOccludedV2(); + void doFalseColorizeBySource(); void doFalseColorizeInView(); void doTrueVoxelColors(); void doTreeStats(); diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 2d1bfb577c..2631e255fb 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "Application.h" #include "CoverageMap.h" @@ -61,6 +63,8 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) : VoxelNode::addDeleteHook(this); _abandonedVBOSlots = 0; + _falseColorizeBySource = false; + _dataSourceID = UNKNOWN_NODE_ID; } void VoxelSystem::nodeDeleted(VoxelNode* node) { @@ -153,13 +157,15 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { case PACKET_TYPE_VOXEL_DATA: { PerformanceWarning warn(_renderWarningsOn, "readBitstreamToTree()"); // ask the VoxelTree to read the bitstream into the tree - _tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, WANT_COLOR, WANT_EXISTS_BITS); + ReadBitstreamToTreeParams args(WANT_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceID()); + _tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args); } break; case PACKET_TYPE_VOXEL_DATA_MONOCHROME: { PerformanceWarning warn(_renderWarningsOn, "readBitstreamToTree()"); // ask the VoxelTree to read the MONOCHROME bitstream into the tree - _tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, NO_COLOR, WANT_EXISTS_BITS); + ReadBitstreamToTreeParams args(NO_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceID()); + _tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args); } break; case PACKET_TYPE_Z_COMMAND: @@ -747,6 +753,7 @@ void VoxelSystem::randomizeVoxelColors() { _nodeCount = 0; _tree->recurseTreeWithOperation(randomColorOperation); qDebug("setting randomized true color for %d nodes\n", _nodeCount); + _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } @@ -761,6 +768,7 @@ void VoxelSystem::falseColorizeRandom() { _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeRandomOperation); qDebug("setting randomized false color for %d nodes\n", _nodeCount); + _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } @@ -775,6 +783,7 @@ void VoxelSystem::trueColorize() { _nodeCount = 0; _tree->recurseTreeWithOperation(trueColorizeOperation); qDebug("setting true color for %d nodes\n", _nodeCount); + _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } @@ -795,6 +804,80 @@ void VoxelSystem::falseColorizeInView(ViewFrustum* viewFrustum) { _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeInViewOperation,(void*)viewFrustum); qDebug("setting in view false color for %d nodes\n", _nodeCount); + _tree->setDirtyBit(); + setupNewVoxelsForDrawing(); +} + +// helper classes and args for falseColorizeBySource +class groupColor { +public: + unsigned char red, green, blue; + groupColor(unsigned char red, unsigned char green, unsigned char blue) : + red(red), green(green), blue(blue) { }; + + groupColor() : + red(0), green(0), blue(0) { }; +}; + +class colorizeBySourceArgs { +public: + std::map colors; +}; + +// Will false colorize voxels that are not in view +bool VoxelSystem::falseColorizeBySourceOperation(VoxelNode* node, void* extraData) { + colorizeBySourceArgs* args = (colorizeBySourceArgs*)extraData; + _nodeCount++; + if (node->isColored()) { + // pick a color based on the source - we want each source to be obviously different + uint16_t nodeID = node->getSourceID(); + + //printf("false colorizing from source %d, color: %d, %d, %d\n", nodeID, + // args->colors[nodeID].red, args->colors[nodeID].green, args->colors[nodeID].blue); + + node->setFalseColor(args->colors[nodeID].red, args->colors[nodeID].green, args->colors[nodeID].blue); + } + return true; // keep going! +} + +void VoxelSystem::falseColorizeBySource() { + _nodeCount = 0; + colorizeBySourceArgs args; + const int NUMBER_OF_COLOR_GROUPS = 3; + const unsigned char MIN_COLOR = 128; + int voxelServerCount = 0; + groupColor groupColors[NUMBER_OF_COLOR_GROUPS] = { groupColor(255, 0, 0), + groupColor(0, 255, 0), + groupColor(0, 0, 255)}; + + // create a bunch of colors we'll use during colorization + NodeList* nodeList = NodeList::getInstance(); + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + if (node->getType() == NODE_TYPE_VOXEL_SERVER) { + uint16_t nodeID = node->getNodeID(); + int groupColor = voxelServerCount % NUMBER_OF_COLOR_GROUPS; + args.colors[nodeID] = groupColors[groupColor]; + + //printf("assigning color for source %d, color: %d, %d, %d\n", nodeID, + // args.colors[nodeID].red, args.colors[nodeID].green, args.colors[nodeID].blue); + + if (groupColors[groupColor].red > 0) { + groupColors[groupColor].red = ((groupColors[groupColor].red - MIN_COLOR)/2) + MIN_COLOR; + } + if (groupColors[groupColor].green > 0) { + groupColors[groupColor].green = ((groupColors[groupColor].green - MIN_COLOR)/2) + MIN_COLOR; + } + if (groupColors[groupColor].blue > 0) { + groupColors[groupColor].blue = ((groupColors[groupColor].blue - MIN_COLOR)/2) + MIN_COLOR; + } + + voxelServerCount++; + } + } + + _tree->recurseTreeWithOperation(falseColorizeBySourceOperation, &args); + qDebug("setting false color by source for %d nodes\n", _nodeCount); + _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } @@ -848,6 +931,7 @@ void VoxelSystem::falseColorizeDistanceFromView(ViewFrustum* viewFrustum) { _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeDistanceFromViewOperation,(void*)viewFrustum); qDebug("setting in distance false color for %d nodes\n", _nodeCount); + _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } @@ -1027,6 +1111,7 @@ void VoxelSystem::falseColorizeRandomEveryOther() { _tree->recurseTreeWithOperation(falseColorizeRandomEveryOtherOperation,&args); qDebug("randomized false color for every other node: total %ld, colorable %ld, colored %ld\n", args.totalNodes, args.colorableNodes, args.coloredNodes); + _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } @@ -1331,6 +1416,7 @@ void VoxelSystem::falseColorizeOccluded() { //myCoverageMap.erase(); + _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } @@ -1447,6 +1533,7 @@ void VoxelSystem::falseColorizeOccludedV2() { //myCoverageMapV2.erase(); + _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index ca390a5a20..ef7c33ee43 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -33,6 +33,9 @@ public: VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = MAX_VOXELS_PER_SYSTEM); ~VoxelSystem(); + void setDataSourceID(int dataSourceID) { _dataSourceID = dataSourceID; }; + int getDataSourceID() const { return _dataSourceID; }; + int parseData(unsigned char* sourceBuffer, int numBytes); virtual void init(); @@ -62,6 +65,8 @@ public: void falseColorizeRandomEveryOther(); void falseColorizeOccluded(); void falseColorizeOccludedV2(); + void falseColorizeBySource(); + void killLocalVoxels(); void setRenderPipelineWarnings(bool on) { _renderWarningsOn = on; }; @@ -94,7 +99,7 @@ public: CoverageMap myCoverageMap; virtual void nodeDeleted(VoxelNode* node); - + protected: float _treeScale; int _maxVoxels; @@ -134,7 +139,7 @@ private: static bool falseColorizeOccludedOperation(VoxelNode* node, void* extraData); static bool falseColorizeSubTreeOperation(VoxelNode* node, void* extraData); static bool falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData); - + static bool falseColorizeBySourceOperation(VoxelNode* node, void* extraData); int updateNodeInArraysAsFullVBO(VoxelNode* node); int updateNodeInArraysAsPartialVBO(VoxelNode* node); @@ -195,6 +200,9 @@ private: void freeBufferIndex(glBufferIndex index); void clearFreeBufferIndexes(); + + bool _falseColorizeBySource; + int _dataSourceID; }; #endif diff --git a/interface/src/avatar/AvatarVoxelSystem.cpp b/interface/src/avatar/AvatarVoxelSystem.cpp index a9f6b31072..b6665fee31 100644 --- a/interface/src/avatar/AvatarVoxelSystem.cpp +++ b/interface/src/avatar/AvatarVoxelSystem.cpp @@ -250,7 +250,8 @@ void AvatarVoxelSystem::handleVoxelDownloadProgress(qint64 bytesReceived, qint64 _voxelReply->deleteLater(); _voxelReply = 0; - _tree->readBitstreamToTree((unsigned char*)entirety.data(), entirety.size(), WANT_COLOR, NO_EXISTS_BITS); + ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS); + _tree->readBitstreamToTree((unsigned char*)entirety.data(), entirety.size(), args); setupNewVoxelsForDrawing(); } diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 8819677bcc..3051f5ce68 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -24,10 +24,9 @@ #include #endif -const char SOLO_NODE_TYPES[3] = { +const char SOLO_NODE_TYPES[2] = { NODE_TYPE_AVATAR_MIXER, - NODE_TYPE_AUDIO_MIXER, - NODE_TYPE_VOXEL_SERVER + NODE_TYPE_AUDIO_MIXER }; const char DEFAULT_DOMAIN_HOSTNAME[MAX_HOSTNAME_BYTES] = "root.highfidelity.io"; diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 2a66fc7374..4c4795bdb0 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -32,7 +32,7 @@ const unsigned int NODE_SOCKET_LISTEN_PORT = 40103; const int NODE_SILENCE_THRESHOLD_USECS = 2 * 1000000; const int DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000; -extern const char SOLO_NODE_TYPES[3]; +extern const char SOLO_NODE_TYPES[2]; const int MAX_HOSTNAME_BYTES = 256; diff --git a/libraries/shared/src/OctalCode.cpp b/libraries/shared/src/OctalCode.cpp index b085a146a2..8acc9a922f 100644 --- a/libraries/shared/src/OctalCode.cpp +++ b/libraries/shared/src/OctalCode.cpp @@ -257,3 +257,42 @@ unsigned char* rebaseOctalCode(unsigned char* originalOctalCode, unsigned char* return newCode; } +bool isAncestorOf(unsigned char* possibleAncestor, unsigned char* possibleDescendent, int descendentsChild) { + if (!possibleAncestor || !possibleDescendent) { + return false; + } + + int ancestorCodeLength = numberOfThreeBitSectionsInCode(possibleAncestor); + if (ancestorCodeLength == 0) { + return true; // this is the root, it's the anscestor of all + } + + int descendentCodeLength = numberOfThreeBitSectionsInCode(possibleDescendent); + + // if the caller also include a child, then our descendent length is actually one extra! + if (descendentsChild != CHECK_NODE_ONLY) { + descendentCodeLength++; + } + + if (ancestorCodeLength > descendentCodeLength) { + return false; // if the descendent is shorter, it can't be a descendent + } + + // compare the sections for the ancestor to the descendent + for (int section = 0; section < ancestorCodeLength; section++) { + char sectionValueAncestor = getOctalCodeSectionValue(possibleAncestor, section); + char sectionValueDescendent; + if (ancestorCodeLength <= descendentCodeLength) { + sectionValueDescendent = getOctalCodeSectionValue(possibleDescendent, section); + } else { + assert(descendentsChild != CHECK_NODE_ONLY); + sectionValueDescendent = descendentsChild; + } + if (sectionValueAncestor != sectionValueDescendent) { + return false; // first non-match, means they don't match + } + } + + // they all match, so we are an ancestor + return true; +} diff --git a/libraries/shared/src/OctalCode.h b/libraries/shared/src/OctalCode.h index 228d5f72b5..477751ea7c 100644 --- a/libraries/shared/src/OctalCode.h +++ b/libraries/shared/src/OctalCode.h @@ -21,7 +21,6 @@ const int BLUE_INDEX = 2; void printOctalCode(unsigned char * octalCode); int bytesRequiredForCodeLength(unsigned char threeBitCodes); -bool isDirectParentOfChild(unsigned char *parentOctalCode, unsigned char * childOctalCode); int branchIndexWithDescendant(unsigned char * ancestorOctalCode, unsigned char * descendantOctalCode); unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber); int numberOfThreeBitSectionsInCode(unsigned char * octalCode); @@ -29,6 +28,9 @@ unsigned char* chopOctalCode(unsigned char* originalOctalCode, int chopLevels); unsigned char* rebaseOctalCode(unsigned char* originalOctalCode, 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); + // 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); diff --git a/libraries/voxels/src/JurisdictionMap.cpp b/libraries/voxels/src/JurisdictionMap.cpp new file mode 100644 index 0000000000..b67963d8d6 --- /dev/null +++ b/libraries/voxels/src/JurisdictionMap.cpp @@ -0,0 +1,178 @@ +// +// JurisdictionMap.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 8/1/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include +#include +#include + +#include "JurisdictionMap.h" +#include "VoxelNode.h" + +JurisdictionMap::~JurisdictionMap() { + clear(); +} + +void JurisdictionMap::clear() { + if (_rootOctalCode) { + delete[] _rootOctalCode; + _rootOctalCode = NULL; + } + + for (int i = 0; i < _endNodes.size(); i++) { + if (_endNodes[i]) { + delete[] _endNodes[i]; + } + } + _endNodes.clear(); +} + +JurisdictionMap::JurisdictionMap() : _rootOctalCode(NULL) { + unsigned char* rootCode = new unsigned char[1]; + *rootCode = 0; + + std::vector emptyEndNodes; + init(rootCode, emptyEndNodes); +} + +JurisdictionMap::JurisdictionMap(const char* filename) : _rootOctalCode(NULL) { + clear(); // clean up our own memory + readFromFile(filename); +} + + +JurisdictionMap::JurisdictionMap(unsigned char* rootOctalCode, const std::vector& endNodes) + : _rootOctalCode(NULL) { + init(rootOctalCode, endNodes); +} + +void JurisdictionMap::init(unsigned char* rootOctalCode, const std::vector& endNodes) { + clear(); // clean up our own memory + _rootOctalCode = rootOctalCode; + _endNodes = endNodes; +} + +JurisdictionMap::Area JurisdictionMap::isMyJurisdiction(unsigned char* nodeOctalCode, int childIndex) const { + // to be in our jurisdiction, we must be under the root... + + // if the node is an ancestor of my root, then we return ABOVE + if (isAncestorOf(nodeOctalCode, _rootOctalCode)) { + return ABOVE; + } + + // otherwise... + bool isInJurisdiction = isAncestorOf(_rootOctalCode, nodeOctalCode, childIndex); + + //printf("isInJurisdiction=%s rootOctalCode=",debug::valueOf(isInJurisdiction)); + //printOctalCode(_rootOctalCode); + //printf("nodeOctalCode="); + //printOctalCode(nodeOctalCode); + + // if we're under the root, then we can't be under any of the endpoints + if (isInJurisdiction) { + for (int i = 0; i < _endNodes.size(); i++) { + bool isUnderEndNode = isAncestorOf(_endNodes[i], nodeOctalCode); + if (isUnderEndNode) { + isInJurisdiction = false; + break; + } + } + } + + return isInJurisdiction ? WITHIN : BELOW; +} + + +bool JurisdictionMap::readFromFile(const char* filename) { + QString settingsFile(filename); + QSettings settings(settingsFile, QSettings::IniFormat); + QString rootCode = settings.value("root","00").toString(); + qDebug() << "rootCode=" << rootCode << "\n"; + + _rootOctalCode = hexStringToOctalCode(rootCode); + printOctalCode(_rootOctalCode); + + settings.beginGroup("endNodes"); + const QStringList childKeys = settings.childKeys(); + QHash values; + foreach (const QString &childKey, childKeys) { + QString childValue = settings.value(childKey).toString(); + values.insert(childKey, childValue); + qDebug() << childKey << "=" << childValue << "\n"; + + unsigned char* octcode = hexStringToOctalCode(childValue); + printOctalCode(octcode); + + _endNodes.push_back(octcode); + } + settings.endGroup(); + return true; +} + +bool JurisdictionMap::writeToFile(const char* filename) { + QString settingsFile(filename); + QSettings settings(settingsFile, QSettings::IniFormat); + + + QString rootNodeValue = octalCodeToHexString(_rootOctalCode); + + settings.setValue("root", rootNodeValue); + + settings.beginGroup("endNodes"); + for (int i = 0; i < _endNodes.size(); i++) { + QString key = QString("endnode%1").arg(i); + QString value = octalCodeToHexString(_endNodes[i]); + settings.setValue(key, value); + } + settings.endGroup(); + return true; +} + + +unsigned char* JurisdictionMap::hexStringToOctalCode(const QString& input) const { + const int HEX_NUMBER_BASE = 16; + const int HEX_BYTE_SIZE = 2; + int stringIndex = 0; + int byteArrayIndex = 0; + + // allocate byte array based on half of string length + unsigned char* bytes = new unsigned char[(input.length()) / HEX_BYTE_SIZE]; + + // loop through the string - 2 bytes at a time converting + // it to decimal equivalent and store in byte array + bool ok; + while (stringIndex < input.length()) { + uint value = input.mid(stringIndex, HEX_BYTE_SIZE).toUInt(&ok, HEX_NUMBER_BASE); + if (!ok) { + break; + } + bytes[byteArrayIndex] = (unsigned char)value; + stringIndex += HEX_BYTE_SIZE; + byteArrayIndex++; + } + + // something went wrong + if (!ok) { + delete[] bytes; + return NULL; + } + return bytes; +} + +QString JurisdictionMap::octalCodeToHexString(unsigned char* octalCode) const { + const int HEX_NUMBER_BASE = 16; + const int HEX_BYTE_SIZE = 2; + QString output; + if (!octalCode) { + output = "00"; + } else { + for (int i = 0; i < bytesRequiredForCodeLength(*octalCode); i++) { + output.append(QString("%1").arg(octalCode[i], HEX_BYTE_SIZE, HEX_NUMBER_BASE, QChar('0')).toUpper()); + } + } + return output; +} diff --git a/libraries/voxels/src/JurisdictionMap.h b/libraries/voxels/src/JurisdictionMap.h new file mode 100644 index 0000000000..a52765549f --- /dev/null +++ b/libraries/voxels/src/JurisdictionMap.h @@ -0,0 +1,46 @@ +// +// JurisdictionMap.h +// hifi +// +// Created by Brad Hefta-Gaub on 8/1/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__JurisdictionMap__ +#define __hifi__JurisdictionMap__ + +#include +#include + +class JurisdictionMap { +public: + enum Area { + ABOVE, + WITHIN, + BELOW + }; + + JurisdictionMap(); + JurisdictionMap(const char* filename); + JurisdictionMap(unsigned char* rootOctalCode, const std::vector& endNodes); + ~JurisdictionMap(); + + Area isMyJurisdiction(unsigned char* nodeOctalCode, int childIndex) const; + + bool writeToFile(const char* filename); + bool readFromFile(const char* filename); + +private: + void clear(); + void init(unsigned char* rootOctalCode, const std::vector& endNodes); + + unsigned char* hexStringToOctalCode(const QString& input) const; + QString octalCodeToHexString(unsigned char* octalCode) const; + + unsigned char* _rootOctalCode; + std::vector _endNodes; +}; + +#endif /* defined(__hifi__JurisdictionMap__) */ + + diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index 8a76d9dfe8..42b34dd542 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -12,6 +12,8 @@ #include +#include + #include "AABox.h" #include "OctalCode.h" #include "SharedUtil.h" @@ -51,6 +53,7 @@ void VoxelNode::init(unsigned char * octalCode) { _voxelSystem = NULL; _isDirty = true; _shouldRender = false; + _sourceID = UNKNOWN_NODE_ID; markWithChangedTime(); calculateAABox(); } diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index 38e9627848..5035dbaad3 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -93,8 +93,6 @@ public: void setColor(const nodeColor& color); const nodeColor& getTrueColor() const { return _trueColor; }; const nodeColor& getColor() const { return _currentColor; }; - void setDensity(float density) { _density = density; }; - float getDensity() const { return _density; }; #else void setFalseColor(colorPart red, colorPart green, colorPart blue) { /* no op */ }; void setFalseColored(bool isFalseColored) { /* no op */ }; @@ -105,6 +103,11 @@ public: const nodeColor& getColor() const { return _trueColor; }; #endif + void setDensity(float density) { _density = density; }; + float getDensity() const { return _density; }; + void setSourceID(uint16_t sourceID) { _sourceID = sourceID; }; + uint16_t getSourceID() const { return _sourceID; }; + static void addDeleteHook(VoxelNodeDeleteHook* hook); static void removeDeleteHook(VoxelNodeDeleteHook* hook); @@ -135,6 +138,7 @@ private: unsigned long _subtreeNodeCount; unsigned long _subtreeLeafNodeCount; float _density; // If leaf: density = 1, if internal node: 0-1 density of voxels inside + uint16_t _sourceID; static std::vector _hooks; }; diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 87def2cc23..a327b561bb 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -231,7 +231,7 @@ VoxelNode* VoxelTree::createMissingNode(VoxelNode* lastParentNode, unsigned char } int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData, int bytesLeftToRead, - bool includeColor, bool includeExistsBits) { + ReadBitstreamToTreeParams& args) { // give this destination node the child mask from the packet const unsigned char ALL_CHILDREN_ASSUMED_TO_EXIST = 0xFF; unsigned char colorInPacketMask = *nodeData; @@ -254,12 +254,13 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData, // pull the color for this child nodeColor newColor = { 128, 128, 128, 1}; - if (includeColor) { + if (args.includeColor) { memcpy(newColor, nodeData + bytesRead, 3); bytesRead += 3; } bool nodeWasDirty = destinationNode->getChildAtIndex(i)->isDirty(); destinationNode->getChildAtIndex(i)->setColor(newColor); + destinationNode->getChildAtIndex(i)->setSourceID(args.sourceID); bool nodeIsDirty = destinationNode->getChildAtIndex(i)->isDirty(); if (nodeIsDirty) { _isDirty = true; @@ -273,11 +274,11 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData, } // give this destination node the child mask from the packet - unsigned char childrenInTreeMask = includeExistsBits ? *(nodeData + bytesRead) : ALL_CHILDREN_ASSUMED_TO_EXIST; - unsigned char childMask = *(nodeData + bytesRead + (includeExistsBits ? sizeof(childrenInTreeMask) : 0)); + unsigned char childrenInTreeMask = args.includeExistsBits ? *(nodeData + bytesRead) : ALL_CHILDREN_ASSUMED_TO_EXIST; + unsigned char childMask = *(nodeData + bytesRead + (args.includeExistsBits ? sizeof(childrenInTreeMask) : 0)); int childIndex = 0; - bytesRead += includeExistsBits ? sizeof(childrenInTreeMask) + sizeof(childMask) : sizeof(childMask); + bytesRead += args.includeExistsBits ? sizeof(childrenInTreeMask) + sizeof(childMask) : sizeof(childMask); while (bytesLeftToRead - bytesRead > 0 && childIndex < NUMBER_OF_CHILDREN) { // check the exists mask to see if we have a child to traverse into @@ -300,13 +301,12 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData, // tell the child to read the subsequent data bytesRead += readNodeData(destinationNode->getChildAtIndex(childIndex), - nodeData + bytesRead, bytesLeftToRead - bytesRead, includeColor, includeExistsBits); + nodeData + bytesRead, bytesLeftToRead - bytesRead, args); } childIndex++; } - - if (includeExistsBits) { + if (args.includeExistsBits) { for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { // now also check the childrenInTreeMask, if the mask is missing the bit, then it means we need to delete this child // subtree/node, because it shouldn't actually exist in the tree. @@ -319,14 +319,14 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData, return bytesRead; } -void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int bufferSizeBytes, - bool includeColor, bool includeExistsBits, VoxelNode* destinationNode) { +void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int bufferSizeBytes, + ReadBitstreamToTreeParams& args) { int bytesRead = 0; unsigned char* bitstreamAt = bitstream; // If destination node is not included, set it to root - if (!destinationNode) { - destinationNode = rootNode; + if (!args.destinationNode) { + args.destinationNode = rootNode; } _nodesChangedFromBitstream = 0; @@ -336,14 +336,14 @@ void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int // if there are more bytes after that, it's assumed to be another root relative tree while (bitstreamAt < bitstream + bufferSizeBytes) { - VoxelNode* bitstreamRootNode = nodeForOctalCode(destinationNode, (unsigned char *)bitstreamAt, NULL); + VoxelNode* bitstreamRootNode = nodeForOctalCode(args.destinationNode, (unsigned char *)bitstreamAt, NULL); if (*bitstreamAt != *bitstreamRootNode->getOctalCode()) { // 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(destinationNode, (unsigned char*) bitstreamAt); + bitstreamRootNode = createMissingNode(args.destinationNode, (unsigned char*) bitstreamAt); if (bitstreamRootNode->isDirty()) { _isDirty = true; _nodesChangedFromBitstream++; @@ -353,8 +353,8 @@ void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt); int theseBytesRead = 0; theseBytesRead += octalCodeBytes; - theseBytesRead += readNodeData(bitstreamRootNode, bitstreamAt + octalCodeBytes, - bufferSizeBytes - (bytesRead + octalCodeBytes), includeColor, includeExistsBits); + theseBytesRead += readNodeData(bitstreamRootNode, bitstreamAt + octalCodeBytes, + bufferSizeBytes - (bytesRead + octalCodeBytes), args); // skip bitstream to new startPoint bitstreamAt += theseBytesRead; @@ -1078,6 +1078,15 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp if (currentEncodeLevel >= params.maxEncodeLevel) { return bytesAtThisLevel; } + + // If we've been provided a jurisdiction map, then we need to honor it. + if (params.jurisdictionMap) { + // here's how it works... if we're currently above our root jurisdiction, then we proceed normally. + // but once we're in our own jurisdiction, then we need to make sure we're not below it. + if (JurisdictionMap::BELOW == params.jurisdictionMap->isMyJurisdiction(node->getOctalCode(), CHECK_NODE_ONLY)) { + return bytesAtThisLevel; + } + } // caller can pass NULL as viewFrustum if they want everything if (params.viewFrustum) { @@ -1197,9 +1206,18 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { VoxelNode* childNode = node->getChildAtIndex(i); - // if the caller wants to include childExistsBits, then include them even if not in view - if (params.includeExistsBits && childNode) { - childrenExistInTreeBits += (1 << (7 - i)); + // if the caller wants to include childExistsBits, then include them even if not in view, if however, + // we're in a portion of the tree that's not our responsibility, then we assume the child nodes exist + // even if they don't in our local tree + bool notMyJurisdiction = false; + if (params.jurisdictionMap) { + notMyJurisdiction = (JurisdictionMap::BELOW == params.jurisdictionMap->isMyJurisdiction(node->getOctalCode(), i)); + } + if (params.includeExistsBits) { + // If the child is known to exist, OR, it's not my jurisdiction, then we mark the bit as existing + if (childNode || notMyJurisdiction) { + childrenExistInTreeBits += (1 << (7 - i)); + } } if (params.wantOcclusionCulling) { @@ -1548,7 +1566,8 @@ bool VoxelTree::readFromSVOFile(const char* fileName) { // read the entire file into a buffer, WHAT!? Why not. unsigned char* entireFile = new unsigned char[fileLength]; file.read((char*)entireFile, fileLength); - readBitstreamToTree(entireFile, fileLength, WANT_COLOR, NO_EXISTS_BITS); + ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS); + readBitstreamToTree(entireFile, fileLength, args); delete[] entireFile; file.close(); @@ -1702,7 +1721,8 @@ void VoxelTree::copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinat bytesWritten = encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params); // ask destination tree to read the bitstream - destinationTree->readBitstreamToTree(&outputBuffer[0], bytesWritten, WANT_COLOR, NO_EXISTS_BITS); + ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS); + destinationTree->readBitstreamToTree(&outputBuffer[0], bytesWritten, args); } } @@ -1722,7 +1742,8 @@ void VoxelTree::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destin bytesWritten = sourceTree->encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params); // ask destination tree to read the bitstream - readBitstreamToTree(&outputBuffer[0], bytesWritten, WANT_COLOR, NO_EXISTS_BITS, destinationNode); + ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, destinationNode); + readBitstreamToTree(&outputBuffer[0], bytesWritten, args); } } @@ -1877,4 +1898,4 @@ void VoxelTree::computeBlockColor(int id, int data, int& red, int& green, int& b create = 0; break; } -} +} \ No newline at end of file diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 88394bd825..f27543caa8 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -13,6 +13,7 @@ #include #include "CoverageMap.h" +#include "JurisdictionMap.h" #include "ViewFrustum.h" #include "VoxelNode.h" #include "VoxelNodeBag.h" @@ -36,9 +37,10 @@ const int NO_BOUNDARY_ADJUST = 0; const int LOW_RES_MOVING_ADJUST = 1; const uint64_t IGNORE_LAST_SENT = 0; -#define IGNORE_SCENE_STATS NULL -#define IGNORE_VIEW_FRUSTUM NULL -#define IGNORE_COVERAGE_MAP NULL +#define IGNORE_SCENE_STATS NULL +#define IGNORE_VIEW_FRUSTUM NULL +#define IGNORE_COVERAGE_MAP NULL +#define IGNORE_JURISDICTION_MAP NULL class EncodeBitstreamParams { public: @@ -56,6 +58,7 @@ public: bool forceSendScene; VoxelSceneStats* stats; CoverageMap* map; + JurisdictionMap* jurisdictionMap; EncodeBitstreamParams( int maxEncodeLevel = INT_MAX, @@ -70,7 +73,8 @@ public: int boundaryLevelAdjust = NO_BOUNDARY_ADJUST, uint64_t lastViewFrustumSent = IGNORE_LAST_SENT, bool forceSendScene = true, - VoxelSceneStats* stats = IGNORE_SCENE_STATS) : + VoxelSceneStats* stats = IGNORE_SCENE_STATS, + JurisdictionMap* jurisdictionMap = IGNORE_JURISDICTION_MAP) : maxEncodeLevel (maxEncodeLevel), maxLevelReached (0), viewFrustum (viewFrustum), @@ -84,7 +88,27 @@ public: lastViewFrustumSent (lastViewFrustumSent), forceSendScene (forceSendScene), stats (stats), - map (map) + map (map), + jurisdictionMap (jurisdictionMap) + {} +}; + +class ReadBitstreamToTreeParams { +public: + bool includeColor; + bool includeExistsBits; + VoxelNode* destinationNode; + uint16_t sourceID; + + ReadBitstreamToTreeParams( + bool includeColor = WANT_COLOR, + bool includeExistsBits = WANT_EXISTS_BITS, + VoxelNode* destinationNode = NULL, + uint16_t sourceID = UNKNOWN_NODE_ID) : + includeColor (includeColor), + includeExistsBits (includeExistsBits), + destinationNode (destinationNode), + sourceID (sourceID) {} }; @@ -109,9 +133,7 @@ public: void eraseAllVoxels(); void processRemoveVoxelBitstream(unsigned char* bitstream, int bufferSizeBytes); - void readBitstreamToTree(unsigned char* bitstream, unsigned long int bufferSizeBytes, - bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS, - VoxelNode* destinationNode = NULL); + void readBitstreamToTree(unsigned char* bitstream, unsigned long int bufferSizeBytes, ReadBitstreamToTreeParams& args); void readCodeColorBufferToTree(unsigned char* codeColorBuffer, bool destructive = false); void deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool collapseEmptyTrees = DONT_COLLAPSE); void printTreeForDebugging(VoxelNode* startNode); @@ -169,7 +191,8 @@ public: void recurseTreeWithOperationDistanceSortedTimed(PointerStack* stackOfNodes, long allowedTime, RecurseVoxelTreeOperation operation, const glm::vec3& point, void* extraData); - + + private: void deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData); void readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraData); @@ -181,8 +204,7 @@ private: VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const; VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate); - int readNodeData(VoxelNode *destinationNode, unsigned char* nodeData, int bufferSizeBytes, - bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS); + int readNodeData(VoxelNode *destinationNode, unsigned char* nodeData, int bufferSizeBytes, ReadBitstreamToTreeParams& args); bool _isDirty; unsigned long int _nodesChangedFromBitstream; diff --git a/voxel-server/src/README b/voxel-server/src/README new file mode 100644 index 0000000000..81824d96a9 --- /dev/null +++ b/voxel-server/src/README @@ -0,0 +1,80 @@ +NAME + voxel-server - the High Fidelity Voxel Server + +SYNOPSIS + voxel-server [--local] [--jurisdictionFile ] [--port ] [--voxelsPersistFilename ] + [--displayVoxelStats] [--debugVoxelSending] [--debugVoxelReceiving] [--shouldShowAnimationDebug] + [--wantColorRandomizer] [--NoVoxelPersist] [--packetsPerSecond ] + [--AddRandomVoxels] [--AddScene] [--NoAddScene] + +DESCRIPTION + voxel-server is a compact, portable, scalable, distributed sparse voxel octree server + +OPTIONS + + --local + This will run the voxel server in "local domain mode" and will look for a domain-server running on the same IP + address as the voxel server + + --jurisdictionFile [filename] + Tells the server to load it's jurisdiction from the specified file. When a voxel server is running with a limited + "jurisdiction" it will only server voxels from that portion of the voxel tree. The jurisdiction file is a ".ini" style + file with the following options: [General/root] specifies the octal code for the node in the tree that this server will + use as it's "root". It will only server voxels under this root. [endNodes/...] a list or group of additional octalcodes + under the root, which will not be served. + + The following example jurisdiction file will server all voxels from the root and below, and exclude voxels from the + voxel of scale 0.25 and 0,0,0. + + ****** example jurisdiction.ini ********************************************************************** + [General] + root=00 + + [endNodes] + endnode0=0200 + + ****************************************************************************************************** + + + --port [port] + Specify the port the voxel-server will listen on. You must specify different ports to enable multiple voxel servers + running on a single machine. + + --voxelsPersistFilename [filename] + Specify and alternate file that the voxel server will read and write persistant voxels to. By default the voxel server + will use one of the following files: + + default: /etc/highfidelity/voxel-server/resources/voxels.svo + in local mode: ./resources/voxels.svo + + --displayVoxelStats + Displays additional voxel stats debugging + + --debugVoxelSending + Displays additional voxel sending debugging + + --debugVoxelReceiving + Displays additional voxel receiving debugging + + --shouldShowAnimationDebug + Displays additional verbose animation debugging + + --wantColorRandomizer + Adds color randomization to inserts into the local voxel tree + + --NoVoxelPersist + Disables voxel persisting + + --packetsPerSecond [value] + Specifies the packets per second that this voxel server will send to attached clients + + --AddRandomVoxels + Add random voxels to the surface on startup + + --AddScene + OBSOLETE: Adds an arbitrary scene with several "planet" spheres + + --NoAddScene + OBSOLETE: disables adding of scene + + \ No newline at end of file diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 216c576b2f..329e8d803c 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -32,6 +32,8 @@ const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo"; const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo"; +const int MAX_FILENAME_LENGTH = 1024; +char voxelPersistFilename[MAX_FILENAME_LENGTH]; const int VOXEL_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds const int VOXEL_LISTEN_PORT = 40106; @@ -65,6 +67,8 @@ bool debugVoxelReceiving = false; EnvironmentData environmentData[3]; +int receivedPacketCount = 0; +JurisdictionMap* jurisdiction = NULL; void randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) { // randomly generate children for this node @@ -292,7 +296,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, wantOcclusionCulling, coverageMap, boundaryLevelAdjust, nodeData->getLastTimeBagEmpty(), - isFullScene, &nodeData->stats); + isFullScene, &nodeData->stats, ::jurisdiction); nodeData->stats.encodeStarted(); bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, @@ -376,7 +380,7 @@ void persistVoxelsWhenDirty() { "persistVoxelsWhenDirty() - writeToSVOFile()", ::shouldShowAnimationDebug); printf("saving voxels to file...\n"); - serverTree.writeToSVOFile(::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE); + serverTree.writeToSVOFile(::voxelPersistFilename); serverTree.clearDirtyBit(); // tree is clean after saving printf("DONE saving voxels to file...\n"); } @@ -427,14 +431,39 @@ void attachVoxelNodeDataToNode(Node* newNode) { } } -int receivedPacketCount = 0; - int main(int argc, const char * argv[]) { pthread_mutex_init(&::treeLock, NULL); qInstallMessageHandler(sharedMessageHandler); - NodeList* nodeList = NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, VOXEL_LISTEN_PORT); + int listenPort = VOXEL_LISTEN_PORT; + // Check to see if the user passed in a command line option for setting listen port + const char* PORT_PARAMETER = "--port"; + const char* portParameter = getCmdOption(argc, argv, PORT_PARAMETER); + if (portParameter) { + listenPort = atoi(portParameter); + if (listenPort < 1) { + listenPort = VOXEL_LISTEN_PORT; + } + printf("portParameter=%s listenPort=%d\n", portParameter, listenPort); + } + + const char* JURISDICTION_FILE = "--jurisdictionFile"; + const char* jurisdictionFile = getCmdOption(argc, argv, JURISDICTION_FILE); + if (jurisdictionFile) { + printf("jurisdictionFile=%s\n", jurisdictionFile); + + printf("about to readFromFile().... jurisdictionFile=%s\n", jurisdictionFile); + jurisdiction = new JurisdictionMap(jurisdictionFile); + printf("after readFromFile().... jurisdictionFile=%s\n", jurisdictionFile); + + // test writing the file... + printf("about to writeToFile().... jurisdictionFile=%s\n", jurisdictionFile); + jurisdiction->writeToFile(jurisdictionFile); + printf("after writeToFile().... jurisdictionFile=%s\n", jurisdictionFile); + } + + NodeList* nodeList = NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, listenPort); setvbuf(stdout, NULL, _IOLBF, 0); // Handle Local Domain testing with the --local command line @@ -480,8 +509,19 @@ int main(int argc, const char * argv[]) { // if we want Voxel Persistance, load the local file now... bool persistantFileRead = false; if (::wantVoxelPersist) { - printf("loading voxels from file...\n"); - persistantFileRead = ::serverTree.readFromSVOFile(::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE); + + // Check to see if the user passed in a command line option for setting packet send rate + const char* VOXELS_PERSIST_FILENAME = "--voxelsPersistFilename"; + const char* voxelsPersistFilenameParameter = getCmdOption(argc, argv, VOXELS_PERSIST_FILENAME); + if (voxelsPersistFilenameParameter) { + strcpy(voxelPersistFilename, voxelsPersistFilenameParameter); + } else { + strcpy(voxelPersistFilename, ::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE); + } + + printf("loading voxels from file: %s...\n", voxelPersistFilename); + + persistantFileRead = ::serverTree.readFromSVOFile(::voxelPersistFilename); if (persistantFileRead) { PerformanceWarning warn(::shouldShowAnimationDebug, "persistVoxelsWhenDirty() - reaverageVoxelColors()", ::shouldShowAnimationDebug); @@ -707,6 +747,10 @@ int main(int argc, const char * argv[]) { pthread_join(sendVoxelThread, NULL); pthread_mutex_destroy(&::treeLock); + + if (jurisdiction) { + delete jurisdiction; + } return 0; }