mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:44:02 +02:00
Merge pull request #852 from ZappoMan/voxel_sending_helper
Improvements to Voxel Edit in the client
This commit is contained in:
commit
ace9da5b85
23 changed files with 842 additions and 383 deletions
|
@ -95,9 +95,7 @@ const int STARTUP_JITTER_SAMPLES = PACKET_LENGTH_SAMPLES_PER_CHANNEL / 2;
|
|||
// customized canvas that simply forwards requests/events to the singleton application
|
||||
class GLCanvas : public QGLWidget {
|
||||
public:
|
||||
|
||||
GLCanvas();
|
||||
|
||||
protected:
|
||||
|
||||
virtual void initializeGL();
|
||||
|
@ -214,8 +212,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_justEditedVoxel(false),
|
||||
_isLookingAtOtherAvatar(false),
|
||||
_lookatIndicatorScale(1.0f),
|
||||
_paintOn(false),
|
||||
_dominantColor(0),
|
||||
_perfStatsOn(false),
|
||||
_chatEntryOn(false),
|
||||
_oculusTextureID(0),
|
||||
|
@ -225,7 +221,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_audio(&_audioScope, STARTUP_JITTER_SAMPLES),
|
||||
#endif
|
||||
_stopNetworkReceiveThread(false),
|
||||
_stopProcessVoxelsThread(false),
|
||||
_voxelProcessor(this),
|
||||
_voxelEditSender(this),
|
||||
_packetCount(0),
|
||||
_packetsPerSecond(0),
|
||||
_bytesPerSecond(0),
|
||||
|
@ -377,9 +374,10 @@ void Application::initializeGL() {
|
|||
}
|
||||
|
||||
// create thread for parsing of voxel data independent of the main network and rendering threads
|
||||
_voxelProcessor.initialize(_enableProcessVoxelsThread);
|
||||
_voxelEditSender.initialize(_enableProcessVoxelsThread);
|
||||
if (_enableProcessVoxelsThread) {
|
||||
pthread_create(&_processVoxelsThread, NULL, processVoxels, NULL);
|
||||
qDebug("Voxel parsing thread created.\n");
|
||||
qDebug("Voxel parsing thread created.\n");
|
||||
}
|
||||
|
||||
// call terminate before exiting
|
||||
|
@ -418,7 +416,7 @@ void Application::paintGL() {
|
|||
PerfStat("display");
|
||||
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
|
||||
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_myCamera.setTightness (100.0f);
|
||||
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
|
||||
|
@ -548,27 +546,20 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_
|
|||
// Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise)
|
||||
BandwidthMeter::ChannelIndex channel;
|
||||
switch (nodeTypes[i]) {
|
||||
case NODE_TYPE_AGENT:
|
||||
case NODE_TYPE_AVATAR_MIXER:
|
||||
channel = BandwidthMeter::AVATARS;
|
||||
break;
|
||||
case NODE_TYPE_VOXEL_SERVER:
|
||||
channel = BandwidthMeter::VOXELS;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
case NODE_TYPE_AGENT:
|
||||
case NODE_TYPE_AVATAR_MIXER:
|
||||
channel = BandwidthMeter::AVATARS;
|
||||
break;
|
||||
case NODE_TYPE_VOXEL_SERVER:
|
||||
channel = BandwidthMeter::VOXELS;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
self->_bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::sendVoxelServerAddScene() {
|
||||
char message[100];
|
||||
sprintf(message,"%c%s",'Z',"add scene");
|
||||
int messageSize = strlen(message) + 1;
|
||||
controlledBroadcastToNodes((unsigned char*)message, messageSize, & NODE_TYPE_VOXEL_SERVER, 1);
|
||||
}
|
||||
|
||||
void Application::keyPressEvent(QKeyEvent* event) {
|
||||
if (activeWindow() == _window) {
|
||||
if (_chatEntryOn) {
|
||||
|
@ -634,19 +625,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
_viewFrustumOffsetUp += 0.05;
|
||||
break;
|
||||
|
||||
case Qt::Key_Ampersand:
|
||||
_paintOn = !_paintOn;
|
||||
setupPaintingVoxel();
|
||||
break;
|
||||
|
||||
case Qt::Key_AsciiCircum:
|
||||
shiftPaintingColor();
|
||||
break;
|
||||
|
||||
case Qt::Key_Percent:
|
||||
sendVoxelServerAddScene();
|
||||
break;
|
||||
|
||||
case Qt::Key_Semicolon:
|
||||
_audio.ping();
|
||||
break;
|
||||
|
@ -1186,10 +1164,8 @@ void Application::terminate() {
|
|||
pthread_join(_networkReceiveThread, NULL);
|
||||
}
|
||||
|
||||
if (_enableProcessVoxelsThread) {
|
||||
_stopProcessVoxelsThread = true;
|
||||
pthread_join(_processVoxelsThread, NULL);
|
||||
}
|
||||
_voxelProcessor.terminate();
|
||||
_voxelEditSender.terminate();
|
||||
}
|
||||
|
||||
void Application::sendAvatarVoxelURLMessage(const QUrl& url) {
|
||||
|
@ -1539,16 +1515,6 @@ void Application::updateVoxelModeActions() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail) {
|
||||
unsigned char* bufferOut;
|
||||
int sizeOut;
|
||||
|
||||
if (createVoxelEditMessage(type, 0, 1, &detail, bufferOut, sizeOut)){
|
||||
Application::controlledBroadcastToNodes(bufferOut, sizeOut, & NODE_TYPE_VOXEL_SERVER, 1);
|
||||
delete[] bufferOut;
|
||||
}
|
||||
}
|
||||
|
||||
const glm::vec3 Application::getMouseVoxelWorldCoordinates(const VoxelDetail _mouseVoxel) {
|
||||
return glm::vec3((_mouseVoxel.x + _mouseVoxel.s / 2.f) * TREE_SCALE,
|
||||
(_mouseVoxel.y + _mouseVoxel.s / 2.f) * TREE_SCALE,
|
||||
|
@ -1586,12 +1552,7 @@ void Application::chooseVoxelPaintColor() {
|
|||
|
||||
const int MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE = 1500;
|
||||
struct SendVoxelsOperationArgs {
|
||||
unsigned char* newBaseOctCode;
|
||||
unsigned char messageBuffer[MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE];
|
||||
int bufferInUse;
|
||||
uint64_t lastSendTime;
|
||||
int packetsSent;
|
||||
uint64_t bytesSent;
|
||||
unsigned char* newBaseOctCode;
|
||||
};
|
||||
|
||||
bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
|
||||
|
@ -1622,38 +1583,11 @@ bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
|
|||
codeColorBuffer[bytesInCode + RED_INDEX ] = node->getColor()[RED_INDEX ];
|
||||
codeColorBuffer[bytesInCode + GREEN_INDEX] = node->getColor()[GREEN_INDEX];
|
||||
codeColorBuffer[bytesInCode + BLUE_INDEX ] = node->getColor()[BLUE_INDEX ];
|
||||
|
||||
getInstance()->_voxelEditSender.queueVoxelEditMessage(PACKET_TYPE_SET_VOXEL_DESTRUCTIVE,
|
||||
codeColorBuffer, codeAndColorLength);
|
||||
|
||||
// if we have room don't have room in the buffer, then send the previously generated message first
|
||||
if (args->bufferInUse + codeAndColorLength > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) {
|
||||
|
||||
args->packetsSent++;
|
||||
args->bytesSent += args->bufferInUse;
|
||||
|
||||
controlledBroadcastToNodes(args->messageBuffer, args->bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1);
|
||||
args->bufferInUse = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_SET_VOXEL_DESTRUCTIVE)
|
||||
+ sizeof(unsigned short int); // reset
|
||||
|
||||
uint64_t now = usecTimestampNow();
|
||||
// dynamically sleep until we need to fire off the next set of voxels
|
||||
uint64_t elapsed = now - args->lastSendTime;
|
||||
int usecToSleep = CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS - elapsed;
|
||||
if (usecToSleep > 0) {
|
||||
//qDebug("sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, sleeping for %d usecs!\n",
|
||||
// args->packetsSent, (long long int)args->bytesSent, (long long int)elapsed, usecToSleep);
|
||||
|
||||
Application::getInstance()->timer();
|
||||
|
||||
usleep(usecToSleep);
|
||||
} else {
|
||||
//qDebug("sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, no need to sleep!\n",
|
||||
// args->packetsSent, (long long int)args->bytesSent, (long long int)elapsed);
|
||||
}
|
||||
args->lastSendTime = now;
|
||||
}
|
||||
|
||||
// copy this node's code color details into our buffer.
|
||||
memcpy(&args->messageBuffer[args->bufferInUse], codeColorBuffer, codeAndColorLength);
|
||||
args->bufferInUse += codeAndColorLength;
|
||||
delete[] codeColorBuffer;
|
||||
}
|
||||
return true; // keep going
|
||||
}
|
||||
|
@ -1835,15 +1769,6 @@ void Application::importVoxels() {
|
|||
// the server as an set voxel message, this will also rebase the voxels to the new location
|
||||
unsigned char* calculatedOctCode = NULL;
|
||||
SendVoxelsOperationArgs args;
|
||||
args.lastSendTime = usecTimestampNow();
|
||||
args.packetsSent = 0;
|
||||
args.bytesSent = 0;
|
||||
|
||||
int numBytesPacketHeader = populateTypeAndVersion(args.messageBuffer, PACKET_TYPE_SET_VOXEL_DESTRUCTIVE);
|
||||
|
||||
unsigned short int* sequenceAt = (unsigned short int*)&args.messageBuffer[numBytesPacketHeader];
|
||||
*sequenceAt = 0;
|
||||
args.bufferInUse = numBytesPacketHeader + sizeof(unsigned short int); // set to command + sequence
|
||||
|
||||
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
|
||||
// voxel size/position details.
|
||||
|
@ -1857,31 +1782,8 @@ void Application::importVoxels() {
|
|||
|
||||
// send the insert/paste of these voxels
|
||||
importVoxels.recurseTreeWithOperation(sendVoxelsOperation, &args);
|
||||
_voxelEditSender.flushQueue();
|
||||
|
||||
// If we have voxels left in the packet, then send the packet
|
||||
if (args.bufferInUse > (numBytesPacketHeader + sizeof(unsigned short int))) {
|
||||
controlledBroadcastToNodes(args.messageBuffer, args.bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1);
|
||||
|
||||
|
||||
args.packetsSent++;
|
||||
args.bytesSent += args.bufferInUse;
|
||||
|
||||
uint64_t now = usecTimestampNow();
|
||||
// dynamically sleep until we need to fire off the next set of voxels
|
||||
uint64_t elapsed = now - args.lastSendTime;
|
||||
int usecToSleep = CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS - elapsed;
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
//qDebug("after sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, sleeping for %d usecs!\n",
|
||||
// args.packetsSent, (long long int)args.bytesSent, (long long int)elapsed, usecToSleep);
|
||||
usleep(usecToSleep);
|
||||
} else {
|
||||
//qDebug("after sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, no need to sleep!\n",
|
||||
// args.packetsSent, (long long int)args.bytesSent, (long long int)elapsed);
|
||||
}
|
||||
args.lastSendTime = now;
|
||||
}
|
||||
|
||||
if (calculatedOctCode) {
|
||||
delete[] calculatedOctCode;
|
||||
}
|
||||
|
@ -1915,15 +1817,6 @@ void Application::pasteVoxels() {
|
|||
// Recurse the clipboard tree, where everything is root relative, and send all the colored voxels to
|
||||
// the server as an set voxel message, this will also rebase the voxels to the new location
|
||||
SendVoxelsOperationArgs args;
|
||||
args.lastSendTime = usecTimestampNow();
|
||||
args.packetsSent = 0;
|
||||
args.bytesSent = 0;
|
||||
|
||||
int numBytesPacketHeader = populateTypeAndVersion(args.messageBuffer, PACKET_TYPE_SET_VOXEL_DESTRUCTIVE);
|
||||
|
||||
unsigned short int* sequenceAt = (unsigned short int*)&args.messageBuffer[numBytesPacketHeader];
|
||||
*sequenceAt = 0;
|
||||
args.bufferInUse = numBytesPacketHeader + sizeof(unsigned short int); // set to command + sequence
|
||||
|
||||
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
|
||||
// voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a
|
||||
|
@ -1935,14 +1828,7 @@ void Application::pasteVoxels() {
|
|||
}
|
||||
|
||||
_clipboardTree.recurseTreeWithOperation(sendVoxelsOperation, &args);
|
||||
|
||||
// If we have voxels left in the packet, then send the packet
|
||||
if (args.bufferInUse > (numBytesPacketHeader + sizeof(unsigned short int))) {
|
||||
controlledBroadcastToNodes(args.messageBuffer, args.bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1);
|
||||
qDebug("sending packet: %d\n", ++args.packetsSent);
|
||||
args.bytesSent += args.bufferInUse;
|
||||
qDebug("total bytes sent: %lld\n", (long long int)args.bytesSent);
|
||||
}
|
||||
_voxelEditSender.flushQueue();
|
||||
|
||||
if (calculatedOctCode) {
|
||||
delete[] calculatedOctCode;
|
||||
|
@ -2232,7 +2118,7 @@ void Application::init() {
|
|||
_voxels.init();
|
||||
|
||||
_environment.init();
|
||||
|
||||
|
||||
_glowEffect.init();
|
||||
|
||||
_handControl.setScreenDimensions(_glWidget->width(), _glWidget->height());
|
||||
|
@ -2605,7 +2491,8 @@ void Application::update(float deltaTime) {
|
|||
|
||||
// parse voxel packets
|
||||
if (!_enableProcessVoxelsThread) {
|
||||
processVoxels(0);
|
||||
_voxelProcessor.threadRoutine();
|
||||
_voxelEditSender.threadRoutine();
|
||||
}
|
||||
|
||||
//loop through all the other avatars and simulate them...
|
||||
|
@ -2789,26 +2676,6 @@ void Application::updateAvatar(float deltaTime) {
|
|||
sendAvatarVoxelURLMessage(_myAvatar.getVoxels()->getVoxelURL());
|
||||
}
|
||||
}
|
||||
|
||||
// If I'm in paint mode, send a voxel out to VOXEL server nodes.
|
||||
if (_paintOn) {
|
||||
|
||||
glm::vec3 avatarPos = _myAvatar.getPosition();
|
||||
|
||||
// For some reason, we don't want to flip X and Z here.
|
||||
_paintingVoxel.x = avatarPos.x / 10.0;
|
||||
_paintingVoxel.y = avatarPos.y / 10.0;
|
||||
_paintingVoxel.z = avatarPos.z / 10.0;
|
||||
|
||||
if (_paintingVoxel.x >= 0.0 && _paintingVoxel.x <= 1.0 &&
|
||||
_paintingVoxel.y >= 0.0 && _paintingVoxel.y <= 1.0 &&
|
||||
_paintingVoxel.z >= 0.0 && _paintingVoxel.z <= 1.0) {
|
||||
|
||||
PACKET_TYPE message = (_destructiveAddVoxel->isChecked() ?
|
||||
PACKET_TYPE_SET_VOXEL_DESTRUCTIVE : PACKET_TYPE_SET_VOXEL);
|
||||
sendVoxelEditMessage(message, _paintingVoxel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -3303,15 +3170,6 @@ void Application::displayOverlay() {
|
|||
sprintf(nodes, "Servers: %d, Avatars: %d\n", totalServers, totalAvatars);
|
||||
drawtext(_glWidget->width() - 150, 20, 0.10, 0, 1.0, 0, nodes, 1, 0, 0);
|
||||
|
||||
if (_paintOn) {
|
||||
|
||||
char paintMessage[100];
|
||||
sprintf(paintMessage,"Painting (%.3f,%.3f,%.3f/%.3f/%d,%d,%d)",
|
||||
_paintingVoxel.x, _paintingVoxel.y, _paintingVoxel.z, _paintingVoxel.s,
|
||||
(unsigned int)_paintingVoxel.red, (unsigned int)_paintingVoxel.green, (unsigned int)_paintingVoxel.blue);
|
||||
drawtext(_glWidget->width() - 350, 50, 0.10, 0, 1.0, 0, paintMessage, 1, 1, 0);
|
||||
}
|
||||
|
||||
// render the webcam input frame
|
||||
_webcam.renderPreview(_glWidget->width(), _glWidget->height());
|
||||
|
||||
|
@ -3769,26 +3627,6 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::setupPaintingVoxel() {
|
||||
glm::vec3 avatarPos = _myAvatar.getPosition();
|
||||
|
||||
_paintingVoxel.x = avatarPos.z/-10.0; // voxel space x is negative z head space
|
||||
_paintingVoxel.y = avatarPos.y/-10.0; // voxel space y is negative y head space
|
||||
_paintingVoxel.z = avatarPos.x/-10.0; // voxel space z is negative x head space
|
||||
_paintingVoxel.s = 1.0/256;
|
||||
|
||||
shiftPaintingColor();
|
||||
}
|
||||
|
||||
void Application::shiftPaintingColor() {
|
||||
// About the color of the paintbrush... first determine the dominant color
|
||||
_dominantColor = (_dominantColor + 1) % 3; // 0=red,1=green,2=blue
|
||||
_paintingVoxel.red = (_dominantColor == 0) ? randIntInRange(200, 255) : randIntInRange(40, 100);
|
||||
_paintingVoxel.green = (_dominantColor == 1) ? randIntInRange(200, 255) : randIntInRange(40, 100);
|
||||
_paintingVoxel.blue = (_dominantColor == 2) ? randIntInRange(200, 255) : randIntInRange(40, 100);
|
||||
}
|
||||
|
||||
|
||||
void Application::injectVoxelAddedSoundEffect() {
|
||||
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(11025);
|
||||
|
||||
|
@ -3850,7 +3688,7 @@ bool Application::maybeEditVoxelUnderCursor() {
|
|||
if (_mouseVoxel.s != 0) {
|
||||
PACKET_TYPE message = (_destructiveAddVoxel->isChecked() ?
|
||||
PACKET_TYPE_SET_VOXEL_DESTRUCTIVE : PACKET_TYPE_SET_VOXEL);
|
||||
sendVoxelEditMessage(message, _mouseVoxel);
|
||||
_voxelEditSender.sendVoxelEditMessage(message, _mouseVoxel);
|
||||
|
||||
// create the voxel locally so it appears immediately
|
||||
_voxels.createVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s,
|
||||
|
@ -3896,7 +3734,7 @@ bool Application::maybeEditVoxelUnderCursor() {
|
|||
void Application::deleteVoxelUnderCursor() {
|
||||
if (_mouseVoxel.s != 0) {
|
||||
// sending delete to the server is sufficient, server will send new version so we see updates soon enough
|
||||
sendVoxelEditMessage(PACKET_TYPE_ERASE_VOXEL, _mouseVoxel);
|
||||
_voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_ERASE_VOXEL, _mouseVoxel);
|
||||
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(5000);
|
||||
|
||||
if (voxelInjector) {
|
||||
|
@ -4016,15 +3854,16 @@ void Application::nodeKilled(Node* node) {
|
|||
uint16_t nodeID = node->getNodeID();
|
||||
// see if this is the first we've heard of this node...
|
||||
if (_voxelServerJurisdictions.find(nodeID) != _voxelServerJurisdictions.end()) {
|
||||
VoxelPositionSize jurisditionDetails;
|
||||
jurisditionDetails = _voxelServerJurisdictions[nodeID];
|
||||
unsigned char* rootCode = _voxelServerJurisdictions[nodeID].getRootOctalCode();
|
||||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(rootCode, rootDetails);
|
||||
|
||||
printf("voxel server going away...... v[%f, %f, %f, %f]\n",
|
||||
jurisditionDetails.x, jurisditionDetails.y, jurisditionDetails.z, jurisditionDetails.s);
|
||||
rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s);
|
||||
|
||||
// Add the jurisditionDetails object to the list of "fade outs"
|
||||
VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE);
|
||||
fade.voxelDetails = jurisditionDetails;
|
||||
fade.voxelDetails = rootDetails;
|
||||
const float slightly_smaller = 0.99;
|
||||
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
||||
_voxelFades.push_back(fade);
|
||||
|
@ -4045,100 +3884,32 @@ int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLeng
|
|||
if (voxelServer) {
|
||||
uint16_t nodeID = voxelServer->getNodeID();
|
||||
|
||||
VoxelPositionSize jurisditionDetails;
|
||||
voxelDetailsForCode(_voxelSceneStats.getJurisdictionRoot(), jurisditionDetails);
|
||||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(_voxelSceneStats.getJurisdictionRoot(), rootDetails);
|
||||
|
||||
// see if this is the first we've heard of this node...
|
||||
if (_voxelServerJurisdictions.find(nodeID) == _voxelServerJurisdictions.end()) {
|
||||
printf("stats from new voxel server... v[%f, %f, %f, %f]\n",
|
||||
jurisditionDetails.x, jurisditionDetails.y, jurisditionDetails.z, jurisditionDetails.s);
|
||||
rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s);
|
||||
|
||||
// Add the jurisditionDetails object to the list of "fade outs"
|
||||
VoxelFade fade(VoxelFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE);
|
||||
fade.voxelDetails = jurisditionDetails;
|
||||
fade.voxelDetails = rootDetails;
|
||||
const float slightly_smaller = 0.99;
|
||||
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
||||
_voxelFades.push_back(fade);
|
||||
}
|
||||
// store jurisdiction details for later use
|
||||
_voxelServerJurisdictions[nodeID] = jurisditionDetails;
|
||||
// This is bit of fiddling is because JurisdictionMap assumes it is the owner of the values used to construct it
|
||||
// but VoxelSceneStats thinks it's just returning a reference to it's contents. So we need to make a copy of the
|
||||
// details from the VoxelSceneStats to construct the JurisdictionMap
|
||||
JurisdictionMap jurisdictionMap;
|
||||
jurisdictionMap.copyContents(_voxelSceneStats.getJurisdictionRoot(), _voxelSceneStats.getJurisdictionEndNodes());
|
||||
_voxelServerJurisdictions[nodeID] = jurisdictionMap;
|
||||
}
|
||||
return statsMessageLength;
|
||||
}
|
||||
|
||||
// Receive packets from other nodes/servers and decide what to do with them!
|
||||
void* Application::processVoxels(void* args) {
|
||||
Application* app = Application::getInstance();
|
||||
while (!app->_stopProcessVoxelsThread) {
|
||||
|
||||
// check to see if the UI thread asked us to kill the voxel tree. since we're the only thread allowed to do that
|
||||
if (app->_wantToKillLocalVoxels) {
|
||||
app->_voxels.killLocalVoxels();
|
||||
app->_wantToKillLocalVoxels = false;
|
||||
}
|
||||
|
||||
app->_voxelPacketMutex.lock();
|
||||
while (app->_voxelPackets.size() > 0) {
|
||||
NetworkPacket& packet = app->_voxelPackets.front();
|
||||
app->processVoxelPacket(packet.getSenderAddress(), packet.getData(), packet.getLength());
|
||||
app->_voxelPackets.erase(app->_voxelPackets.begin());
|
||||
}
|
||||
app->_voxelPacketMutex.unlock();
|
||||
|
||||
if (!app->_enableProcessVoxelsThread) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (app->_enableProcessVoxelsThread) {
|
||||
pthread_exit(0);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Application::queueVoxelPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
|
||||
_voxelPacketMutex.lock();
|
||||
_voxelPackets.push_back(NetworkPacket(senderAddress, packetData, packetLength));
|
||||
_voxelPacketMutex.unlock();
|
||||
}
|
||||
|
||||
void Application::processVoxelPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
|
||||
PerformanceWarning warn(_renderPipelineWarnings->isChecked(),"processVoxelPacket()");
|
||||
ssize_t messageLength = packetLength;
|
||||
|
||||
// note: PACKET_TYPE_VOXEL_STATS can have PACKET_TYPE_VOXEL_DATA or PACKET_TYPE_VOXEL_DATA_MONOCHROME
|
||||
// immediately following them inside the same packet. So, we process the PACKET_TYPE_VOXEL_STATS first
|
||||
// then process any remaining bytes as if it was another packet
|
||||
if (packetData[0] == PACKET_TYPE_VOXEL_STATS) {
|
||||
|
||||
int statsMessageLength = parseVoxelStats(packetData, messageLength, senderAddress);
|
||||
if (messageLength > statsMessageLength) {
|
||||
packetData += statsMessageLength;
|
||||
messageLength -= statsMessageLength;
|
||||
if (!packetVersionMatch(packetData)) {
|
||||
return; // bail since piggyback data doesn't match our versioning
|
||||
}
|
||||
} else {
|
||||
return; // bail since no piggyback data
|
||||
}
|
||||
} // fall through to piggyback message
|
||||
|
||||
if (_renderVoxels->isChecked()) {
|
||||
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
if (voxelServer && socketMatch(voxelServer->getActiveSocket(), &senderAddress)) {
|
||||
voxelServer->lock();
|
||||
if (packetData[0] == PACKET_TYPE_ENVIRONMENT_DATA) {
|
||||
_environment.parseData(&senderAddress, packetData, messageLength);
|
||||
} else {
|
||||
_voxels.setDataSourceID(voxelServer->getNodeID());
|
||||
_voxels.parseData(packetData, messageLength);
|
||||
_voxels.setDataSourceID(UNKNOWN_NODE_ID);
|
||||
}
|
||||
voxelServer->unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Receive packets from other nodes/servers and decide what to do with them!
|
||||
void* Application::networkReceive(void* args) {
|
||||
sockaddr senderAddress;
|
||||
|
@ -4146,12 +3917,6 @@ void* Application::networkReceive(void* args) {
|
|||
|
||||
Application* app = Application::getInstance();
|
||||
while (!app->_stopNetworkReceiveThread) {
|
||||
// check to see if the UI thread asked us to kill the voxel tree. since we're the only thread allowed to do that
|
||||
if (app->_wantToKillLocalVoxels) {
|
||||
app->_voxels.killLocalVoxels();
|
||||
app->_wantToKillLocalVoxels = false;
|
||||
}
|
||||
|
||||
if (NodeList::getInstance()->getNodeSocket()->receive(&senderAddress, app->_incomingPacket, &bytesReceived)) {
|
||||
|
||||
app->_packetCount++;
|
||||
|
@ -4175,7 +3940,7 @@ void* Application::networkReceive(void* args) {
|
|||
case PACKET_TYPE_VOXEL_STATS:
|
||||
case PACKET_TYPE_ENVIRONMENT_DATA: {
|
||||
// add this packet to our list of voxel packets and process them on the voxel processing
|
||||
app->queueVoxelPacket(senderAddress, app->_incomingPacket, bytesReceived);
|
||||
app->_voxelProcessor.queuePacket(senderAddress, app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
}
|
||||
case PACKET_TYPE_BULK_AVATAR_DATA:
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
#include "ToolsPalette.h"
|
||||
#include "ViewFrustum.h"
|
||||
#include "VoxelFade.h"
|
||||
#include "VoxelEditPacketSender.h"
|
||||
#include "VoxelPacketProcessor.h"
|
||||
#include "VoxelSystem.h"
|
||||
#include "Webcam.h"
|
||||
#include "PieMenu.h"
|
||||
|
@ -73,6 +75,9 @@ static const float NODE_KILLED_BLUE = 0.0f;
|
|||
class Application : public QApplication, public NodeListHook {
|
||||
Q_OBJECT
|
||||
|
||||
friend class VoxelPacketProcessor;
|
||||
friend class VoxelEditPacketSender;
|
||||
|
||||
public:
|
||||
static Application* getInstance() { return static_cast<Application*>(QCoreApplication::instance()); }
|
||||
|
||||
|
@ -218,9 +223,7 @@ private:
|
|||
static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
|
||||
const char* nodeTypes, int numNodeTypes);
|
||||
|
||||
static void sendVoxelServerAddScene();
|
||||
static bool sendVoxelsOperation(VoxelNode* node, void* extraData);
|
||||
static void sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail);
|
||||
static void sendAvatarVoxelURLMessage(const QUrl& url);
|
||||
static void processAvatarVoxelURLMessage(unsigned char* packetData, size_t dataBytes);
|
||||
static void processAvatarFaceVideoMessage(unsigned char* packetData, size_t dataBytes);
|
||||
|
@ -249,8 +252,6 @@ private:
|
|||
|
||||
void checkBandwidthMeterClick();
|
||||
|
||||
void setupPaintingVoxel();
|
||||
void shiftPaintingColor();
|
||||
bool maybeEditVoxelUnderCursor();
|
||||
void deleteVoxelUnderCursor();
|
||||
void eyedropperVoxelUnderCursor();
|
||||
|
@ -266,10 +267,6 @@ private:
|
|||
static void attachNewHeadToNode(Node *newNode);
|
||||
static void* networkReceive(void* args); // network receive thread
|
||||
|
||||
static void* processVoxels(void* args); // voxel parsing thread
|
||||
void processVoxelPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
||||
void queueVoxelPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
||||
|
||||
// methodes handling menu settings
|
||||
typedef void(*settingsAction)(QSettings*, QAction*);
|
||||
static void loadAction(QSettings* set, QAction* action);
|
||||
|
@ -427,10 +424,6 @@ private:
|
|||
glm::vec3 _lookatOtherPosition;
|
||||
float _lookatIndicatorScale;
|
||||
|
||||
bool _paintOn; // Whether to paint voxels as you fly around
|
||||
unsigned char _dominantColor; // The dominant color of the voxel we're painting
|
||||
VoxelDetail _paintingVoxel; // The voxel we're painting if we're painting
|
||||
|
||||
bool _perfStatsOn; // Do we want to display perfStats?
|
||||
|
||||
ChatEntry _chatEntry; // chat entry field
|
||||
|
@ -461,10 +454,8 @@ private:
|
|||
bool _stopNetworkReceiveThread;
|
||||
|
||||
bool _enableProcessVoxelsThread;
|
||||
pthread_t _processVoxelsThread;
|
||||
bool _stopProcessVoxelsThread;
|
||||
std::vector<NetworkPacket> _voxelPackets;
|
||||
QMutex _voxelPacketMutex;
|
||||
VoxelPacketProcessor _voxelProcessor;
|
||||
VoxelEditPacketSender _voxelEditSender;
|
||||
|
||||
unsigned char _incomingPacket[MAX_PACKET_SIZE];
|
||||
int _packetCount;
|
||||
|
@ -483,7 +474,7 @@ private:
|
|||
VoxelSceneStats _voxelSceneStats;
|
||||
int parseVoxelStats(unsigned char* messageData, ssize_t messageLength, sockaddr senderAddress);
|
||||
|
||||
std::map<uint16_t,VoxelPositionSize> _voxelServerJurisdictions;
|
||||
std::map<uint16_t, JurisdictionMap> _voxelServerJurisdictions;
|
||||
|
||||
std::vector<VoxelFade> _voxelFades;
|
||||
};
|
||||
|
|
108
interface/src/VoxelEditPacketSender.cpp
Normal file
108
interface/src/VoxelEditPacketSender.cpp
Normal file
|
@ -0,0 +1,108 @@
|
|||
//
|
||||
// VoxelEditPacketSender.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded voxel packet Sender for the Application
|
||||
//
|
||||
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "VoxelEditPacketSender.h"
|
||||
|
||||
VoxelEditPacketSender::VoxelEditPacketSender(Application* app) :
|
||||
_app(app)
|
||||
{
|
||||
}
|
||||
|
||||
void VoxelEditPacketSender::sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail) {
|
||||
|
||||
// if the app has Voxels disabled, we don't do any of this...
|
||||
if (!_app->_renderVoxels->isChecked()) {
|
||||
return; // bail early
|
||||
}
|
||||
|
||||
unsigned char* bufferOut;
|
||||
int sizeOut;
|
||||
int totalBytesSent = 0;
|
||||
|
||||
if (createVoxelEditMessage(type, 0, 1, &detail, bufferOut, sizeOut)){
|
||||
actuallySendMessage(UNKNOWN_NODE_ID, bufferOut, sizeOut); // sends to all servers... not ideal!
|
||||
delete[] bufferOut;
|
||||
}
|
||||
|
||||
// Tell the application's bandwidth meters about what we've sent
|
||||
_app->_bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(totalBytesSent);
|
||||
}
|
||||
|
||||
void VoxelEditPacketSender::actuallySendMessage(uint16_t nodeID, unsigned char* bufferOut, ssize_t sizeOut) {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
// only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER
|
||||
if (node->getActiveSocket() != NULL && node->getType() == NODE_TYPE_VOXEL_SERVER &&
|
||||
((node->getNodeID() == nodeID) || (nodeID == (uint16_t)UNKNOWN_NODE_ID)) ) {
|
||||
sockaddr* nodeAddress = node->getActiveSocket();
|
||||
queuePacket(*nodeAddress, bufferOut, sizeOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelEditPacketSender::queueVoxelEditMessage(PACKET_TYPE type, unsigned char* codeColorBuffer, ssize_t length) {
|
||||
// We want to filter out edit messages for voxel servers based on the server's Jurisdiction
|
||||
// But we can't really do that with a packed message, since each edit message could be destined
|
||||
// for a different voxel server... So we need to actually manage multiple queued packets... one
|
||||
// for each voxel server
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
// only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER
|
||||
if (node->getActiveSocket() != NULL && node->getType() == NODE_TYPE_VOXEL_SERVER) {
|
||||
|
||||
// we need to get the jurisdiction for this
|
||||
// here we need to get the "pending packet" for this server
|
||||
uint16_t nodeID = node->getNodeID();
|
||||
const JurisdictionMap& map = _app->_voxelServerJurisdictions[nodeID];
|
||||
if (map.isMyJurisdiction(codeColorBuffer, CHECK_NODE_ONLY) == JurisdictionMap::WITHIN) {
|
||||
EditPacketBuffer& packetBuffer = _pendingEditPackets[nodeID];
|
||||
packetBuffer._nodeID = nodeID;
|
||||
|
||||
// If we're switching type, then we send the last one and start over
|
||||
if ((type != packetBuffer._currentType && packetBuffer._currentSize > 0) ||
|
||||
(packetBuffer._currentSize + length >= MAX_PACKET_SIZE)) {
|
||||
flushQueue(packetBuffer);
|
||||
initializePacket(packetBuffer, type);
|
||||
}
|
||||
|
||||
// If the buffer is empty and not correctly initialized for our type...
|
||||
if (type != packetBuffer._currentType && packetBuffer._currentSize == 0) {
|
||||
initializePacket(packetBuffer, type);
|
||||
}
|
||||
|
||||
memcpy(&packetBuffer._currentBuffer[packetBuffer._currentSize], codeColorBuffer, length);
|
||||
packetBuffer._currentSize += length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelEditPacketSender::flushQueue() {
|
||||
for (std::map<uint16_t,EditPacketBuffer>::iterator i = _pendingEditPackets.begin(); i != _pendingEditPackets.end(); i++) {
|
||||
flushQueue(i->second);
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelEditPacketSender::flushQueue(EditPacketBuffer& packetBuffer) {
|
||||
actuallySendMessage(packetBuffer._nodeID, &packetBuffer._currentBuffer[0], packetBuffer._currentSize);
|
||||
packetBuffer._currentSize = 0;
|
||||
packetBuffer._currentType = PACKET_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
void VoxelEditPacketSender::initializePacket(EditPacketBuffer& packetBuffer, PACKET_TYPE type) {
|
||||
packetBuffer._currentSize = populateTypeAndVersion(&packetBuffer._currentBuffer[0], type);
|
||||
unsigned short int* sequenceAt = (unsigned short int*)&packetBuffer._currentBuffer[packetBuffer._currentSize];
|
||||
*sequenceAt = 0;
|
||||
packetBuffer._currentSize += sizeof(unsigned short int); // set to command + sequence
|
||||
packetBuffer._currentType = type;
|
||||
}
|
52
interface/src/VoxelEditPacketSender.h
Normal file
52
interface/src/VoxelEditPacketSender.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// VoxelEditPacketSender.h
|
||||
// interface
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Voxel Packet Sender
|
||||
//
|
||||
|
||||
#ifndef __shared__VoxelEditPacketSender__
|
||||
#define __shared__VoxelEditPacketSender__
|
||||
|
||||
#include <PacketSender.h>
|
||||
#include <SharedUtil.h> // for VoxelDetail
|
||||
|
||||
class Application;
|
||||
|
||||
/// Used for construction of edit voxel packets
|
||||
class EditPacketBuffer {
|
||||
public:
|
||||
EditPacketBuffer() { _currentSize = 0; _currentType = PACKET_TYPE_UNKNOWN; _nodeID = UNKNOWN_NODE_ID; }
|
||||
uint16_t _nodeID;
|
||||
PACKET_TYPE _currentType;
|
||||
unsigned char _currentBuffer[MAX_PACKET_SIZE];
|
||||
ssize_t _currentSize;
|
||||
};
|
||||
|
||||
/// Threaded processor for queueing and sending of outbound edit voxel packets.
|
||||
class VoxelEditPacketSender : public PacketSender {
|
||||
public:
|
||||
VoxelEditPacketSender(Application* app);
|
||||
|
||||
/// Send voxel edit message immediately
|
||||
void sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail);
|
||||
|
||||
/// Queues a voxel edit message. Will potentially sends a pending multi-command packet. Determines which voxel-server
|
||||
/// node or nodes the packet should be sent to.
|
||||
void queueVoxelEditMessage(PACKET_TYPE type, unsigned char* codeColorBuffer, ssize_t length);
|
||||
|
||||
/// flushes all queued packets for all nodes
|
||||
void flushQueue();
|
||||
|
||||
private:
|
||||
void actuallySendMessage(uint16_t nodeID, unsigned char* bufferOut, ssize_t sizeOut);
|
||||
void initializePacket(EditPacketBuffer& packetBuffer, PACKET_TYPE type);
|
||||
void flushQueue(EditPacketBuffer& packetBuffer); // flushes specific queued packet
|
||||
|
||||
Application* _app;
|
||||
std::map<uint16_t,EditPacketBuffer> _pendingEditPackets;
|
||||
};
|
||||
#endif // __shared__VoxelEditPacketSender__
|
62
interface/src/VoxelPacketProcessor.cpp
Normal file
62
interface/src/VoxelPacketProcessor.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// VoxelPacketProcessor.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded voxel packet receiver for the Application
|
||||
//
|
||||
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "VoxelPacketProcessor.h"
|
||||
|
||||
VoxelPacketProcessor::VoxelPacketProcessor(Application* app) :
|
||||
_app(app) {
|
||||
}
|
||||
|
||||
void VoxelPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
|
||||
PerformanceWarning warn(_app->_renderPipelineWarnings->isChecked(),"VoxelPacketProcessor::processPacket()");
|
||||
ssize_t messageLength = packetLength;
|
||||
|
||||
// check to see if the UI thread asked us to kill the voxel tree. since we're the only thread allowed to do that
|
||||
if (_app->_wantToKillLocalVoxels) {
|
||||
_app->_voxels.killLocalVoxels();
|
||||
_app->_wantToKillLocalVoxels = false;
|
||||
}
|
||||
|
||||
// note: PACKET_TYPE_VOXEL_STATS can have PACKET_TYPE_VOXEL_DATA or PACKET_TYPE_VOXEL_DATA_MONOCHROME
|
||||
// immediately following them inside the same packet. So, we process the PACKET_TYPE_VOXEL_STATS first
|
||||
// then process any remaining bytes as if it was another packet
|
||||
if (packetData[0] == PACKET_TYPE_VOXEL_STATS) {
|
||||
|
||||
int statsMessageLength = _app->parseVoxelStats(packetData, messageLength, senderAddress);
|
||||
if (messageLength > statsMessageLength) {
|
||||
packetData += statsMessageLength;
|
||||
messageLength -= statsMessageLength;
|
||||
if (!packetVersionMatch(packetData)) {
|
||||
return; // bail since piggyback data doesn't match our versioning
|
||||
}
|
||||
} else {
|
||||
return; // bail since no piggyback data
|
||||
}
|
||||
} // fall through to piggyback message
|
||||
|
||||
if (_app->_renderVoxels->isChecked()) {
|
||||
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
if (voxelServer && socketMatch(voxelServer->getActiveSocket(), &senderAddress)) {
|
||||
voxelServer->lock();
|
||||
if (packetData[0] == PACKET_TYPE_ENVIRONMENT_DATA) {
|
||||
_app->_environment.parseData(&senderAddress, packetData, messageLength);
|
||||
} else {
|
||||
_app->_voxels.setDataSourceID(voxelServer->getNodeID());
|
||||
_app->_voxels.parseData(packetData, messageLength);
|
||||
_app->_voxels.setDataSourceID(UNKNOWN_NODE_ID);
|
||||
}
|
||||
voxelServer->unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
29
interface/src/VoxelPacketProcessor.h
Normal file
29
interface/src/VoxelPacketProcessor.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// VoxelPacketProcessor.h
|
||||
// interface
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Voxel Packet Receiver
|
||||
//
|
||||
|
||||
#ifndef __shared__VoxelPacketProcessor__
|
||||
#define __shared__VoxelPacketProcessor__
|
||||
|
||||
#include <ReceivedPacketProcessor.h>
|
||||
|
||||
class Application;
|
||||
|
||||
/// Handles processing of incoming voxel packets for the interface application.
|
||||
class VoxelPacketProcessor : public ReceivedPacketProcessor {
|
||||
public:
|
||||
VoxelPacketProcessor(Application* app);
|
||||
|
||||
protected:
|
||||
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
||||
|
||||
private:
|
||||
Application* _app;
|
||||
};
|
||||
#endif // __shared__VoxelPacketProcessor__
|
64
libraries/shared/src/GenericThread.cpp
Normal file
64
libraries/shared/src/GenericThread.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// GenericThread.cpp
|
||||
// shared
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Generic Threaded or non-threaded processing class
|
||||
//
|
||||
|
||||
#include "GenericThread.h"
|
||||
|
||||
GenericThread::GenericThread() :
|
||||
_stopThread(false),
|
||||
_isThreaded(false) // assume non-threaded, must call initialize()
|
||||
{
|
||||
pthread_mutex_init(&_mutex, 0);
|
||||
}
|
||||
|
||||
GenericThread::~GenericThread() {
|
||||
terminate();
|
||||
pthread_mutex_destroy(&_mutex);
|
||||
}
|
||||
|
||||
void GenericThread::initialize(bool isThreaded) {
|
||||
_isThreaded = isThreaded;
|
||||
if (_isThreaded) {
|
||||
pthread_create(&_thread, NULL, GenericThreadEntry, this);
|
||||
}
|
||||
}
|
||||
|
||||
void GenericThread::terminate() {
|
||||
if (_isThreaded) {
|
||||
_stopThread = true;
|
||||
pthread_join(_thread, NULL);
|
||||
_isThreaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
void* GenericThread::threadRoutine() {
|
||||
while (!_stopThread) {
|
||||
|
||||
// override this function to do whatever your class actually does, return false to exit thread early
|
||||
if (!process()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// In non-threaded mode, this will break each time you call it so it's the
|
||||
// callers responsibility to continuously call this method
|
||||
if (!_isThreaded) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_isThreaded) {
|
||||
pthread_exit(0);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extern "C" void* GenericThreadEntry(void* arg) {
|
||||
GenericThread* genericThread = (GenericThread*)arg;
|
||||
return genericThread->threadRoutine();
|
||||
}
|
53
libraries/shared/src/GenericThread.h
Normal file
53
libraries/shared/src/GenericThread.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// GenericThread.h
|
||||
// shared
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Generic Threaded or non-threaded processing class.
|
||||
//
|
||||
|
||||
#ifndef __shared__GenericThread__
|
||||
#define __shared__GenericThread__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/// A basic generic "thread" class. Handles a single thread of control within the application. Can operate in non-threaded
|
||||
/// mode but caller must regularly call threadRoutine() method.
|
||||
class GenericThread {
|
||||
public:
|
||||
GenericThread();
|
||||
virtual ~GenericThread();
|
||||
|
||||
/// Call to start the thread.
|
||||
/// \param bool isThreaded true by default. false for non-threaded mode and caller must call threadRoutine() regularly.
|
||||
void initialize(bool isThreaded = true);
|
||||
|
||||
/// Call to stop the thread
|
||||
void terminate();
|
||||
|
||||
/// If you're running in non-threaded mode, you must call this regularly
|
||||
void* threadRoutine();
|
||||
|
||||
protected:
|
||||
/// Override this function to do whatever your class actually does, return false to exit thread early.
|
||||
virtual bool process() = 0;
|
||||
|
||||
/// Locks all the resources of the thread.
|
||||
void lock() { pthread_mutex_lock(&_mutex); }
|
||||
|
||||
/// Unlocks all the resources of the thread.
|
||||
void unlock() { pthread_mutex_unlock(&_mutex); }
|
||||
|
||||
private:
|
||||
pthread_mutex_t _mutex;
|
||||
|
||||
bool _stopThread;
|
||||
bool _isThreaded;
|
||||
pthread_t _thread;
|
||||
};
|
||||
|
||||
extern "C" void* GenericThreadEntry(void* arg);
|
||||
|
||||
#endif // __shared__GenericThread__
|
|
@ -10,20 +10,53 @@
|
|||
|
||||
#include <cstring>
|
||||
#include <QtDebug>
|
||||
#include <cassert>
|
||||
|
||||
#include "NetworkPacket.h"
|
||||
|
||||
NetworkPacket::NetworkPacket() : _packetLength(0) {
|
||||
NetworkPacket::NetworkPacket() {
|
||||
_packetLength = 0;
|
||||
}
|
||||
|
||||
NetworkPacket::~NetworkPacket() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
void NetworkPacket::copyContents(const sockaddr& address, const unsigned char* packetData, ssize_t packetLength) {
|
||||
_packetLength = 0;
|
||||
if (packetLength >=0 && packetLength <= MAX_PACKET_SIZE) {
|
||||
memcpy(&_address, &address, sizeof(_address));
|
||||
_packetLength = packetLength;
|
||||
memcpy(&_packetData[0], packetData, packetLength);
|
||||
} else {
|
||||
qDebug(">>> NetworkPacket::copyContents() unexpected length=%lu\n",packetLength);
|
||||
}
|
||||
}
|
||||
|
||||
NetworkPacket::NetworkPacket(const NetworkPacket& packet) {
|
||||
memcpy(&_senderAddress, &packet.getSenderAddress(), sizeof(_senderAddress));
|
||||
_packetLength = packet.getLength();
|
||||
memcpy(&_packetData[0], packet.getData(), _packetLength);
|
||||
copyContents(packet.getAddress(), packet.getData(), packet.getLength());
|
||||
}
|
||||
|
||||
NetworkPacket::NetworkPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
|
||||
memcpy(&_senderAddress, &senderAddress, sizeof(_senderAddress));
|
||||
_packetLength = packetLength;
|
||||
memcpy(&_packetData[0], packetData, packetLength);
|
||||
NetworkPacket::NetworkPacket(sockaddr& address, unsigned char* packetData, ssize_t packetLength) {
|
||||
copyContents(address, packetData, packetLength);
|
||||
};
|
||||
|
||||
// copy assignment
|
||||
NetworkPacket& NetworkPacket::operator=(NetworkPacket const& other) {
|
||||
copyContents(other.getAddress(), other.getData(), other.getLength());
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef HAS_MOVE_SEMANTICS
|
||||
// move, same as copy, but other packet won't be used further
|
||||
NetworkPacket::NetworkPacket(NetworkPacket && packet) {
|
||||
copyContents(packet.getAddress(), packet.getData(), packet.getLength());
|
||||
}
|
||||
|
||||
// move assignment
|
||||
NetworkPacket& NetworkPacket::operator=(NetworkPacket&& other) {
|
||||
_packetLength = 0;
|
||||
copyContents(other.getAddress(), other.getData(), other.getLength());
|
||||
return *this;
|
||||
}
|
||||
#endif
|
|
@ -17,24 +17,34 @@
|
|||
|
||||
#include "NodeList.h" // for MAX_PACKET_SIZE
|
||||
|
||||
/// Storage of not-yet processed inbound, or not yet sent outbound generic UDP network packet
|
||||
class NetworkPacket {
|
||||
public:
|
||||
NetworkPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
||||
NetworkPacket(const NetworkPacket& packet);
|
||||
NetworkPacket();
|
||||
//~NetworkPacket();
|
||||
|
||||
sockaddr& getSenderAddress() { return _senderAddress; };
|
||||
ssize_t getLength() const { return _packetLength; };
|
||||
unsigned char* getData() { return &_packetData[0]; };
|
||||
NetworkPacket(const NetworkPacket& packet); // copy constructor
|
||||
~NetworkPacket(); // destructor
|
||||
NetworkPacket& operator= (const NetworkPacket& other); // copy assignment
|
||||
|
||||
const sockaddr& getSenderAddress() const { return _senderAddress; };
|
||||
#ifdef HAS_MOVE_SEMANTICS
|
||||
NetworkPacket(NetworkPacket&& packet); // move?? // same as copy, but other packet won't be used further
|
||||
NetworkPacket& operator= (NetworkPacket&& other); // move assignment
|
||||
#endif
|
||||
|
||||
NetworkPacket(sockaddr& address, unsigned char* packetData, ssize_t packetLength);
|
||||
|
||||
sockaddr& getAddress() { return _address; };
|
||||
ssize_t getLength() const { return _packetLength; };
|
||||
unsigned char* getData() { return &_packetData[0]; };
|
||||
|
||||
const sockaddr& getAddress() const { return _address; };
|
||||
const unsigned char* getData() const { return &_packetData[0]; };
|
||||
|
||||
private:
|
||||
sockaddr _senderAddress;
|
||||
ssize_t _packetLength;
|
||||
unsigned char _packetData[MAX_PACKET_SIZE];
|
||||
void copyContents(const sockaddr& address, const unsigned char* packetData, ssize_t packetLength);
|
||||
|
||||
sockaddr _address;
|
||||
ssize_t _packetLength;
|
||||
unsigned char _packetData[MAX_PACKET_SIZE];
|
||||
};
|
||||
|
||||
#endif /* defined(__shared_NetworkPacket__) */
|
||||
|
|
|
@ -439,7 +439,7 @@ void NodeList::addNodeToList(Node* newNode) {
|
|||
notifyHooksOfAddedNode(newNode);
|
||||
}
|
||||
|
||||
unsigned NodeList::broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes) {
|
||||
unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes) {
|
||||
unsigned n = 0;
|
||||
for(NodeList::iterator node = begin(); node != end(); node++) {
|
||||
// only send to the NodeTypes we are asked to send to.
|
||||
|
|
|
@ -319,3 +319,48 @@ bool isAncestorOf(unsigned char* possibleAncestor, unsigned char* possibleDescen
|
|||
// they all match, so we are an ancestor
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char* hexStringToOctalCode(const QString& input) {
|
||||
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 octalCodeToHexString(unsigned char* octalCode) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#define __hifi__OctalCode__
|
||||
|
||||
#include <string.h>
|
||||
#include <QString>
|
||||
|
||||
const int BITS_IN_BYTE = 8;
|
||||
const int BITS_IN_OCTAL = 3;
|
||||
|
@ -39,7 +40,7 @@ void copyFirstVertexForCode(unsigned char * octalCode, float* output);
|
|||
struct VoxelPositionSize {
|
||||
float x, y, z, s;
|
||||
};
|
||||
void voxelDetailsForCode(unsigned char * octalCode, VoxelPositionSize& voxelPositionSize);
|
||||
void voxelDetailsForCode(unsigned char* octalCode, VoxelPositionSize& voxelPositionSize);
|
||||
|
||||
typedef enum {
|
||||
ILLEGAL_CODE = -2,
|
||||
|
@ -49,4 +50,8 @@ typedef enum {
|
|||
} OctalCodeComparison;
|
||||
|
||||
OctalCodeComparison compareOctalCodes(unsigned char* code1, unsigned char* code2);
|
||||
|
||||
QString octalCodeToHexString(unsigned char* octalCode);
|
||||
unsigned char* hexStringToOctalCode(const QString& input);
|
||||
|
||||
#endif /* defined(__hifi__OctalCode__) */
|
||||
|
|
|
@ -26,7 +26,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
|||
return 1;
|
||||
|
||||
case PACKET_TYPE_VOXEL_STATS:
|
||||
return 1;
|
||||
return 2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_PacketHeaders_h
|
||||
|
||||
typedef char PACKET_TYPE;
|
||||
const PACKET_TYPE PACKET_TYPE_UNKNOWN = 0;
|
||||
const PACKET_TYPE PACKET_TYPE_DOMAIN = 'D';
|
||||
const PACKET_TYPE PACKET_TYPE_PING = 'P';
|
||||
const PACKET_TYPE PACKET_TYPE_PING_REPLY = 'R';
|
||||
|
|
59
libraries/shared/src/PacketSender.cpp
Normal file
59
libraries/shared/src/PacketSender.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// PacketSender.cpp
|
||||
// shared
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded packet sender.
|
||||
//
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
const uint64_t SEND_INTERVAL_USECS = 1000 * 5; // no more than 200pps... should be settable
|
||||
|
||||
#include "NodeList.h"
|
||||
#include "PacketSender.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
PacketSender::PacketSender() {
|
||||
_lastSendTime = usecTimestampNow();
|
||||
}
|
||||
|
||||
|
||||
void PacketSender::queuePacket(sockaddr& address, unsigned char* packetData, ssize_t packetLength) {
|
||||
NetworkPacket packet(address, packetData, packetLength);
|
||||
lock();
|
||||
_packets.push_back(packet);
|
||||
unlock();
|
||||
}
|
||||
|
||||
bool PacketSender::process() {
|
||||
if (_packets.size() == 0) {
|
||||
const uint64_t SEND_THREAD_SLEEP_INTERVAL = (1000 * 1000)/60; // check at 60fps
|
||||
usleep(SEND_THREAD_SLEEP_INTERVAL);
|
||||
}
|
||||
while (_packets.size() > 0) {
|
||||
NetworkPacket& packet = _packets.front();
|
||||
|
||||
// send the packet through the NodeList...
|
||||
UDPSocket* nodeSocket = NodeList::getInstance()->getNodeSocket();
|
||||
|
||||
nodeSocket->send(&packet.getAddress(), packet.getData(), packet.getLength());
|
||||
|
||||
lock();
|
||||
_packets.erase(_packets.begin());
|
||||
unlock();
|
||||
|
||||
uint64_t now = usecTimestampNow();
|
||||
// dynamically sleep until we need to fire off the next set of voxels
|
||||
uint64_t elapsed = now - _lastSendTime;
|
||||
int usecToSleep = SEND_INTERVAL_USECS - elapsed;
|
||||
_lastSendTime = now;
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
|
||||
}
|
||||
return true; // keep running till they terminate us
|
||||
}
|
38
libraries/shared/src/PacketSender.h
Normal file
38
libraries/shared/src/PacketSender.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// PacketSender.h
|
||||
// shared
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded packet sender.
|
||||
//
|
||||
|
||||
#ifndef __shared__PacketSender__
|
||||
#define __shared__PacketSender__
|
||||
|
||||
#include "GenericThread.h"
|
||||
#include "NetworkPacket.h"
|
||||
|
||||
/// Generalized threaded processor for queueing and sending of outbound packets.
|
||||
class PacketSender : public GenericThread {
|
||||
public:
|
||||
|
||||
PacketSender();
|
||||
|
||||
/// Add packet to outbound queue.
|
||||
/// \param sockaddr& address the destination address
|
||||
/// \param packetData pointer to data
|
||||
/// \param ssize_t packetLength size of data
|
||||
/// \thread any thread, typically the application thread
|
||||
void queuePacket(sockaddr& address, unsigned char* packetData, ssize_t packetLength);
|
||||
|
||||
private:
|
||||
virtual bool process();
|
||||
|
||||
std::vector<NetworkPacket> _packets;
|
||||
uint64_t _lastSendTime;
|
||||
|
||||
};
|
||||
|
||||
#endif // __shared__PacketSender__
|
34
libraries/shared/src/ReceivedPacketProcessor.cpp
Normal file
34
libraries/shared/src/ReceivedPacketProcessor.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// ReceivedPacketProcessor.cpp
|
||||
// shared
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded packet receiver.
|
||||
//
|
||||
|
||||
#include "ReceivedPacketProcessor.h"
|
||||
|
||||
void ReceivedPacketProcessor::queuePacket(sockaddr& address, unsigned char* packetData, ssize_t packetLength) {
|
||||
NetworkPacket packet(address, packetData, packetLength);
|
||||
lock();
|
||||
_packets.push_back(packet);
|
||||
unlock();
|
||||
}
|
||||
|
||||
bool ReceivedPacketProcessor::process() {
|
||||
if (_packets.size() == 0) {
|
||||
const uint64_t RECEIVED_THREAD_SLEEP_INTERVAL = (1000 * 1000)/60; // check at 60fps
|
||||
usleep(RECEIVED_THREAD_SLEEP_INTERVAL);
|
||||
}
|
||||
while (_packets.size() > 0) {
|
||||
NetworkPacket& packet = _packets.front();
|
||||
processPacket(packet.getAddress(), packet.getData(), packet.getLength());
|
||||
|
||||
lock();
|
||||
_packets.erase(_packets.begin());
|
||||
unlock();
|
||||
}
|
||||
return true; // keep running till they terminate us
|
||||
}
|
43
libraries/shared/src/ReceivedPacketProcessor.h
Normal file
43
libraries/shared/src/ReceivedPacketProcessor.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// ReceivedPacketProcessor.h
|
||||
// shared
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded received packet processor.
|
||||
//
|
||||
|
||||
#ifndef __shared__ReceivedPacketProcessor__
|
||||
#define __shared__ReceivedPacketProcessor__
|
||||
|
||||
#include "GenericThread.h"
|
||||
#include "NetworkPacket.h"
|
||||
|
||||
/// Generalized threaded processor for handling received inbound packets.
|
||||
class ReceivedPacketProcessor : public GenericThread {
|
||||
public:
|
||||
|
||||
/// Add packet from network receive thread to the processing queue.
|
||||
/// \param sockaddr& senderAddress the address of the sender
|
||||
/// \param packetData pointer to received data
|
||||
/// \param ssize_t packetLength size of received data
|
||||
/// \thread network receive thread
|
||||
void queuePacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
||||
|
||||
protected:
|
||||
/// Callback for processing of recieved packets. Implement this to process the incoming packets.
|
||||
/// \param sockaddr& senderAddress the address of the sender
|
||||
/// \param packetData pointer to received data
|
||||
/// \param ssize_t packetLength size of received data
|
||||
/// \thread "this" individual processing thread
|
||||
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) = 0;
|
||||
|
||||
/// Implements generic processing behavior for this thread.
|
||||
virtual bool process();
|
||||
private:
|
||||
|
||||
std::vector<NetworkPacket> _packets;
|
||||
};
|
||||
|
||||
#endif // __shared__PacketReceiver__
|
|
@ -13,6 +13,63 @@
|
|||
#include "JurisdictionMap.h"
|
||||
#include "VoxelNode.h"
|
||||
|
||||
|
||||
// standard assignment
|
||||
// copy assignment
|
||||
JurisdictionMap& JurisdictionMap::operator=(const JurisdictionMap& other) {
|
||||
copyContents(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef HAS_MOVE_SEMANTICS
|
||||
// Move constructor
|
||||
JurisdictionMap::JurisdictionMap(JurisdictionMap&& other) : _rootOctalCode(NULL) {
|
||||
init(other._rootOctalCode, other._endNodes);
|
||||
other._rootOctalCode = NULL;
|
||||
other._endNodes.clear();
|
||||
}
|
||||
|
||||
// move assignment
|
||||
JurisdictionMap& JurisdictionMap::operator=(JurisdictionMap&& other) {
|
||||
init(other._rootOctalCode, other._endNodes);
|
||||
other._rootOctalCode = NULL;
|
||||
other._endNodes.clear();
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Copy constructor
|
||||
JurisdictionMap::JurisdictionMap(const JurisdictionMap& other) : _rootOctalCode(NULL) {
|
||||
copyContents(other);
|
||||
}
|
||||
|
||||
void JurisdictionMap::copyContents(unsigned char* rootCodeIn, const std::vector<unsigned char*>& endNodesIn) {
|
||||
unsigned char* rootCode;
|
||||
std::vector<unsigned char*> endNodes;
|
||||
if (rootCodeIn) {
|
||||
int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(rootCodeIn));
|
||||
rootCode = new unsigned char[bytes];
|
||||
memcpy(rootCode, rootCodeIn, bytes);
|
||||
} else {
|
||||
rootCode = new unsigned char[1];
|
||||
*rootCode = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < endNodesIn.size(); i++) {
|
||||
if (endNodesIn[i]) {
|
||||
int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(endNodesIn[i]));
|
||||
unsigned char* endNodeCode = new unsigned char[bytes];
|
||||
memcpy(endNodeCode, endNodesIn[i], bytes);
|
||||
endNodes.push_back(endNodeCode);
|
||||
}
|
||||
}
|
||||
init(rootCode, endNodes);
|
||||
}
|
||||
|
||||
void JurisdictionMap::copyContents(const JurisdictionMap& other) {
|
||||
copyContents(other._rootOctalCode, other._endNodes);
|
||||
}
|
||||
|
||||
JurisdictionMap::~JurisdictionMap() {
|
||||
clear();
|
||||
}
|
||||
|
@ -74,7 +131,7 @@ void JurisdictionMap::init(unsigned char* rootOctalCode, const std::vector<unsig
|
|||
|
||||
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;
|
||||
|
@ -82,12 +139,6 @@ JurisdictionMap::Area JurisdictionMap::isMyJurisdiction(unsigned char* nodeOctal
|
|||
|
||||
// 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++) {
|
||||
|
@ -98,7 +149,6 @@ JurisdictionMap::Area JurisdictionMap::isMyJurisdiction(unsigned char* nodeOctal
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isInJurisdiction ? WITHIN : BELOW;
|
||||
}
|
||||
|
||||
|
@ -147,48 +197,3 @@ bool JurisdictionMap::writeToFile(const char* filename) {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,20 @@ public:
|
|||
BELOW
|
||||
};
|
||||
|
||||
JurisdictionMap();
|
||||
// standard constructors
|
||||
JurisdictionMap(); // default constructor
|
||||
JurisdictionMap(const JurisdictionMap& other); // copy constructor
|
||||
|
||||
// standard assignment
|
||||
JurisdictionMap& operator=(const JurisdictionMap& other); // copy assignment
|
||||
|
||||
#ifdef HAS_MOVE_SEMANTICS
|
||||
// move constructor and assignment
|
||||
JurisdictionMap(JurisdictionMap&& other); // move constructor
|
||||
JurisdictionMap& operator= (JurisdictionMap&& other); // move assignment
|
||||
#endif
|
||||
|
||||
// application constructors
|
||||
JurisdictionMap(const char* filename);
|
||||
JurisdictionMap(unsigned char* rootOctalCode, const std::vector<unsigned char*>& endNodes);
|
||||
JurisdictionMap(const char* rootHextString, const char* endNodesHextString);
|
||||
|
@ -34,14 +47,14 @@ public:
|
|||
unsigned char* getRootOctalCode() const { return _rootOctalCode; }
|
||||
unsigned char* getEndNodeOctalCode(int index) const { return _endNodes[index]; }
|
||||
int getEndNodeCount() const { return _endNodes.size(); }
|
||||
|
||||
void copyContents(unsigned char* rootCodeIn, const std::vector<unsigned char*>& endNodesIn);
|
||||
|
||||
private:
|
||||
void copyContents(const JurisdictionMap& other); // use assignment instead
|
||||
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;
|
||||
};
|
||||
|
|
|
@ -17,21 +17,18 @@
|
|||
const int samples = 100;
|
||||
VoxelSceneStats::VoxelSceneStats() :
|
||||
_elapsedAverage(samples),
|
||||
_bitsPerVoxelAverage(samples)
|
||||
_bitsPerVoxelAverage(samples),
|
||||
_jurisdictionRoot(NULL)
|
||||
{
|
||||
reset();
|
||||
_isReadyToSend = false;
|
||||
_isStarted = false;
|
||||
_jurisdictionRoot = NULL;
|
||||
}
|
||||
|
||||
VoxelSceneStats::~VoxelSceneStats() {
|
||||
if (_jurisdictionRoot) {
|
||||
delete[] _jurisdictionRoot;
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* root, JurisdictionMap* jurisdictionMap) {
|
||||
reset(); // resets packet and voxel stats
|
||||
_isStarted = true;
|
||||
|
@ -54,6 +51,16 @@ void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* r
|
|||
_jurisdictionRoot = new unsigned char[bytes];
|
||||
memcpy(_jurisdictionRoot, jurisdictionRoot, bytes);
|
||||
}
|
||||
|
||||
for (int i=0; i < jurisdictionMap->getEndNodeCount(); i++) {
|
||||
unsigned char* endNodeCode = jurisdictionMap->getEndNodeOctalCode(i);
|
||||
if (endNodeCode) {
|
||||
int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(endNodeCode));
|
||||
unsigned char* endNodeCodeCopy = new unsigned char[bytes];
|
||||
memcpy(endNodeCodeCopy, endNodeCode, bytes);
|
||||
_jurisdictionEndNodes.push_back(endNodeCodeCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,6 +132,17 @@ void VoxelSceneStats::reset() {
|
|||
_existsBitsWritten = 0;
|
||||
_existsInPacketBitsWritten = 0;
|
||||
_treesRemoved = 0;
|
||||
|
||||
if (_jurisdictionRoot) {
|
||||
delete[] _jurisdictionRoot;
|
||||
_jurisdictionRoot = NULL;
|
||||
}
|
||||
for (int i=0; i < _jurisdictionEndNodes.size(); i++) {
|
||||
if (_jurisdictionEndNodes[i]) {
|
||||
delete[] _jurisdictionEndNodes[i];
|
||||
}
|
||||
}
|
||||
_jurisdictionEndNodes.clear();
|
||||
}
|
||||
|
||||
void VoxelSceneStats::packetSent(int bytes) {
|
||||
|
@ -303,6 +321,21 @@ int VoxelSceneStats::packIntoMessage(unsigned char* destinationBuffer, int avail
|
|||
destinationBuffer += sizeof(bytes);
|
||||
memcpy(destinationBuffer, _jurisdictionRoot, bytes);
|
||||
destinationBuffer += bytes;
|
||||
|
||||
// if and only if there's a root jurisdiction, also include the end nodes
|
||||
int endNodeCount = _jurisdictionEndNodes.size();
|
||||
|
||||
memcpy(destinationBuffer, &endNodeCount, sizeof(endNodeCount));
|
||||
destinationBuffer += sizeof(endNodeCount);
|
||||
|
||||
for (int i=0; i < endNodeCount; i++) {
|
||||
unsigned char* endNodeCode = _jurisdictionEndNodes[i];
|
||||
int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(endNodeCode));
|
||||
memcpy(destinationBuffer, &bytes, sizeof(bytes));
|
||||
destinationBuffer += sizeof(bytes);
|
||||
memcpy(destinationBuffer, endNodeCode, bytes);
|
||||
destinationBuffer += bytes;
|
||||
}
|
||||
} else {
|
||||
int bytes = 0;
|
||||
memcpy(destinationBuffer, &bytes, sizeof(bytes));
|
||||
|
@ -406,10 +439,25 @@ int VoxelSceneStats::unpackFromMessage(unsigned char* sourceBuffer, int availabl
|
|||
|
||||
if (bytes == 0) {
|
||||
_jurisdictionRoot = NULL;
|
||||
_jurisdictionEndNodes.clear();
|
||||
} else {
|
||||
_jurisdictionRoot = new unsigned char[bytes];
|
||||
memcpy(_jurisdictionRoot, sourceBuffer, bytes);
|
||||
sourceBuffer += bytes;
|
||||
// if and only if there's a root jurisdiction, also include the end nodes
|
||||
_jurisdictionEndNodes.clear();
|
||||
int endNodeCount = 0;
|
||||
memcpy(&endNodeCount, sourceBuffer, sizeof(endNodeCount));
|
||||
sourceBuffer += sizeof(endNodeCount);
|
||||
for (int i=0; i < endNodeCount; i++) {
|
||||
int bytes = 0;
|
||||
memcpy(&bytes, sourceBuffer, sizeof(bytes));
|
||||
sourceBuffer += sizeof(bytes);
|
||||
unsigned char* endNodeCode = new unsigned char[bytes];
|
||||
memcpy(endNodeCode, sourceBuffer, bytes);
|
||||
sourceBuffer += bytes;
|
||||
_jurisdictionEndNodes.push_back(endNodeCode);
|
||||
}
|
||||
}
|
||||
|
||||
// running averages
|
||||
|
|
|
@ -81,7 +81,7 @@ public:
|
|||
char* getItemValue(int item);
|
||||
|
||||
unsigned char* getJurisdictionRoot() const { return _jurisdictionRoot; }
|
||||
|
||||
const std::vector<unsigned char*>& getJurisdictionEndNodes() const { return _jurisdictionEndNodes; }
|
||||
|
||||
private:
|
||||
bool _isReadyToSend;
|
||||
|
@ -172,6 +172,7 @@ private:
|
|||
char _itemValueBuffer[MAX_ITEM_VALUE_LENGTH];
|
||||
|
||||
unsigned char* _jurisdictionRoot;
|
||||
std::vector<unsigned char*> _jurisdictionEndNodes;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__VoxelSceneStats__) */
|
||||
|
|
Loading…
Reference in a new issue