Merge pull request #754 from ZappoMan/multiple_voxel_servers

Multiple voxel servers
This commit is contained in:
Philip Rosedale 2013-08-02 15:22:19 -07:00
commit f24cf192a4
17 changed files with 619 additions and 61 deletions

View file

@ -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();
}

View file

@ -150,6 +150,7 @@ private slots:
void doFalseColorizeByDistance();
void doFalseColorizeOccluded();
void doFalseColorizeOccludedV2();
void doFalseColorizeBySource();
void doFalseColorizeInView();
void doTrueVoxelColors();
void doTreeStats();

View file

@ -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();
}

View file

@ -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; };
@ -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

View file

@ -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();
}

View file

@ -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";

View file

@ -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;

View file

@ -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;
}

View file

@ -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);

View 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;
}

View 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__) */

View file

@ -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();
}

View file

@ -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;
};

View file

@ -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.
@ -320,13 +320,13 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData,
}
void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int bufferSizeBytes,
bool includeColor, bool includeExistsBits, VoxelNode* destinationNode) {
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++;
@ -354,7 +354,7 @@ void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int
int theseBytesRead = 0;
theseBytesRead += octalCodeBytes;
theseBytesRead += readNodeData(bitstreamRootNode, bitstreamAt + octalCodeBytes,
bufferSizeBytes - (bytesRead + octalCodeBytes), includeColor, includeExistsBits);
bufferSizeBytes - (bytesRead + octalCodeBytes), args);
// skip bitstream to new startPoint
bitstreamAt += theseBytesRead;
@ -1079,6 +1079,15 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
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) {
float distance = node->distanceToCamera(*params.viewFrustum);
@ -1197,10 +1206,19 @@ 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) {
// 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) {
if (childNode) {
@ -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);
}
}

View file

@ -13,6 +13,7 @@
#include <SimpleMovingAverage.h>
#include "CoverageMap.h"
#include "JurisdictionMap.h"
#include "ViewFrustum.h"
#include "VoxelNode.h"
#include "VoxelNodeBag.h"
@ -39,6 +40,7 @@ const uint64_t IGNORE_LAST_SENT = 0;
#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);
@ -170,6 +192,7 @@ public:
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
View 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

View file

@ -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);
@ -708,6 +748,10 @@ int main(int argc, const char * argv[]) {
pthread_join(sendVoxelThread, NULL);
pthread_mutex_destroy(&::treeLock);
if (jurisdiction) {
delete jurisdiction;
}
return 0;
}