mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-22 12:17:42 +02:00
Merge pull request #754 from ZappoMan/multiple_voxel_servers
Multiple voxel servers
This commit is contained in:
commit
f24cf192a4
17 changed files with 619 additions and 61 deletions
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -150,6 +150,7 @@ private slots:
|
|||
void doFalseColorizeByDistance();
|
||||
void doFalseColorizeOccluded();
|
||||
void doFalseColorizeOccludedV2();
|
||||
void doFalseColorizeBySource();
|
||||
void doFalseColorizeInView();
|
||||
void doTrueVoxelColors();
|
||||
void doTreeStats();
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include <PacketHeaders.h>
|
||||
#include <PerfStat.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <NodeList.h>
|
||||
#include <NodeTypes.h>
|
||||
|
||||
#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<uint16_t, groupColor> 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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,10 +24,9 @@
|
|||
#include <arpa/inet.h>
|
||||
#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";
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
178
libraries/voxels/src/JurisdictionMap.cpp
Normal file
178
libraries/voxels/src/JurisdictionMap.cpp
Normal file
|
@ -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 <QSettings>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#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<unsigned char*> 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<unsigned char*>& endNodes)
|
||||
: _rootOctalCode(NULL) {
|
||||
init(rootOctalCode, endNodes);
|
||||
}
|
||||
|
||||
void JurisdictionMap::init(unsigned char* rootOctalCode, const std::vector<unsigned char*>& 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<QString, QString> 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;
|
||||
}
|
46
libraries/voxels/src/JurisdictionMap.h
Normal file
46
libraries/voxels/src/JurisdictionMap.h
Normal file
|
@ -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 <vector>
|
||||
#include <QString>
|
||||
|
||||
class JurisdictionMap {
|
||||
public:
|
||||
enum Area {
|
||||
ABOVE,
|
||||
WITHIN,
|
||||
BELOW
|
||||
};
|
||||
|
||||
JurisdictionMap();
|
||||
JurisdictionMap(const char* filename);
|
||||
JurisdictionMap(unsigned char* rootOctalCode, const std::vector<unsigned char*>& 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<unsigned char*>& endNodes);
|
||||
|
||||
unsigned char* hexStringToOctalCode(const QString& input) const;
|
||||
QString octalCodeToHexString(unsigned char* octalCode) const;
|
||||
|
||||
unsigned char* _rootOctalCode;
|
||||
std::vector<unsigned char*> _endNodes;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__JurisdictionMap__) */
|
||||
|
||||
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include <QDebug>
|
||||
|
||||
#include <NodeList.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
|
|
@ -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<VoxelNodeDeleteHook*> _hooks;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
#include <SimpleMovingAverage.h>
|
||||
|
||||
#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;
|
||||
|
|
80
voxel-server/src/README
Normal file
80
voxel-server/src/README
Normal file
|
@ -0,0 +1,80 @@
|
|||
NAME
|
||||
voxel-server - the High Fidelity Voxel Server
|
||||
|
||||
SYNOPSIS
|
||||
voxel-server [--local] [--jurisdictionFile <filename>] [--port <port>] [--voxelsPersistFilename <filename>]
|
||||
[--displayVoxelStats] [--debugVoxelSending] [--debugVoxelReceiving] [--shouldShowAnimationDebug]
|
||||
[--wantColorRandomizer] [--NoVoxelPersist] [--packetsPerSecond <value>]
|
||||
[--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
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue