mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 18:25:52 +02:00
Merge remote-tracking branch 'upstream/master' into menu
This commit is contained in:
commit
443203201e
23 changed files with 841 additions and 258 deletions
|
@ -114,8 +114,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
||||||
_justEditedVoxel(false),
|
_justEditedVoxel(false),
|
||||||
_isLookingAtOtherAvatar(false),
|
_isLookingAtOtherAvatar(false),
|
||||||
_lookatIndicatorScale(1.0f),
|
_lookatIndicatorScale(1.0f),
|
||||||
_paintOn(false),
|
|
||||||
_dominantColor(0),
|
|
||||||
_perfStatsOn(false),
|
_perfStatsOn(false),
|
||||||
_chatEntryOn(false),
|
_chatEntryOn(false),
|
||||||
_oculusTextureID(0),
|
_oculusTextureID(0),
|
||||||
|
@ -125,7 +123,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
||||||
_audio(&_audioScope, STARTUP_JITTER_SAMPLES),
|
_audio(&_audioScope, STARTUP_JITTER_SAMPLES),
|
||||||
#endif
|
#endif
|
||||||
_stopNetworkReceiveThread(false),
|
_stopNetworkReceiveThread(false),
|
||||||
_stopProcessVoxelsThread(false),
|
_voxelProcessor(this),
|
||||||
|
_voxelEditSender(this),
|
||||||
_packetCount(0),
|
_packetCount(0),
|
||||||
_packetsPerSecond(0),
|
_packetsPerSecond(0),
|
||||||
_bytesPerSecond(0),
|
_bytesPerSecond(0),
|
||||||
|
@ -278,9 +277,10 @@ void Application::initializeGL() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// create thread for parsing of voxel data independent of the main network and rendering threads
|
// create thread for parsing of voxel data independent of the main network and rendering threads
|
||||||
|
_voxelProcessor.initialize(_enableProcessVoxelsThread);
|
||||||
|
_voxelEditSender.initialize(_enableProcessVoxelsThread);
|
||||||
if (_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
|
// call terminate before exiting
|
||||||
|
@ -319,7 +319,7 @@ void Application::paintGL() {
|
||||||
PerfStat("display");
|
PerfStat("display");
|
||||||
|
|
||||||
glEnable(GL_LINE_SMOOTH);
|
glEnable(GL_LINE_SMOOTH);
|
||||||
|
|
||||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||||
_myCamera.setTightness (100.0f);
|
_myCamera.setTightness (100.0f);
|
||||||
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
|
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
|
||||||
|
@ -451,27 +451,20 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_
|
||||||
// Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise)
|
// Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise)
|
||||||
BandwidthMeter::ChannelIndex channel;
|
BandwidthMeter::ChannelIndex channel;
|
||||||
switch (nodeTypes[i]) {
|
switch (nodeTypes[i]) {
|
||||||
case NODE_TYPE_AGENT:
|
case NODE_TYPE_AGENT:
|
||||||
case NODE_TYPE_AVATAR_MIXER:
|
case NODE_TYPE_AVATAR_MIXER:
|
||||||
channel = BandwidthMeter::AVATARS;
|
channel = BandwidthMeter::AVATARS;
|
||||||
break;
|
break;
|
||||||
case NODE_TYPE_VOXEL_SERVER:
|
case NODE_TYPE_VOXEL_SERVER:
|
||||||
channel = BandwidthMeter::VOXELS;
|
channel = BandwidthMeter::VOXELS;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
self->_bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes);
|
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) {
|
void Application::keyPressEvent(QKeyEvent* event) {
|
||||||
if (activeWindow() == _window) {
|
if (activeWindow() == _window) {
|
||||||
if (_chatEntryOn) {
|
if (_chatEntryOn) {
|
||||||
|
@ -1056,10 +1049,8 @@ void Application::terminate() {
|
||||||
pthread_join(_networkReceiveThread, NULL);
|
pthread_join(_networkReceiveThread, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_enableProcessVoxelsThread) {
|
_voxelProcessor.terminate();
|
||||||
_stopProcessVoxelsThread = true;
|
_voxelEditSender.terminate();
|
||||||
pthread_join(_processVoxelsThread, NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Avatar* processAvatarMessageHeader(unsigned char*& packetData, size_t& dataBytes) {
|
static Avatar* processAvatarMessageHeader(unsigned char*& packetData, size_t& dataBytes) {
|
||||||
|
@ -1251,12 +1242,7 @@ void Application::resetSwatchColors() {
|
||||||
|
|
||||||
const int MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE = 1500;
|
const int MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE = 1500;
|
||||||
struct SendVoxelsOperationArgs {
|
struct SendVoxelsOperationArgs {
|
||||||
unsigned char* newBaseOctCode;
|
unsigned char* newBaseOctCode;
|
||||||
unsigned char messageBuffer[MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE];
|
|
||||||
int bufferInUse;
|
|
||||||
uint64_t lastSendTime;
|
|
||||||
int packetsSent;
|
|
||||||
uint64_t bytesSent;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
|
bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
|
||||||
|
@ -1287,38 +1273,11 @@ bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
|
||||||
codeColorBuffer[bytesInCode + RED_INDEX ] = node->getColor()[RED_INDEX ];
|
codeColorBuffer[bytesInCode + RED_INDEX ] = node->getColor()[RED_INDEX ];
|
||||||
codeColorBuffer[bytesInCode + GREEN_INDEX] = node->getColor()[GREEN_INDEX];
|
codeColorBuffer[bytesInCode + GREEN_INDEX] = node->getColor()[GREEN_INDEX];
|
||||||
codeColorBuffer[bytesInCode + BLUE_INDEX ] = node->getColor()[BLUE_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
|
delete[] codeColorBuffer;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
return true; // keep going
|
return true; // keep going
|
||||||
}
|
}
|
||||||
|
@ -1500,15 +1459,6 @@ void Application::importVoxels() {
|
||||||
// the server as an set voxel message, this will also rebase the voxels to the new location
|
// the server as an set voxel message, this will also rebase the voxels to the new location
|
||||||
unsigned char* calculatedOctCode = NULL;
|
unsigned char* calculatedOctCode = NULL;
|
||||||
SendVoxelsOperationArgs args;
|
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
|
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
|
||||||
// voxel size/position details.
|
// voxel size/position details.
|
||||||
|
@ -1522,31 +1472,8 @@ void Application::importVoxels() {
|
||||||
|
|
||||||
// send the insert/paste of these voxels
|
// send the insert/paste of these voxels
|
||||||
importVoxels.recurseTreeWithOperation(sendVoxelsOperation, &args);
|
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) {
|
if (calculatedOctCode) {
|
||||||
delete[] calculatedOctCode;
|
delete[] calculatedOctCode;
|
||||||
}
|
}
|
||||||
|
@ -1580,15 +1507,6 @@ void Application::pasteVoxels() {
|
||||||
// Recurse the clipboard tree, where everything is root relative, and send all the colored voxels to
|
// 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
|
// the server as an set voxel message, this will also rebase the voxels to the new location
|
||||||
SendVoxelsOperationArgs args;
|
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
|
// 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
|
// voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a
|
||||||
|
@ -1600,14 +1518,7 @@ void Application::pasteVoxels() {
|
||||||
}
|
}
|
||||||
|
|
||||||
_clipboardTree.recurseTreeWithOperation(sendVoxelsOperation, &args);
|
_clipboardTree.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);
|
|
||||||
qDebug("sending packet: %d\n", ++args.packetsSent);
|
|
||||||
args.bytesSent += args.bufferInUse;
|
|
||||||
qDebug("total bytes sent: %lld\n", (long long int)args.bytesSent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (calculatedOctCode) {
|
if (calculatedOctCode) {
|
||||||
delete[] calculatedOctCode;
|
delete[] calculatedOctCode;
|
||||||
|
@ -1654,7 +1565,7 @@ void Application::init() {
|
||||||
_voxels.init();
|
_voxels.init();
|
||||||
|
|
||||||
_environment.init();
|
_environment.init();
|
||||||
|
|
||||||
_glowEffect.init();
|
_glowEffect.init();
|
||||||
|
|
||||||
_handControl.setScreenDimensions(_glWidget->width(), _glWidget->height());
|
_handControl.setScreenDimensions(_glWidget->width(), _glWidget->height());
|
||||||
|
@ -2025,7 +1936,8 @@ void Application::update(float deltaTime) {
|
||||||
|
|
||||||
// parse voxel packets
|
// parse voxel packets
|
||||||
if (!_enableProcessVoxelsThread) {
|
if (!_enableProcessVoxelsThread) {
|
||||||
processVoxels(0);
|
_voxelProcessor.threadRoutine();
|
||||||
|
_voxelEditSender.threadRoutine();
|
||||||
}
|
}
|
||||||
|
|
||||||
//loop through all the other avatars and simulate them...
|
//loop through all the other avatars and simulate them...
|
||||||
|
@ -2725,15 +2637,6 @@ void Application::displayOverlay() {
|
||||||
sprintf(nodes, "Servers: %d, Avatars: %d\n", totalServers, totalAvatars);
|
sprintf(nodes, "Servers: %d, Avatars: %d\n", totalServers, totalAvatars);
|
||||||
drawtext(_glWidget->width() - 150, 20, 0.10, 0, 1.0, 0, nodes, 1, 0, 0);
|
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
|
// render the webcam input frame
|
||||||
_webcam.renderPreview(_glWidget->width(), _glWidget->height());
|
_webcam.renderPreview(_glWidget->width(), _glWidget->height());
|
||||||
|
|
||||||
|
@ -3199,26 +3102,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() {
|
void Application::injectVoxelAddedSoundEffect() {
|
||||||
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(11025);
|
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(11025);
|
||||||
|
|
||||||
|
@ -3329,7 +3212,7 @@ bool Application::maybeEditVoxelUnderCursor() {
|
||||||
void Application::deleteVoxelUnderCursor() {
|
void Application::deleteVoxelUnderCursor() {
|
||||||
if (_mouseVoxel.s != 0) {
|
if (_mouseVoxel.s != 0) {
|
||||||
// sending delete to the server is sufficient, server will send new version so we see updates soon enough
|
// 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);
|
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(5000);
|
||||||
|
|
||||||
if (voxelInjector) {
|
if (voxelInjector) {
|
||||||
|
@ -3433,15 +3316,16 @@ void Application::nodeKilled(Node* node) {
|
||||||
uint16_t nodeID = node->getNodeID();
|
uint16_t nodeID = node->getNodeID();
|
||||||
// see if this is the first we've heard of this node...
|
// see if this is the first we've heard of this node...
|
||||||
if (_voxelServerJurisdictions.find(nodeID) != _voxelServerJurisdictions.end()) {
|
if (_voxelServerJurisdictions.find(nodeID) != _voxelServerJurisdictions.end()) {
|
||||||
VoxelPositionSize jurisditionDetails;
|
unsigned char* rootCode = _voxelServerJurisdictions[nodeID].getRootOctalCode();
|
||||||
jurisditionDetails = _voxelServerJurisdictions[nodeID];
|
VoxelPositionSize rootDetails;
|
||||||
|
voxelDetailsForCode(rootCode, rootDetails);
|
||||||
|
|
||||||
printf("voxel server going away...... v[%f, %f, %f, %f]\n",
|
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"
|
// Add the jurisditionDetails object to the list of "fade outs"
|
||||||
VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE);
|
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;
|
const float slightly_smaller = 0.99;
|
||||||
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
||||||
_voxelFades.push_back(fade);
|
_voxelFades.push_back(fade);
|
||||||
|
@ -3462,23 +3346,28 @@ int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLeng
|
||||||
if (voxelServer) {
|
if (voxelServer) {
|
||||||
uint16_t nodeID = voxelServer->getNodeID();
|
uint16_t nodeID = voxelServer->getNodeID();
|
||||||
|
|
||||||
VoxelPositionSize jurisditionDetails;
|
VoxelPositionSize rootDetails;
|
||||||
voxelDetailsForCode(_voxelSceneStats.getJurisdictionRoot(), jurisditionDetails);
|
voxelDetailsForCode(_voxelSceneStats.getJurisdictionRoot(), rootDetails);
|
||||||
|
|
||||||
// see if this is the first we've heard of this node...
|
// see if this is the first we've heard of this node...
|
||||||
if (_voxelServerJurisdictions.find(nodeID) == _voxelServerJurisdictions.end()) {
|
if (_voxelServerJurisdictions.find(nodeID) == _voxelServerJurisdictions.end()) {
|
||||||
printf("stats from new voxel server... v[%f, %f, %f, %f]\n",
|
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"
|
// Add the jurisditionDetails object to the list of "fade outs"
|
||||||
VoxelFade fade(VoxelFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE);
|
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;
|
const float slightly_smaller = 0.99;
|
||||||
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
||||||
_voxelFades.push_back(fade);
|
_voxelFades.push_back(fade);
|
||||||
}
|
}
|
||||||
// store jurisdiction details for later use
|
// 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;
|
return statsMessageLength;
|
||||||
}
|
}
|
||||||
|
@ -3563,12 +3452,6 @@ void* Application::networkReceive(void* args) {
|
||||||
|
|
||||||
Application* app = Application::getInstance();
|
Application* app = Application::getInstance();
|
||||||
while (!app->_stopNetworkReceiveThread) {
|
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)) {
|
if (NodeList::getInstance()->getNodeSocket()->receive(&senderAddress, app->_incomingPacket, &bytesReceived)) {
|
||||||
|
|
||||||
app->_packetCount++;
|
app->_packetCount++;
|
||||||
|
@ -3592,7 +3475,7 @@ void* Application::networkReceive(void* args) {
|
||||||
case PACKET_TYPE_VOXEL_STATS:
|
case PACKET_TYPE_VOXEL_STATS:
|
||||||
case PACKET_TYPE_ENVIRONMENT_DATA: {
|
case PACKET_TYPE_ENVIRONMENT_DATA: {
|
||||||
// add this packet to our list of voxel packets and process them on the voxel processing
|
// 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;
|
break;
|
||||||
}
|
}
|
||||||
case PACKET_TYPE_BULK_AVATAR_DATA:
|
case PACKET_TYPE_BULK_AVATAR_DATA:
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
#include "ToolsPalette.h"
|
#include "ToolsPalette.h"
|
||||||
#include "ViewFrustum.h"
|
#include "ViewFrustum.h"
|
||||||
#include "VoxelFade.h"
|
#include "VoxelFade.h"
|
||||||
|
#include "VoxelEditPacketSender.h"
|
||||||
|
#include "VoxelPacketProcessor.h"
|
||||||
#include "VoxelSystem.h"
|
#include "VoxelSystem.h"
|
||||||
#include "Webcam.h"
|
#include "Webcam.h"
|
||||||
#include "PieMenu.h"
|
#include "PieMenu.h"
|
||||||
|
@ -74,6 +76,9 @@ static const float NODE_KILLED_BLUE = 0.0f;
|
||||||
class Application : public QApplication, public NodeListHook {
|
class Application : public QApplication, public NodeListHook {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class VoxelPacketProcessor;
|
||||||
|
friend class VoxelEditPacketSender;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static Application* getInstance() { return static_cast<Application*>(QCoreApplication::instance()); }
|
static Application* getInstance() { return static_cast<Application*>(QCoreApplication::instance()); }
|
||||||
|
|
||||||
|
@ -220,8 +225,6 @@ private:
|
||||||
|
|
||||||
void checkBandwidthMeterClick();
|
void checkBandwidthMeterClick();
|
||||||
|
|
||||||
void setupPaintingVoxel();
|
|
||||||
void shiftPaintingColor();
|
|
||||||
bool maybeEditVoxelUnderCursor();
|
bool maybeEditVoxelUnderCursor();
|
||||||
void deleteVoxelUnderCursor();
|
void deleteVoxelUnderCursor();
|
||||||
void eyedropperVoxelUnderCursor();
|
void eyedropperVoxelUnderCursor();
|
||||||
|
@ -325,10 +328,6 @@ private:
|
||||||
glm::vec3 _lookatOtherPosition;
|
glm::vec3 _lookatOtherPosition;
|
||||||
float _lookatIndicatorScale;
|
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?
|
bool _perfStatsOn; // Do we want to display perfStats?
|
||||||
|
|
||||||
ChatEntry _chatEntry; // chat entry field
|
ChatEntry _chatEntry; // chat entry field
|
||||||
|
@ -359,10 +358,8 @@ private:
|
||||||
bool _stopNetworkReceiveThread;
|
bool _stopNetworkReceiveThread;
|
||||||
|
|
||||||
bool _enableProcessVoxelsThread;
|
bool _enableProcessVoxelsThread;
|
||||||
pthread_t _processVoxelsThread;
|
VoxelPacketProcessor _voxelProcessor;
|
||||||
bool _stopProcessVoxelsThread;
|
VoxelEditPacketSender _voxelEditSender;
|
||||||
std::vector<NetworkPacket> _voxelPackets;
|
|
||||||
QMutex _voxelPacketMutex;
|
|
||||||
|
|
||||||
unsigned char _incomingPacket[MAX_PACKET_SIZE];
|
unsigned char _incomingPacket[MAX_PACKET_SIZE];
|
||||||
int _packetCount;
|
int _packetCount;
|
||||||
|
@ -381,7 +378,7 @@ private:
|
||||||
VoxelSceneStats _voxelSceneStats;
|
VoxelSceneStats _voxelSceneStats;
|
||||||
int parseVoxelStats(unsigned char* messageData, ssize_t messageLength, sockaddr senderAddress);
|
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;
|
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 <cstring>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include "NetworkPacket.h"
|
#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) {
|
NetworkPacket::NetworkPacket(const NetworkPacket& packet) {
|
||||||
memcpy(&_senderAddress, &packet.getSenderAddress(), sizeof(_senderAddress));
|
copyContents(packet.getAddress(), packet.getData(), packet.getLength());
|
||||||
_packetLength = packet.getLength();
|
|
||||||
memcpy(&_packetData[0], packet.getData(), _packetLength);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkPacket::NetworkPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
|
NetworkPacket::NetworkPacket(sockaddr& address, unsigned char* packetData, ssize_t packetLength) {
|
||||||
memcpy(&_senderAddress, &senderAddress, sizeof(_senderAddress));
|
copyContents(address, packetData, packetLength);
|
||||||
_packetLength = packetLength;
|
|
||||||
memcpy(&_packetData[0], 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
|
#include "NodeList.h" // for MAX_PACKET_SIZE
|
||||||
|
|
||||||
|
/// Storage of not-yet processed inbound, or not yet sent outbound generic UDP network packet
|
||||||
class NetworkPacket {
|
class NetworkPacket {
|
||||||
public:
|
public:
|
||||||
NetworkPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
|
||||||
NetworkPacket(const NetworkPacket& packet);
|
|
||||||
NetworkPacket();
|
NetworkPacket();
|
||||||
//~NetworkPacket();
|
NetworkPacket(const NetworkPacket& packet); // copy constructor
|
||||||
|
~NetworkPacket(); // destructor
|
||||||
sockaddr& getSenderAddress() { return _senderAddress; };
|
NetworkPacket& operator= (const NetworkPacket& other); // copy assignment
|
||||||
ssize_t getLength() const { return _packetLength; };
|
|
||||||
unsigned char* getData() { return &_packetData[0]; };
|
|
||||||
|
|
||||||
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]; };
|
const unsigned char* getData() const { return &_packetData[0]; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
sockaddr _senderAddress;
|
void copyContents(const sockaddr& address, const unsigned char* packetData, ssize_t packetLength);
|
||||||
ssize_t _packetLength;
|
|
||||||
unsigned char _packetData[MAX_PACKET_SIZE];
|
sockaddr _address;
|
||||||
|
ssize_t _packetLength;
|
||||||
|
unsigned char _packetData[MAX_PACKET_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__shared_NetworkPacket__) */
|
#endif /* defined(__shared_NetworkPacket__) */
|
||||||
|
|
|
@ -439,7 +439,7 @@ void NodeList::addNodeToList(Node* newNode) {
|
||||||
notifyHooksOfAddedNode(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;
|
unsigned n = 0;
|
||||||
for(NodeList::iterator node = begin(); node != end(); node++) {
|
for(NodeList::iterator node = begin(); node != end(); node++) {
|
||||||
// only send to the NodeTypes we are asked to send to.
|
// 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
|
// they all match, so we are an ancestor
|
||||||
return true;
|
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__
|
#define __hifi__OctalCode__
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
const int BITS_IN_BYTE = 8;
|
const int BITS_IN_BYTE = 8;
|
||||||
const int BITS_IN_OCTAL = 3;
|
const int BITS_IN_OCTAL = 3;
|
||||||
|
@ -39,7 +40,7 @@ void copyFirstVertexForCode(unsigned char * octalCode, float* output);
|
||||||
struct VoxelPositionSize {
|
struct VoxelPositionSize {
|
||||||
float x, y, z, s;
|
float x, y, z, s;
|
||||||
};
|
};
|
||||||
void voxelDetailsForCode(unsigned char * octalCode, VoxelPositionSize& voxelPositionSize);
|
void voxelDetailsForCode(unsigned char* octalCode, VoxelPositionSize& voxelPositionSize);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ILLEGAL_CODE = -2,
|
ILLEGAL_CODE = -2,
|
||||||
|
@ -49,4 +50,8 @@ typedef enum {
|
||||||
} OctalCodeComparison;
|
} OctalCodeComparison;
|
||||||
|
|
||||||
OctalCodeComparison compareOctalCodes(unsigned char* code1, unsigned char* code2);
|
OctalCodeComparison compareOctalCodes(unsigned char* code1, unsigned char* code2);
|
||||||
|
|
||||||
|
QString octalCodeToHexString(unsigned char* octalCode);
|
||||||
|
unsigned char* hexStringToOctalCode(const QString& input);
|
||||||
|
|
||||||
#endif /* defined(__hifi__OctalCode__) */
|
#endif /* defined(__hifi__OctalCode__) */
|
||||||
|
|
|
@ -26,7 +26,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
case PACKET_TYPE_VOXEL_STATS:
|
case PACKET_TYPE_VOXEL_STATS:
|
||||||
return 1;
|
return 2;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#define hifi_PacketHeaders_h
|
#define hifi_PacketHeaders_h
|
||||||
|
|
||||||
typedef char PACKET_TYPE;
|
typedef char PACKET_TYPE;
|
||||||
|
const PACKET_TYPE PACKET_TYPE_UNKNOWN = 0;
|
||||||
const PACKET_TYPE PACKET_TYPE_DOMAIN = 'D';
|
const PACKET_TYPE PACKET_TYPE_DOMAIN = 'D';
|
||||||
const PACKET_TYPE PACKET_TYPE_PING = 'P';
|
const PACKET_TYPE PACKET_TYPE_PING = 'P';
|
||||||
const PACKET_TYPE PACKET_TYPE_PING_REPLY = 'R';
|
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 "JurisdictionMap.h"
|
||||||
#include "VoxelNode.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() {
|
JurisdictionMap::~JurisdictionMap() {
|
||||||
clear();
|
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 {
|
JurisdictionMap::Area JurisdictionMap::isMyJurisdiction(unsigned char* nodeOctalCode, int childIndex) const {
|
||||||
// to be in our jurisdiction, we must be under the root...
|
// 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 the node is an ancestor of my root, then we return ABOVE
|
||||||
if (isAncestorOf(nodeOctalCode, _rootOctalCode)) {
|
if (isAncestorOf(nodeOctalCode, _rootOctalCode)) {
|
||||||
return ABOVE;
|
return ABOVE;
|
||||||
|
@ -82,12 +139,6 @@ JurisdictionMap::Area JurisdictionMap::isMyJurisdiction(unsigned char* nodeOctal
|
||||||
|
|
||||||
// otherwise...
|
// otherwise...
|
||||||
bool isInJurisdiction = isAncestorOf(_rootOctalCode, nodeOctalCode, childIndex);
|
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 we're under the root, then we can't be under any of the endpoints
|
||||||
if (isInJurisdiction) {
|
if (isInJurisdiction) {
|
||||||
for (int i = 0; i < _endNodes.size(); i++) {
|
for (int i = 0; i < _endNodes.size(); i++) {
|
||||||
|
@ -98,7 +149,6 @@ JurisdictionMap::Area JurisdictionMap::isMyJurisdiction(unsigned char* nodeOctal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return isInJurisdiction ? WITHIN : BELOW;
|
return isInJurisdiction ? WITHIN : BELOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,48 +197,3 @@ bool JurisdictionMap::writeToFile(const char* filename) {
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
return true;
|
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
|
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(const char* filename);
|
||||||
JurisdictionMap(unsigned char* rootOctalCode, const std::vector<unsigned char*>& endNodes);
|
JurisdictionMap(unsigned char* rootOctalCode, const std::vector<unsigned char*>& endNodes);
|
||||||
JurisdictionMap(const char* rootHextString, const char* endNodesHextString);
|
JurisdictionMap(const char* rootHextString, const char* endNodesHextString);
|
||||||
|
@ -34,14 +47,14 @@ public:
|
||||||
unsigned char* getRootOctalCode() const { return _rootOctalCode; }
|
unsigned char* getRootOctalCode() const { return _rootOctalCode; }
|
||||||
unsigned char* getEndNodeOctalCode(int index) const { return _endNodes[index]; }
|
unsigned char* getEndNodeOctalCode(int index) const { return _endNodes[index]; }
|
||||||
int getEndNodeCount() const { return _endNodes.size(); }
|
int getEndNodeCount() const { return _endNodes.size(); }
|
||||||
|
|
||||||
|
void copyContents(unsigned char* rootCodeIn, const std::vector<unsigned char*>& endNodesIn);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void copyContents(const JurisdictionMap& other); // use assignment instead
|
||||||
void clear();
|
void clear();
|
||||||
void init(unsigned char* rootOctalCode, const std::vector<unsigned char*>& endNodes);
|
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;
|
unsigned char* _rootOctalCode;
|
||||||
std::vector<unsigned char*> _endNodes;
|
std::vector<unsigned char*> _endNodes;
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,21 +17,18 @@
|
||||||
const int samples = 100;
|
const int samples = 100;
|
||||||
VoxelSceneStats::VoxelSceneStats() :
|
VoxelSceneStats::VoxelSceneStats() :
|
||||||
_elapsedAverage(samples),
|
_elapsedAverage(samples),
|
||||||
_bitsPerVoxelAverage(samples)
|
_bitsPerVoxelAverage(samples),
|
||||||
|
_jurisdictionRoot(NULL)
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
_isReadyToSend = false;
|
_isReadyToSend = false;
|
||||||
_isStarted = false;
|
_isStarted = false;
|
||||||
_jurisdictionRoot = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VoxelSceneStats::~VoxelSceneStats() {
|
VoxelSceneStats::~VoxelSceneStats() {
|
||||||
if (_jurisdictionRoot) {
|
reset();
|
||||||
delete[] _jurisdictionRoot;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* root, JurisdictionMap* jurisdictionMap) {
|
void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* root, JurisdictionMap* jurisdictionMap) {
|
||||||
reset(); // resets packet and voxel stats
|
reset(); // resets packet and voxel stats
|
||||||
_isStarted = true;
|
_isStarted = true;
|
||||||
|
@ -54,6 +51,16 @@ void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* r
|
||||||
_jurisdictionRoot = new unsigned char[bytes];
|
_jurisdictionRoot = new unsigned char[bytes];
|
||||||
memcpy(_jurisdictionRoot, jurisdictionRoot, 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;
|
_existsBitsWritten = 0;
|
||||||
_existsInPacketBitsWritten = 0;
|
_existsInPacketBitsWritten = 0;
|
||||||
_treesRemoved = 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) {
|
void VoxelSceneStats::packetSent(int bytes) {
|
||||||
|
@ -303,6 +321,21 @@ int VoxelSceneStats::packIntoMessage(unsigned char* destinationBuffer, int avail
|
||||||
destinationBuffer += sizeof(bytes);
|
destinationBuffer += sizeof(bytes);
|
||||||
memcpy(destinationBuffer, _jurisdictionRoot, bytes);
|
memcpy(destinationBuffer, _jurisdictionRoot, bytes);
|
||||||
destinationBuffer += 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 {
|
} else {
|
||||||
int bytes = 0;
|
int bytes = 0;
|
||||||
memcpy(destinationBuffer, &bytes, sizeof(bytes));
|
memcpy(destinationBuffer, &bytes, sizeof(bytes));
|
||||||
|
@ -406,10 +439,25 @@ int VoxelSceneStats::unpackFromMessage(unsigned char* sourceBuffer, int availabl
|
||||||
|
|
||||||
if (bytes == 0) {
|
if (bytes == 0) {
|
||||||
_jurisdictionRoot = NULL;
|
_jurisdictionRoot = NULL;
|
||||||
|
_jurisdictionEndNodes.clear();
|
||||||
} else {
|
} else {
|
||||||
_jurisdictionRoot = new unsigned char[bytes];
|
_jurisdictionRoot = new unsigned char[bytes];
|
||||||
memcpy(_jurisdictionRoot, sourceBuffer, bytes);
|
memcpy(_jurisdictionRoot, sourceBuffer, bytes);
|
||||||
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
|
// running averages
|
||||||
|
|
|
@ -81,7 +81,7 @@ public:
|
||||||
char* getItemValue(int item);
|
char* getItemValue(int item);
|
||||||
|
|
||||||
unsigned char* getJurisdictionRoot() const { return _jurisdictionRoot; }
|
unsigned char* getJurisdictionRoot() const { return _jurisdictionRoot; }
|
||||||
|
const std::vector<unsigned char*>& getJurisdictionEndNodes() const { return _jurisdictionEndNodes; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _isReadyToSend;
|
bool _isReadyToSend;
|
||||||
|
@ -172,6 +172,7 @@ private:
|
||||||
char _itemValueBuffer[MAX_ITEM_VALUE_LENGTH];
|
char _itemValueBuffer[MAX_ITEM_VALUE_LENGTH];
|
||||||
|
|
||||||
unsigned char* _jurisdictionRoot;
|
unsigned char* _jurisdictionRoot;
|
||||||
|
std::vector<unsigned char*> _jurisdictionEndNodes;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__VoxelSceneStats__) */
|
#endif /* defined(__hifi__VoxelSceneStats__) */
|
||||||
|
|
Loading…
Reference in a new issue