Merge remote-tracking branch 'upstream/master' into menu

This commit is contained in:
Stephen Birarda 2013-08-15 12:22:37 -07:00
commit 443203201e
23 changed files with 841 additions and 258 deletions

View file

@ -114,8 +114,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),
@ -125,7 +123,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),
@ -278,9 +277,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
@ -319,7 +319,7 @@ void Application::paintGL() {
PerfStat("display");
glEnable(GL_LINE_SMOOTH);
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
_myCamera.setTightness (100.0f);
_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)
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) {
@ -1056,10 +1049,8 @@ void Application::terminate() {
pthread_join(_networkReceiveThread, NULL);
}
if (_enableProcessVoxelsThread) {
_stopProcessVoxelsThread = true;
pthread_join(_processVoxelsThread, NULL);
}
_voxelProcessor.terminate();
_voxelEditSender.terminate();
}
static Avatar* processAvatarMessageHeader(unsigned char*& packetData, size_t& dataBytes) {
@ -1251,12 +1242,7 @@ void Application::resetSwatchColors() {
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) {
@ -1287,38 +1273,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
}
@ -1500,15 +1459,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.
@ -1522,31 +1472,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;
}
@ -1580,15 +1507,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
@ -1600,14 +1518,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;
@ -1654,7 +1565,7 @@ void Application::init() {
_voxels.init();
_environment.init();
_glowEffect.init();
_handControl.setScreenDimensions(_glWidget->width(), _glWidget->height());
@ -2025,7 +1936,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...
@ -2725,15 +2637,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());
@ -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() {
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(11025);
@ -3329,7 +3212,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) {
@ -3433,15 +3316,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);
@ -3462,23 +3346,28 @@ 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;
}
@ -3563,12 +3452,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++;
@ -3592,7 +3475,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:

View file

@ -39,6 +39,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"
@ -74,6 +76,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()); }
@ -220,8 +225,6 @@ private:
void checkBandwidthMeterClick();
void setupPaintingVoxel();
void shiftPaintingColor();
bool maybeEditVoxelUnderCursor();
void deleteVoxelUnderCursor();
void eyedropperVoxelUnderCursor();
@ -325,10 +328,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
@ -359,10 +358,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;
@ -381,7 +378,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;
};

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

View 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__

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

View 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__

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

View 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__

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -26,7 +26,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
return 1;
case PACKET_TYPE_VOXEL_STATS:
return 1;
return 2;
default:
return 0;
}

View file

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

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

View 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__

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

View 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__

View file

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

View file

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

View file

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

View file

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