diff --git a/README.md b/README.md index bc32998113..a42bbd1626 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,15 @@ We have successfully built on OS X 10.8, Ubuntu and a few other modern Linux distributions. A Windows build is planned for the future, but not currently in development. -On a fresh Ubuntu 13.10 install, these are all the packages you need to grab and build the hifi project: -
sudo apt-get install build-essential cmake git libcurl4-openssl-dev libqt5scripttools5 libqt5svg5-dev libqt5webkit5-dev libqt5location5 qtlocation5-dev qtdeclarative5-dev qtscript5-dev qtsensors5-dev qtmultimedia5-dev qtquick1-5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-dev
+On a fresh Ubuntu 13.10 install, get these requirements from Ubuntu repositories: + + sudo apt-get install build-essential cmake git libcurl4-openssl-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-dev + +Then [download lastest Qt packages](http://qt-project.org/downloads), untar/install to your prefered path +and set your `QT_CMAKE_PREFIX_PATH` environment variable as described above in the CMake section. It's +recommended to set the variable automatically on each shell instance to save this task in the future: + + echo 'export QT_CMAKE_PREFIX_PATH=~/Qt5.2.0/5.2.0/gcc_64/lib/cmake' >> ~/.bashrc Running Interface ----- diff --git a/animation-server/src/AnimationServer.cpp b/animation-server/src/AnimationServer.cpp index 6d493b62e5..24ea7ede9c 100644 --- a/animation-server/src/AnimationServer.cpp +++ b/animation-server/src/AnimationServer.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include #include @@ -152,7 +151,7 @@ static void renderMovingBug() { } // send the "erase message" first... - PACKET_TYPE message = PACKET_TYPE_VOXEL_ERASE; + PacketType message = PacketTypeVoxelErase; ::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_BUG, (VoxelDetail*)&details); // Move the bug... @@ -212,7 +211,7 @@ static void renderMovingBug() { } // send the "create message" ... - message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; + message = PacketTypeVoxelSetDestructive; ::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_BUG, (VoxelDetail*)&details); } @@ -247,7 +246,7 @@ static void sendVoxelBlinkMessage() { detail.green = 0 * ::intensity; detail.blue = 0 * ::intensity; - PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; + PacketType message = PacketTypeVoxelSetDestructive; ::voxelEditPacketSender->sendVoxelEditMessage(message, detail); } @@ -264,7 +263,7 @@ unsigned char onColor[3] = { 0, 255, 255 }; const float STRING_OF_LIGHTS_SIZE = 0.125f / TREE_SCALE; // approximately 1/8th meter static void sendBlinkingStringOfLights() { - PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully! + PacketType message = PacketTypeVoxelSetDestructive; // we're a bully! float lightScale = STRING_OF_LIGHTS_SIZE; static VoxelDetail details[LIGHTS_PER_SEGMENT]; @@ -370,7 +369,7 @@ const int PACKETS_PER_DANCE_FLOOR = DANCE_FLOOR_VOXELS_PER_PACKET / (DANCE_FLOOR int danceFloorColors[DANCE_FLOOR_WIDTH][DANCE_FLOOR_LENGTH]; void sendDanceFloor() { - PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully! + PacketType message = PacketTypeVoxelSetDestructive; // we're a bully! float lightScale = DANCE_FLOOR_LIGHT_SIZE; static VoxelDetail details[DANCE_FLOOR_VOXELS_PER_PACKET]; @@ -486,7 +485,7 @@ bool billboardMessage[BILLBOARD_HEIGHT][BILLBOARD_WIDTH] = { }; static void sendBillboard() { - PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully! + PacketType message = PacketTypeVoxelSetDestructive; // we're a bully! float lightScale = BILLBOARD_LIGHT_SIZE; static VoxelDetail details[VOXELS_PER_PACKET]; @@ -557,7 +556,7 @@ void doBuildStreet() { return; } - PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully! + PacketType message = PacketTypeVoxelSetDestructive; // we're a bully! static VoxelDetail details[BRICKS_PER_PACKET]; for (int z = 0; z < ROAD_LENGTH; z++) { @@ -592,8 +591,8 @@ double start = 0; void* animateVoxels(void* args) { - uint64_t lastAnimateTime = 0; - uint64_t lastProcessTime = 0; + quint64 lastAnimateTime = 0; + quint64 lastProcessTime = 0; int processesPerAnimate = 0; bool firstTime = true; @@ -623,8 +622,8 @@ void* animateVoxels(void* args) { // The while loop will be running at PROCESSING_FPS, but we only want to call these animation functions at // ANIMATE_FPS. So we check out last animate time and only call these if we've elapsed that time. - uint64_t now = usecTimestampNow(); - uint64_t animationElapsed = now - lastAnimateTime; + quint64 now = usecTimestampNow(); + quint64 animationElapsed = now - lastAnimateTime; int withinAnimationTarget = ANIMATE_VOXELS_INTERVAL_USECS - animationElapsed; const int CLOSE_ENOUGH_TO_ANIMATE = 2000; // approximately 2 ms @@ -677,7 +676,7 @@ void* animateVoxels(void* args) { processesPerAnimate++; } // dynamically sleep until we need to fire off the next set of voxels - uint64_t usecToSleep = PROCESSING_INTERVAL_USECS - (usecTimestampNow() - lastProcessTime); + quint64 usecToSleep = PROCESSING_INTERVAL_USECS - (usecTimestampNow() - lastProcessTime); if (usecToSleep > PROCESSING_INTERVAL_USECS) { usecToSleep = PROCESSING_INTERVAL_USECS; } @@ -695,7 +694,7 @@ AnimationServer::AnimationServer(int &argc, char **argv) : { ::start = usecTimestampNow(); - NodeList* nodeList = NodeList::createInstance(NODE_TYPE_ANIMATION_SERVER, ANIMATION_LISTEN_PORT); + NodeList* nodeList = NodeList::createInstance(NodeType::AnimationServer, ANIMATION_LISTEN_PORT); setvbuf(stdout, NULL, _IOLBF, 0); // Handle Local Domain testing with the --local command line @@ -807,7 +806,7 @@ AnimationServer::AnimationServer(int &argc, char **argv) : pthread_create(&::animateVoxelThread, NULL, animateVoxels, NULL); - NodeList::getInstance()->addNodeTypeToInterestSet(NODE_TYPE_VOXEL_SERVER); + NodeList::getInstance()->addNodeTypeToInterestSet(NodeType::VoxelServer); QTimer* domainServerTimer = new QTimer(this); connect(domainServerTimer, SIGNAL(timeout()), nodeList, SLOT(sendDomainServerCheckIn())); @@ -823,25 +822,24 @@ AnimationServer::AnimationServer(int &argc, char **argv) : void AnimationServer::readPendingDatagrams() { NodeList* nodeList = NodeList::getInstance(); - static int receivedBytes = 0; - static unsigned char packetData[MAX_PACKET_SIZE]; + static QByteArray receivedPacket; static HifiSockAddr nodeSockAddr; // Nodes sending messages to us... - while (nodeList->getNodeSocket().hasPendingDatagrams() - && (receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE, - nodeSockAddr.getAddressPointer(), - nodeSockAddr.getPortPointer())) && - packetVersionMatch(packetData, nodeSockAddr)) { - - if (packetData[0] == PACKET_TYPE_JURISDICTION) { - int headerBytes = numBytesForPacketHeader(packetData); - // PACKET_TYPE_JURISDICTION, first byte is the node type... - if (packetData[headerBytes] == NODE_TYPE_VOXEL_SERVER && ::jurisdictionListener) { - ::jurisdictionListener->queueReceivedPacket(nodeSockAddr, packetData, receivedBytes); + while (nodeList->getNodeSocket().hasPendingDatagrams()) { + receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); + nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(), + nodeSockAddr.getAddressPointer(), nodeSockAddr.getPortPointer()); + if (packetVersionMatch(receivedPacket)) { + if (packetTypeForPacket(receivedPacket) == PacketTypeJurisdiction) { + int headerBytes = numBytesForPacketHeader(receivedPacket); + // PacketType_JURISDICTION, first byte is the node type... + if (receivedPacket.data()[headerBytes] == NodeType::VoxelServer && ::jurisdictionListener) { + ::jurisdictionListener->queueReceivedPacket(nodeSockAddr, receivedPacket); + } } + NodeList::getInstance()->processNodeData(nodeSockAddr, receivedPacket); } - NodeList::getInstance()->processNodeData(nodeSockAddr, packetData, receivedBytes); } } diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 4f81c42046..2694bf83e2 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -22,37 +22,42 @@ #include "Agent.h" -Agent::Agent(const unsigned char* dataBuffer, int numBytes) : - ThreadedAssignment(dataBuffer, numBytes) +Agent::Agent(const QByteArray& packet) : + ThreadedAssignment(packet) { } void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { - if (dataByteArray[0] == PACKET_TYPE_JURISDICTION) { - int headerBytes = numBytesForPacketHeader((const unsigned char*) dataByteArray.constData()); - // PACKET_TYPE_JURISDICTION, first byte is the node type... + PacketType datagramPacketType = packetTypeForPacket(dataByteArray); + if (datagramPacketType == PacketTypeJurisdiction) { + int headerBytes = numBytesForPacketHeader(dataByteArray); + // PacketType_JURISDICTION, first byte is the node type... switch (dataByteArray[headerBytes]) { - case NODE_TYPE_VOXEL_SERVER: + case NodeType::VoxelServer: _scriptEngine.getVoxelsScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr, - (unsigned char*) dataByteArray.data(), - dataByteArray.size()); + dataByteArray); break; - case NODE_TYPE_PARTICLE_SERVER: + case NodeType::ParticleServer: _scriptEngine.getParticlesScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr, - (unsigned char*) dataByteArray.data(), - dataByteArray.size()); + dataByteArray); break; } + } else if (datagramPacketType == PacketTypeParticleAddResponse) { + // this will keep creatorTokenIDs to IDs mapped correctly + Particle::handleAddParticleResponse(dataByteArray); + + // also give our local particle tree a chance to remap any internal locally created particles + _particleTree.handleAddParticleResponse(dataByteArray); } else { - NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); + NodeList::getInstance()->processNodeData(senderSockAddr, dataByteArray); } } void Agent::run() { NodeList* nodeList = NodeList::getInstance(); - nodeList->setOwnerType(NODE_TYPE_AGENT); + nodeList->setOwnerType(NodeType::Agent); - nodeList->addSetOfNodeTypesToNodeInterestSet(QSet() << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER); + nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer); // figure out the URL for the script for this agent assignment QString scriptURLString("http://%1:8080/assignment/%2"); @@ -88,11 +93,17 @@ void Agent::run() { connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); + // tell our script engine about our local particle tree + _scriptEngine.getParticlesScriptingInterface()->setParticleTree(&_particleTree); + // setup an Avatar for the script to use AvatarData scriptedAvatar; // give this AvatarData object to the script engine _scriptEngine.setAvatarData(&scriptedAvatar, "Avatar"); + + // register ourselves to the script engine + _scriptEngine.registerGlobalObject("Agent", this); _scriptEngine.setScriptContents(scriptContents); _scriptEngine.run(); diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 980885fcf3..8b2038a8b0 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -15,13 +15,19 @@ #include #include +#include #include #include class Agent : public ThreadedAssignment { Q_OBJECT + + Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar) public: - Agent(const unsigned char* dataBuffer, int numBytes); + Agent(const QByteArray& packet); + + void setIsAvatar(bool isAvatar) { _scriptEngine.setIsAvatar(isAvatar); } + bool isAvatar() const { return _scriptEngine.isAvatar(); } public slots: void run(); @@ -32,6 +38,7 @@ signals: void willSendVisualDataCallback(); private: ScriptEngine _scriptEngine; + ParticleTree _particleTree; }; #endif /* defined(__hifi__Agent__) */ diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index fc8d2deffd..b2a5555e36 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -52,7 +52,7 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, requestAssignmentPool); // create a NodeList as an unassigned client - NodeList* nodeList = NodeList::createInstance(NODE_TYPE_UNASSIGNED); + NodeList* nodeList = NodeList::createInstance(NodeType::Unassigned); const char CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION[] = "-a"; const char CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION[] = "-p"; @@ -90,7 +90,8 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : timer->start(ASSIGNMENT_REQUEST_INTERVAL_MSECS); // connect our readPendingDatagrams method to the readyRead() signal of the socket - connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); + connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams, + Qt::QueuedConnection); } void AssignmentClient::sendAssignmentRequest() { @@ -102,34 +103,33 @@ void AssignmentClient::sendAssignmentRequest() { void AssignmentClient::readPendingDatagrams() { NodeList* nodeList = NodeList::getInstance(); - static unsigned char packetData[1500]; - static qint64 receivedBytes = 0; - static HifiSockAddr senderSockAddr; + QByteArray receivedPacket; + HifiSockAddr senderSockAddr; while (nodeList->getNodeSocket().hasPendingDatagrams()) { + receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); + nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(), + senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); - if ((receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE, - senderSockAddr.getAddressPointer(), - senderSockAddr.getPortPointer())) - && packetVersionMatch(packetData, senderSockAddr)) { - + if (packetVersionMatch(receivedPacket)) { if (_currentAssignment) { // have the threaded current assignment handle this datagram QMetaObject::invokeMethod(_currentAssignment, "processDatagram", Qt::QueuedConnection, - Q_ARG(QByteArray, QByteArray((char*) packetData, receivedBytes)), + Q_ARG(QByteArray, receivedPacket), Q_ARG(HifiSockAddr, senderSockAddr)); - } else if (packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT || packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) { + } else if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) { if (_currentAssignment) { qDebug() << "Dropping received assignment since we are currently running one."; } else { // construct the deployed assignment from the packet data - _currentAssignment = AssignmentFactory::unpackAssignment(packetData, receivedBytes); + _currentAssignment = AssignmentFactory::unpackAssignment(receivedPacket); - qDebug() << "Received an assignment -" << *_currentAssignment; - - // switch our nodelist domain IP and port to whoever sent us the assignment - if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) { + if (_currentAssignment) { + qDebug() << "Received an assignment -" << *_currentAssignment; + + // switch our nodelist domain IP and port to whoever sent us the assignment + nodeList->setDomainSockAddr(senderSockAddr); nodeList->setOwnerUUID(_currentAssignment->getUUID()); @@ -153,12 +153,12 @@ void AssignmentClient::readPendingDatagrams() { // Starts an event loop, and emits workerThread->started() workerThread->start(); } else { - qDebug("Received a bad destination socket for assignment."); + qDebug() << "Received an assignment that could not be unpacked. Re-requesting."; } } } else { // have the NodeList attempt to handle it - nodeList->processNodeData(senderSockAddr, packetData, receivedBytes); + nodeList->processNodeData(senderSockAddr, receivedPacket); } } } @@ -175,6 +175,6 @@ void AssignmentClient::assignmentCompleted() { NodeList* nodeList = NodeList::getInstance(); // reset our NodeList by switching back to unassigned and clearing the list - nodeList->setOwnerType(NODE_TYPE_UNASSIGNED); + nodeList->setOwnerType(NodeType::Unassigned); nodeList->reset(); } diff --git a/assignment-client/src/AssignmentFactory.cpp b/assignment-client/src/AssignmentFactory.cpp index b32a909bfe..5bf0417f22 100644 --- a/assignment-client/src/AssignmentFactory.cpp +++ b/assignment-client/src/AssignmentFactory.cpp @@ -18,25 +18,28 @@ #include "avatars/AvatarMixer.h" #include "metavoxels/MetavoxelServer.h" -ThreadedAssignment* AssignmentFactory::unpackAssignment(const unsigned char* dataBuffer, int numBytes) { - int headerBytes = numBytesForPacketHeader(dataBuffer); +ThreadedAssignment* AssignmentFactory::unpackAssignment(const QByteArray& packet) { + QDataStream packetStream(packet); + packetStream.skipRawData(numBytesForPacketHeader(packet)); + + quint8 packedType; + packetStream >> packedType; - Assignment::Type assignmentType = Assignment::AllTypes; - memcpy(&assignmentType, dataBuffer + headerBytes, sizeof(Assignment::Type)); + Assignment::Type unpackedType = (Assignment::Type) packedType; - switch (assignmentType) { + switch (unpackedType) { case Assignment::AudioMixerType: - return new AudioMixer(dataBuffer, numBytes); + return new AudioMixer(packet); case Assignment::AvatarMixerType: - return new AvatarMixer(dataBuffer, numBytes); + return new AvatarMixer(packet); case Assignment::AgentType: - return new Agent(dataBuffer, numBytes); + return new Agent(packet); case Assignment::VoxelServerType: - return new VoxelServer(dataBuffer, numBytes); + return new VoxelServer(packet); case Assignment::ParticleServerType: - return new ParticleServer(dataBuffer, numBytes); + return new ParticleServer(packet); case Assignment::MetavoxelServerType: - return new MetavoxelServer(dataBuffer, numBytes); + return new MetavoxelServer(packet); default: return NULL; } diff --git a/assignment-client/src/AssignmentFactory.h b/assignment-client/src/AssignmentFactory.h index 4605d961ec..9eff29a468 100644 --- a/assignment-client/src/AssignmentFactory.h +++ b/assignment-client/src/AssignmentFactory.h @@ -13,7 +13,7 @@ class AssignmentFactory { public: - static ThreadedAssignment* unpackAssignment(const unsigned char* dataBuffer, int numBytes); + static ThreadedAssignment* unpackAssignment(const QByteArray& packet); }; #endif /* defined(__hifi__AssignmentFactory__) */ diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 705a877a00..90e2810f27 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -61,8 +60,8 @@ void attachNewBufferToNode(Node *newNode) { } } -AudioMixer::AudioMixer(const unsigned char* dataBuffer, int numBytes) : - ThreadedAssignment(dataBuffer, numBytes) +AudioMixer::AudioMixer(const QByteArray& packet) : + ThreadedAssignment(packet) { } @@ -210,18 +209,19 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { void AudioMixer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { // pull any new audio data from nodes off of the network stack - if (dataByteArray[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO - || dataByteArray[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO - || dataByteArray[0] == PACKET_TYPE_INJECT_AUDIO) { - QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()), - NUM_BYTES_RFC4122_UUID)); + PacketType mixerPacketType = packetTypeForPacket(dataByteArray); + if (mixerPacketType == PacketTypeMicrophoneAudioNoEcho + || mixerPacketType == PacketTypeMicrophoneAudioWithEcho + || mixerPacketType == PacketTypeInjectAudio) { + QUuid nodeUUID; + deconstructPacketHeader(dataByteArray, nodeUUID); NodeList* nodeList = NodeList::getInstance(); SharedNodePointer matchingNode = nodeList->nodeWithUUID(nodeUUID); if (matchingNode) { - nodeList->updateNodeWithData(matchingNode.data(), senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); + nodeList->updateNodeWithData(matchingNode.data(), senderSockAddr, dataByteArray); if (!matchingNode->getActiveSocket()) { // we don't have an active socket for this node, but they're talking to us @@ -231,17 +231,17 @@ void AudioMixer::processDatagram(const QByteArray& dataByteArray, const HifiSock } } else { // let processNodeData handle it. - NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); + NodeList::getInstance()->processNodeData(senderSockAddr, dataByteArray); } } void AudioMixer::run() { - commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NODE_TYPE_AUDIO_MIXER); + commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NodeType::AudioMixer); NodeList* nodeList = NodeList::getInstance(); - nodeList->addNodeTypeToInterestSet(NODE_TYPE_AGENT); + nodeList->addNodeTypeToInterestSet(NodeType::Agent); nodeList->linkedDataCreateCallback = attachNewBufferToNode; @@ -250,14 +250,14 @@ void AudioMixer::run() { gettimeofday(&startTime, NULL); - int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_MIXED_AUDIO); + int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(PacketTypeMixedAudio); // note: Visual Studio 2010 doesn't support variable sized local arrays #ifdef _WIN32 unsigned char clientPacket[MAX_PACKET_SIZE]; #else unsigned char clientPacket[NETWORK_BUFFER_LENGTH_BYTES_STEREO + numBytesPacketHeader]; #endif - populateTypeAndVersion(clientPacket, PACKET_TYPE_MIXED_AUDIO); + populatePacketHeader(reinterpret_cast(clientPacket), PacketTypeMixedAudio); while (!_isFinished) { @@ -274,7 +274,7 @@ void AudioMixer::run() { } foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { - if (node->getType() == NODE_TYPE_AGENT && node->getActiveSocket() && node->getLinkedData() + if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() && ((AudioMixerClientData*) node->getLinkedData())->getAvatarAudioRingBuffer()) { prepareMixForListeningNode(node.data()); diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 7326e1a161..2d42a6b629 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -20,7 +20,7 @@ class AvatarAudioRingBuffer; class AudioMixer : public ThreadedAssignment { Q_OBJECT public: - AudioMixer(const unsigned char* dataBuffer, int numBytes); + AudioMixer(const QByteArray& packet); public slots: /// threaded run of assignment void run(); diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 0838de4f45..a41889e77c 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -31,9 +31,10 @@ AvatarAudioRingBuffer* AudioMixerClientData::getAvatarAudioRingBuffer() const { return NULL; } -int AudioMixerClientData::parseData(unsigned char* packetData, int numBytes) { - if (packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO - || packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO) { +int AudioMixerClientData::parseData(const QByteArray& packet) { + PacketType packetType = packetTypeForPacket(packet); + if (packetType == PacketTypeMicrophoneAudioWithEcho + || packetType == PacketTypeMicrophoneAudioNoEcho) { // grab the AvatarAudioRingBuffer from the vector (or create it if it doesn't exist) AvatarAudioRingBuffer* avatarRingBuffer = getAvatarAudioRingBuffer(); @@ -45,14 +46,12 @@ int AudioMixerClientData::parseData(unsigned char* packetData, int numBytes) { } // ask the AvatarAudioRingBuffer instance to parse the data - avatarRingBuffer->parseData(packetData, numBytes); + avatarRingBuffer->parseData(packet); } else { // this is injected audio // grab the stream identifier for this injected audio - QByteArray rfcUUID = QByteArray((char*) packetData + numBytesForPacketHeader(packetData) + NUM_BYTES_RFC4122_UUID, - NUM_BYTES_RFC4122_UUID); - QUuid streamIdentifier = QUuid::fromRfc4122(rfcUUID); + QUuid streamIdentifier = QUuid::fromRfc4122(packet.mid(numBytesForPacketHeader(packet), NUM_BYTES_RFC4122_UUID)); InjectedAudioRingBuffer* matchingInjectedRingBuffer = NULL; @@ -69,7 +68,7 @@ int AudioMixerClientData::parseData(unsigned char* packetData, int numBytes) { _ringBuffers.push_back(matchingInjectedRingBuffer); } - matchingInjectedRingBuffer->parseData(packetData, numBytes); + matchingInjectedRingBuffer->parseData(packet); } return 0; diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 05cfa51e5f..8031dfec3e 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -23,7 +23,7 @@ public: const std::vector getRingBuffers() const { return _ringBuffers; } AvatarAudioRingBuffer* getAvatarAudioRingBuffer() const; - int parseData(unsigned char* packetData, int numBytes); + int parseData(const QByteArray& packet); void checkBuffersBeforeFrameSend(int jitterBufferLengthSamples); void pushBuffersAfterFrameSend(); private: diff --git a/assignment-client/src/audio/AvatarAudioRingBuffer.cpp b/assignment-client/src/audio/AvatarAudioRingBuffer.cpp index 64d71d9836..4b1907efb8 100644 --- a/assignment-client/src/audio/AvatarAudioRingBuffer.cpp +++ b/assignment-client/src/audio/AvatarAudioRingBuffer.cpp @@ -15,7 +15,7 @@ AvatarAudioRingBuffer::AvatarAudioRingBuffer() : } -int AvatarAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) { - _shouldLoopbackForNode = (sourceBuffer[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO); - return PositionalAudioRingBuffer::parseData(sourceBuffer, numBytes); +int AvatarAudioRingBuffer::parseData(const QByteArray& packet) { + _shouldLoopbackForNode = (packetTypeForPacket(packet) == PacketTypeMicrophoneAudioWithEcho); + return PositionalAudioRingBuffer::parseData(packet); } \ No newline at end of file diff --git a/assignment-client/src/audio/AvatarAudioRingBuffer.h b/assignment-client/src/audio/AvatarAudioRingBuffer.h index 15542383fb..0d4f28467b 100644 --- a/assignment-client/src/audio/AvatarAudioRingBuffer.h +++ b/assignment-client/src/audio/AvatarAudioRingBuffer.h @@ -17,7 +17,7 @@ class AvatarAudioRingBuffer : public PositionalAudioRingBuffer { public: AvatarAudioRingBuffer(); - int parseData(unsigned char* sourceBuffer, int numBytes); + int parseData(const QByteArray& packet); private: // disallow copying of AvatarAudioRingBuffer objects AvatarAudioRingBuffer(const AvatarAudioRingBuffer&); diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 64e93fd751..358e507fc4 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -27,24 +27,13 @@ const char AVATAR_MIXER_LOGGING_NAME[] = "avatar-mixer"; const unsigned int AVATAR_DATA_SEND_INTERVAL_USECS = (1 / 60.0) * 1000 * 1000; -AvatarMixer::AvatarMixer(const unsigned char* dataBuffer, int numBytes) : - ThreadedAssignment(dataBuffer, numBytes) +AvatarMixer::AvatarMixer(const QByteArray& packet) : + ThreadedAssignment(packet) { // make sure we hear about node kills so we can tell the other nodes connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled); } -unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) { - QByteArray rfcUUID = nodeToAdd->getUUID().toRfc4122(); - memcpy(currentPosition, rfcUUID.constData(), rfcUUID.size()); - currentPosition += rfcUUID.size(); - - AvatarData *nodeData = (AvatarData *)nodeToAdd->getLinkedData(); - currentPosition += nodeData->getBroadcastData(currentPosition); - - return currentPosition; -} - void attachAvatarDataToNode(Node* newNode) { if (newNode->getLinkedData() == NULL) { newNode->setLinkedData(new AvatarData()); @@ -59,58 +48,50 @@ void attachAvatarDataToNode(Node* newNode) { // determine which avatars are included in the packet stream // 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful). void broadcastAvatarData() { - static unsigned char broadcastPacket[MAX_PACKET_SIZE]; - static unsigned char avatarDataBuffer[MAX_PACKET_SIZE]; - int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_BULK_AVATAR_DATA); - unsigned char* currentBufferPosition = broadcastPacket + numHeaderBytes; - int packetLength = currentBufferPosition - broadcastPacket; + static QByteArray mixedAvatarByteArray; + + int numPacketHeaderBytes = populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData); + int packetsSent = 0; NodeList* nodeList = NodeList::getInstance(); foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { - if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT && node->getActiveSocket()) { + if (node->getLinkedData() && node->getType() == NodeType::Agent && node->getActiveSocket()) { // reset packet pointers for this node - currentBufferPosition = broadcastPacket + numHeaderBytes; - packetLength = currentBufferPosition - broadcastPacket; + mixedAvatarByteArray.resize(numPacketHeaderBytes); // this is an AGENT we have received head data from // send back a packet with other active node data to this node foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) { if (otherNode->getLinkedData() && otherNode->getUUID() != node->getUUID()) { - unsigned char* avatarDataEndpoint = addNodeToBroadcastPacket((unsigned char*)&avatarDataBuffer[0], - otherNode.data()); - int avatarDataLength = avatarDataEndpoint - (unsigned char*)&avatarDataBuffer; + QByteArray avatarByteArray; + avatarByteArray.append(otherNode->getUUID().toRfc4122()); - if (avatarDataLength + packetLength <= MAX_PACKET_SIZE) { - memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength); - packetLength += avatarDataLength; - currentBufferPosition += avatarDataLength; - } else { + AvatarData* nodeData = (AvatarData*) otherNode->getLinkedData(); + avatarByteArray.append(nodeData->toByteArray()); + + if (avatarByteArray.size() + mixedAvatarByteArray.size() > MAX_PACKET_SIZE) { packetsSent++; //printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength); - nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, - currentBufferPosition - broadcastPacket, + nodeList->getNodeSocket().writeDatagram(mixedAvatarByteArray, node->getActiveSocket()->getAddress(), node->getActiveSocket()->getPort()); // reset the packet - currentBufferPosition = broadcastPacket + numHeaderBytes; - packetLength = currentBufferPosition - broadcastPacket; - - // copy the avatar that didn't fit into the next packet - memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength); - packetLength += avatarDataLength; - currentBufferPosition += avatarDataLength; + mixedAvatarByteArray.resize(numPacketHeaderBytes); } + + // copy the avatar into the mixedAvatarByteArray packet + mixedAvatarByteArray.append(avatarByteArray); } } packetsSent++; //printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength); - nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, currentBufferPosition - broadcastPacket, + nodeList->getNodeSocket().writeDatagram(mixedAvatarByteArray, node->getActiveSocket()->getAddress(), node->getActiveSocket()->getPort()); } @@ -118,18 +99,15 @@ void broadcastAvatarData() { } void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { - if (killedNode->getType() == NODE_TYPE_AGENT + if (killedNode->getType() == NodeType::Agent && killedNode->getLinkedData()) { // this was an avatar we were sending to other people // send a kill packet for it to our other nodes - unsigned char packetData[MAX_PACKET_SIZE]; - int numHeaderBytes = populateTypeAndVersion(packetData, PACKET_TYPE_KILL_AVATAR); + QByteArray killPacket = byteArrayWithPopluatedHeader(PacketTypeKillAvatar); + killPacket += killedNode->getUUID().toRfc4122(); - QByteArray rfcUUID = killedNode->getUUID().toRfc4122(); - memcpy(packetData + numHeaderBytes, rfcUUID.constData(), rfcUUID.size()); - - NodeList::getInstance()->broadcastToNodes(packetData, numHeaderBytes + NUM_BYTES_RFC4122_UUID, - QSet() << NODE_TYPE_AGENT); + NodeList::getInstance()->broadcastToNodes(killPacket, + NodeSet() << NodeType::Agent); } } @@ -137,39 +115,38 @@ void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSoc NodeList* nodeList = NodeList::getInstance(); - switch (dataByteArray[0]) { - case PACKET_TYPE_HEAD_DATA: { - QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()), - NUM_BYTES_RFC4122_UUID)); + switch (packetTypeForPacket(dataByteArray)) { + case PacketTypeAvatarData: { + QUuid nodeUUID; + deconstructPacketHeader(dataByteArray, nodeUUID); // add or update the node in our list SharedNodePointer avatarNode = nodeList->nodeWithUUID(nodeUUID); if (avatarNode) { // parse positional data from an node - nodeList->updateNodeWithData(avatarNode.data(), senderSockAddr, - (unsigned char*) dataByteArray.data(), dataByteArray.size()); + nodeList->updateNodeWithData(avatarNode.data(), senderSockAddr, dataByteArray); } break; } - case PACKET_TYPE_KILL_AVATAR: { + case PacketTypeKillAvatar: { nodeList->processKillNode(dataByteArray); break; } default: // hand this off to the NodeList - nodeList->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); + nodeList->processNodeData(senderSockAddr, dataByteArray); break; } } void AvatarMixer::run() { - commonInit(AVATAR_MIXER_LOGGING_NAME, NODE_TYPE_AVATAR_MIXER); + commonInit(AVATAR_MIXER_LOGGING_NAME, NodeType::AvatarMixer); NodeList* nodeList = NodeList::getInstance(); - nodeList->addNodeTypeToInterestSet(NODE_TYPE_AGENT); + nodeList->addNodeTypeToInterestSet(NodeType::Agent); nodeList->linkedDataCreateCallback = attachAvatarDataToNode; diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 2da0ed98eb..92ab1a5c53 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -14,7 +14,7 @@ /// Handles assignments of type AvatarMixer - distribution of avatar data to various clients class AvatarMixer : public ThreadedAssignment { public: - AvatarMixer(const unsigned char* dataBuffer, int numBytes); + AvatarMixer(const QByteArray& packet); public slots: /// runs the avatar mixer diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index af982abf9b..3cd81d8ac7 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -17,22 +17,25 @@ const int SEND_INTERVAL = 50; -MetavoxelServer::MetavoxelServer(const unsigned char* dataBuffer, int numBytes) : - ThreadedAssignment(dataBuffer, numBytes), - _data(new MetavoxelData()) { +MetavoxelServer::MetavoxelServer(const QByteArray& packet) : + ThreadedAssignment(packet) { _sendTimer.setSingleShot(true); connect(&_sendTimer, SIGNAL(timeout()), SLOT(sendDeltas())); } +void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) { + edit.apply(_data); +} + void MetavoxelServer::removeSession(const QUuid& sessionId) { - delete _sessions.take(sessionId); + _sessions.take(sessionId)->deleteLater(); } const char METAVOXEL_SERVER_LOGGING_NAME[] = "metavoxel-server"; void MetavoxelServer::run() { - commonInit(METAVOXEL_SERVER_LOGGING_NAME, NODE_TYPE_METAVOXEL_SERVER); + commonInit(METAVOXEL_SERVER_LOGGING_NAME, NodeType::MetavoxelServer); _lastSend = QDateTime::currentMSecsSinceEpoch(); _sendTimer.start(SEND_INTERVAL); @@ -40,12 +43,12 @@ void MetavoxelServer::run() { void MetavoxelServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { switch (dataByteArray.at(0)) { - case PACKET_TYPE_METAVOXEL_DATA: + case PacketTypeMetavoxelData: processData(dataByteArray, senderSockAddr); break; default: - NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*)dataByteArray.data(), dataByteArray.size()); + NodeList::getInstance()->processNodeData(senderSockAddr, dataByteArray); break; } } @@ -75,16 +78,18 @@ void MetavoxelServer::processData(const QByteArray& data, const HifiSockAddr& se // forward to session, creating if necessary MetavoxelSession*& session = _sessions[sessionID]; if (!session) { - session = new MetavoxelSession(this, sessionID, QByteArray::fromRawData(data.constData(), headerPlusIDSize)); + session = new MetavoxelSession(this, sessionID, QByteArray::fromRawData(data.constData(), headerPlusIDSize), sender); } session->receivedData(data, sender); } -MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId, const QByteArray& datagramHeader) : +MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId, + const QByteArray& datagramHeader, const HifiSockAddr& sender) : QObject(server), _server(server), _sessionId(sessionId), - _sequencer(datagramHeader) { + _sequencer(datagramHeader), + _sender(sender) { const int TIMEOUT_INTERVAL = 30 * 1000; _timeoutTimer.setInterval(TIMEOUT_INTERVAL); @@ -94,10 +99,13 @@ MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const QUuid& session connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(clearSendRecordsBefore(int))); + connect(&_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)), SLOT(handleMessage(const QVariant&))); // insert the baseline send record - SendRecord record = { 0, MetavoxelDataPointer(new MetavoxelData()) }; + SendRecord record = { 0 }; _sendRecords.append(record); + + qDebug() << "Opened session [sessionId=" << _sessionId << ", sender=" << _sender << "]"; } void MetavoxelSession::receivedData(const QByteArray& data, const HifiSockAddr& sender) { @@ -114,7 +122,7 @@ void MetavoxelSession::receivedData(const QByteArray& data, const HifiSockAddr& void MetavoxelSession::sendDelta() { Bitstream& out = _sequencer.startPacket(); out << QVariant::fromValue(MetavoxelDeltaMessage()); - writeDelta(_server->getData(), _sendRecords.first().data, out); + _server->getData().writeDelta(_sendRecords.first().data, out); _sequencer.endPacket(); // record the send @@ -134,25 +142,29 @@ void MetavoxelSession::sendData(const QByteArray& data) { void MetavoxelSession::readPacket(Bitstream& in) { QVariant message; in >> message; - handleMessage(message, in); + handleMessage(message); } void MetavoxelSession::clearSendRecordsBefore(int index) { _sendRecords.erase(_sendRecords.begin(), _sendRecords.begin() + index + 1); } -void MetavoxelSession::handleMessage(const QVariant& message, Bitstream& in) { +void MetavoxelSession::handleMessage(const QVariant& message) { int userType = message.userType(); - if (userType == ClientStateMessage::Type) { + if (userType == CloseSessionMessage::Type) { + qDebug() << "Session closed [sessionId=" << _sessionId << ", sender=" << _sender << "]"; + _server->removeSession(_sessionId); + + } else if (userType == ClientStateMessage::Type) { ClientStateMessage state = message.value(); _position = state.position; - } else if (userType == MetavoxelDeltaMessage::Type) { + } else if (userType == MetavoxelEditMessage::Type) { + _server->applyEdit(message.value()); - } else if (userType == QMetaType::QVariantList) { foreach (const QVariant& element, message.toList()) { - handleMessage(element, in); + handleMessage(element); } } } diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index 407c520116..f106bd3494 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -20,6 +20,7 @@ #include #include +class MetavoxelEditMessage; class MetavoxelSession; /// Maintains a shared metavoxel system, accepting change requests and broadcasting updates. @@ -28,9 +29,11 @@ class MetavoxelServer : public ThreadedAssignment { public: - MetavoxelServer(const unsigned char* dataBuffer, int numBytes); + MetavoxelServer(const QByteArray& packet); - const MetavoxelDataPointer& getData() const { return _data; } + void applyEdit(const MetavoxelEditMessage& edit); + + const MetavoxelData& getData() const { return _data; } void removeSession(const QUuid& sessionId); @@ -51,7 +54,7 @@ private: QHash _sessions; - MetavoxelDataPointer _data; + MetavoxelData _data; }; /// Contains the state of a single client session. @@ -60,7 +63,8 @@ class MetavoxelSession : public QObject { public: - MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId, const QByteArray& datagramHeader); + MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId, + const QByteArray& datagramHeader, const HifiSockAddr& sender); void receivedData(const QByteArray& data, const HifiSockAddr& sender); @@ -76,14 +80,14 @@ private slots: void clearSendRecordsBefore(int index); -private: + void handleMessage(const QVariant& message); - void handleMessage(const QVariant& message, Bitstream& in); +private: class SendRecord { public: int packetNumber; - MetavoxelDataPointer data; + MetavoxelData data; }; MetavoxelServer* _server; diff --git a/cmake/macros/AutoMTC.cmake b/cmake/macros/AutoMTC.cmake index fca8170e64..f0c9ebc6a0 100644 --- a/cmake/macros/AutoMTC.cmake +++ b/cmake/macros/AutoMTC.cmake @@ -9,10 +9,12 @@ macro(AUTO_MTC TARGET ROOT_DIR) ${INCLUDE_FILES} DEPENDS mtc ${INCLUDE_FILES}) find_package(Qt5Core REQUIRED) + find_package(Qt5Script REQUIRED) + find_package(Qt5Widgets REQUIRED) add_library(${TARGET}_automtc STATIC ${TARGET}_automtc.cpp) - qt5_use_modules(${TARGET}_automtc Core) + qt5_use_modules(${TARGET}_automtc Core Script Widgets) target_link_libraries(${TARGET} ${TARGET}_automtc) diff --git a/data-server/src/DataServer.cpp b/data-server/src/DataServer.cpp index c59749ed08..43fc52fb06 100644 --- a/data-server/src/DataServer.cpp +++ b/data-server/src/DataServer.cpp @@ -6,9 +6,12 @@ // Copyright (c) 2014 HighFidelity, Inc. All rights reserved. // +#include +#include #include #include +#include #include #include "DataServer.h" @@ -21,7 +24,8 @@ const unsigned short REDIS_PORT = 6379; DataServer::DataServer(int argc, char* argv[]) : QCoreApplication(argc, argv), _socket(), - _redis(NULL) + _redis(NULL), + _uuid(QUuid::createUuid()) { _socket.bind(QHostAddress::Any, DATA_SERVER_LISTEN_PORT); @@ -51,66 +55,60 @@ DataServer::~DataServer() { const int MAX_PACKET_SIZE = 1500; void DataServer::readPendingDatagrams() { - qint64 receivedBytes = 0; - static unsigned char packetData[MAX_PACKET_SIZE]; - + QByteArray receivedPacket; HifiSockAddr senderSockAddr; - while (_socket.hasPendingDatagrams() && - (receivedBytes = _socket.readDatagram(reinterpret_cast(packetData), MAX_PACKET_SIZE, - senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()))) { - if ((packetData[0] == PACKET_TYPE_DATA_SERVER_PUT || packetData[0] == PACKET_TYPE_DATA_SERVER_GET) && - packetVersionMatch(packetData, senderSockAddr)) { - - int readBytes = numBytesForPacketHeader(packetData); + while (_socket.hasPendingDatagrams()) { + receivedPacket.resize(_socket.pendingDatagramSize()); + _socket.readDatagram(receivedPacket.data(), _socket.pendingDatagramSize(), + senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); + + PacketType requestType = packetTypeForPacket(receivedPacket); + + if ((requestType == PacketTypeDataServerPut || requestType == PacketTypeDataServerGet) && + packetVersionMatch(receivedPacket)) { + + QDataStream packetStream(receivedPacket); + int numReceivedHeaderBytes = numBytesForPacketHeader(receivedPacket); + packetStream.skipRawData(numReceivedHeaderBytes); // pull the sequence number used for this packet quint8 sequenceNumber = 0; - memcpy(&sequenceNumber, packetData + readBytes, sizeof(sequenceNumber)); - readBytes += sizeof(sequenceNumber); - + + packetStream >> sequenceNumber; + // pull the UUID that we will need as part of the key - QString uuidString(reinterpret_cast(packetData + readBytes)); - QUuid parsedUUID(uuidString); + QString userString; + packetStream >> userString; + QUuid parsedUUID(userString); if (parsedUUID.isNull()) { // we failed to parse a UUID, this means the user has sent us a username - QString username(reinterpret_cast(packetData + readBytes)); - readBytes += username.size() + sizeof('\0'); - // ask redis for the UUID for this user - redisReply* reply = (redisReply*) redisCommand(_redis, "GET user:%s", qPrintable(username)); + redisReply* reply = (redisReply*) redisCommand(_redis, "GET user:%s", qPrintable(userString)); if (reply->type == REDIS_REPLY_STRING) { parsedUUID = QUuid(QString(reply->str)); } if (!parsedUUID.isNull()) { - qDebug() << "Found UUID" << parsedUUID << "for username" << username; + qDebug() << "Found UUID" << parsedUUID << "for username" << userString; } else { - qDebug() << "Failed UUID lookup for username" << username; + qDebug() << "Failed UUID lookup for username" << userString; } freeReplyObject(reply); reply = NULL; - } else { - readBytes += uuidString.size() + sizeof('\0'); } if (!parsedUUID.isNull()) { - // pull the number of keys the user has sent - unsigned char numKeys = packetData[readBytes++]; - - if (packetData[0] == PACKET_TYPE_DATA_SERVER_PUT) { + if (requestType == PacketTypeDataServerPut) { - // pull the key that specifies the data the user is putting/getting - QString dataKey(reinterpret_cast(packetData + readBytes)); - readBytes += dataKey.size() + sizeof('\0'); + // pull the key and value that specifies the data the user is putting/getting + QString dataKey, dataValue; - // grab the string value the user wants us to put, null terminate it - QString dataValue(reinterpret_cast(packetData + readBytes)); - readBytes += dataValue.size() + sizeof('\0'); + packetStream >> dataKey >> dataValue; qDebug("Sending command to redis: SET uuid:%s:%s %s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), @@ -122,78 +120,72 @@ void DataServer::readPendingDatagrams() { if (reply->type == REDIS_REPLY_STATUS && strcmp("OK", reply->str) == 0) { // if redis stored the value successfully reply back with a confirm - // which is the sent packet with the header replaced - packetData[0] = PACKET_TYPE_DATA_SERVER_CONFIRM; - _socket.writeDatagram(reinterpret_cast(packetData), receivedBytes, - senderSockAddr.getAddress(), senderSockAddr.getPort()); + // which is a reply packet with the sequence number + QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerConfirm, _uuid); + + replyPacket.append(sequenceNumber); + + _socket.writeDatagram(replyPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); } freeReplyObject(reply); } else { // setup a send packet with the returned data // leverage the packetData sent by overwriting and appending - int numSendPacketBytes = receivedBytes; + QByteArray sendPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerSend, _uuid); + QDataStream sendPacketStream(&sendPacket, QIODevice::Append); - packetData[0] = PACKET_TYPE_DATA_SERVER_SEND; + sendPacketStream << sequenceNumber; - if (strcmp((char*) packetData + readBytes, "uuid") != 0) { + // pull the key list that specifies the data the user is putting/getting + QString keyListString; + packetStream >> keyListString; + + if (keyListString != "uuid") { + + // copy the parsed UUID + sendPacketStream << uuidStringWithoutCurlyBraces(parsedUUID); const char MULTI_KEY_VALUE_SEPARATOR = '|'; - // the user has sent one or more keys - make the associated requests - for (int j = 0; j < numKeys; j++) { - - // pull the key that specifies the data the user is putting/getting, null terminate it - int numDataKeyBytes = 0; - - // look for the key separator or the null terminator - while (packetData[readBytes + numDataKeyBytes] != MULTI_KEY_VALUE_SEPARATOR - && packetData[readBytes + numDataKeyBytes] != '\0') { - numDataKeyBytes++; - } - - QString dataKey(QByteArray(reinterpret_cast(packetData + readBytes), numDataKeyBytes)); - readBytes += dataKey.size() + sizeof('\0'); - + // append the keyListString back to the sendPacket + sendPacketStream << keyListString; + + QStringList keyList = keyListString.split(MULTI_KEY_VALUE_SEPARATOR); + QStringList valueList; + + foreach (const QString& dataKey, keyList) { qDebug("Sending command to redis: GET uuid:%s:%s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey)); redisReply* reply = (redisReply*) redisCommand(_redis, "GET uuid:%s:%s", - qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), - qPrintable(dataKey)); + qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), + qPrintable(dataKey)); if (reply->len) { // copy the value that redis returned - memcpy(packetData + numSendPacketBytes, reply->str, reply->len); - numSendPacketBytes += reply->len; - + valueList << QString(reply->str); } else { // didn't find a value - insert a space - packetData[numSendPacketBytes++] = ' '; + valueList << QChar(' '); } - // add the multi-value separator - packetData[numSendPacketBytes++] = MULTI_KEY_VALUE_SEPARATOR; - freeReplyObject(reply); } - // null terminate the packet we're sending back (erases the trailing separator) - packetData[(numSendPacketBytes - 1)] = '\0'; + // append the value QStringList using the right separator + sendPacketStream << valueList.join(MULTI_KEY_VALUE_SEPARATOR); } else { - // user is asking for a UUID matching username, copy the UUID we found - QString uuidString = uuidStringWithoutCurlyBraces(parsedUUID); - memcpy(packetData + numSendPacketBytes, qPrintable(uuidString), uuidString.size() + sizeof('\0')); - numSendPacketBytes += uuidString.size() + sizeof('\0'); + // user was asking for their UUID + sendPacketStream << userString; + sendPacketStream << QString("uuid"); + sendPacketStream << uuidStringWithoutCurlyBraces(parsedUUID); } // reply back with the send packet - _socket.writeDatagram(reinterpret_cast(packetData), numSendPacketBytes, - senderSockAddr.getAddress(), senderSockAddr.getPort()); - - + _socket.writeDatagram(sendPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); } } } } -} \ No newline at end of file +} diff --git a/data-server/src/DataServer.h b/data-server/src/DataServer.h index 097f6a6533..392387f4d8 100644 --- a/data-server/src/DataServer.h +++ b/data-server/src/DataServer.h @@ -10,6 +10,7 @@ #define __hifi__DataServer__ #include +#include #include #include @@ -22,6 +23,7 @@ public: private: QUdpSocket _socket; redisContext* _redis; + QUuid _uuid; private slots: void readPendingDatagrams(); }; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 0ee7cca467..37626e8a43 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -8,9 +8,11 @@ #include +#include #include #include #include +#include #include #include @@ -27,65 +29,37 @@ const char* VOXEL_SERVER_CONFIG = "voxelServerConfig"; const char* PARTICLE_SERVER_CONFIG = "particleServerConfig"; const char* METAVOXEL_SERVER_CONFIG = "metavoxelServerConfig"; -void signalhandler(int sig){ - if (sig == SIGINT) { - qApp->quit(); - } -} - const quint16 DOMAIN_SERVER_HTTP_PORT = 8080; DomainServer::DomainServer(int argc, char* argv[]) : QCoreApplication(argc, argv), _HTTPManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this), - _assignmentQueueMutex(), + _staticAssignmentHash(), _assignmentQueue(), - _staticAssignmentFile(QString("%1/config.ds").arg(QCoreApplication::applicationDirPath())), - _staticAssignmentFileData(NULL), - _voxelServerConfig(NULL), - _metavoxelServerConfig(NULL), _hasCompletedRestartHold(false) { - signal(SIGINT, signalhandler); - const char CUSTOM_PORT_OPTION[] = "-p"; const char* customPortString = getCmdOption(argc, (const char**) argv, CUSTOM_PORT_OPTION); unsigned short domainServerPort = customPortString ? atoi(customPortString) : DEFAULT_DOMAIN_SERVER_PORT; - - const char CONFIG_FILE_OPTION[] = "-c"; - const char* configFilePath = getCmdOption(argc, (const char**) argv, CONFIG_FILE_OPTION); - - if (!readConfigFile(configFilePath)) { - QByteArray voxelConfigOption = QString("--%1").arg(VOXEL_SERVER_CONFIG).toLocal8Bit(); - _voxelServerConfig = getCmdOption(argc, (const char**) argv, voxelConfigOption.constData()); - - QByteArray particleConfigOption = QString("--%1").arg(PARTICLE_SERVER_CONFIG).toLocal8Bit(); - _particleServerConfig = getCmdOption(argc, (const char**) argv, particleConfigOption.constData()); - - QByteArray metavoxelConfigOption = QString("--%1").arg(METAVOXEL_SERVER_CONFIG).toLocal8Bit(); - _metavoxelServerConfig = getCmdOption(argc, (const char**) argv, metavoxelConfigOption.constData()); + + QStringList argumentList = arguments(); + int argumentIndex = 0; + + QSet parsedTypes(QSet() << Assignment::AgentType); + parseCommandLineTypeConfigs(argumentList, parsedTypes); + + const QString CONFIG_FILE_OPTION = "--configFile"; + if ((argumentIndex = argumentList.indexOf(CONFIG_FILE_OPTION)) != -1) { + QString configFilePath = argumentList.value(argumentIndex + 1); + readConfigFile(configFilePath, parsedTypes); } + + populateDefaultStaticAssignmentsExcludingTypes(parsedTypes); - NodeList* nodeList = NodeList::createInstance(NODE_TYPE_DOMAIN, domainServerPort); + NodeList* nodeList = NodeList::createInstance(NodeType::DomainServer, domainServerPort); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), this, SLOT(nodeKilled(SharedNodePointer))); - if (!_staticAssignmentFile.exists() || _voxelServerConfig) { - - if (_voxelServerConfig) { - // we have a new VS config, clear the existing file to start fresh - _staticAssignmentFile.remove(); - } - - prepopulateStaticAssignmentFile(); - } - - _staticAssignmentFile.open(QIODevice::ReadWrite); - - _staticAssignmentFileData = _staticAssignmentFile.map(0, _staticAssignmentFile.size()); - - _staticAssignments = (Assignment*) _staticAssignmentFileData; - QTimer* silentNodeTimer = new QTimer(this); connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); @@ -94,47 +68,184 @@ DomainServer::DomainServer(int argc, char* argv[]) : // fire a single shot timer to add static assignments back into the queue after a restart QTimer::singleShot(RESTART_HOLD_TIME_MSECS, this, SLOT(addStaticAssignmentsBackToQueueAfterRestart())); +} - connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup())); +void DomainServer::parseCommandLineTypeConfigs(const QStringList& argumentList, QSet& excludedTypes) { + // check for configs from the command line, these take precedence + const QString CONFIG_TYPE_OPTION = "--configType"; + int clConfigIndex = argumentList.indexOf(CONFIG_TYPE_OPTION); + + // enumerate all CL config overrides and parse them to files + while (clConfigIndex != -1) { + int clConfigType = argumentList.value(clConfigIndex + 1).toInt(); + if (clConfigType < Assignment::AllTypes && !excludedTypes.contains((Assignment::Type) clConfigIndex)) { + Assignment::Type assignmentType = (Assignment::Type) clConfigType; + createStaticAssignmentsForTypeGivenConfigString((Assignment::Type) assignmentType, + argumentList.value(clConfigIndex + 2)); + excludedTypes.insert(assignmentType); + } + + clConfigIndex = argumentList.indexOf(CONFIG_TYPE_OPTION, clConfigIndex + 1); + } +} + +// Attempts to read configuration from specified path +// returns true on success, false otherwise +void DomainServer::readConfigFile(const QString& path, QSet& excludedTypes) { + if (path.isEmpty()) { + // config file not specified + return; + } + + if (!QFile::exists(path)) { + qWarning("Specified configuration file does not exist!"); + return; + } + + QFile configFile(path); + if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning("Can't open specified configuration file!"); + return; + } else { + qDebug() << "Reading configuration from" << path; + } + + QTextStream configStream(&configFile); + QByteArray configStringByteArray = configStream.readAll().toUtf8(); + QJsonObject configDocObject = QJsonDocument::fromJson(configStringByteArray).object(); + configFile.close(); + + QSet appendedExcludedTypes = excludedTypes; + + foreach (const QString& rootStringValue, configDocObject.keys()) { + int possibleConfigType = rootStringValue.toInt(); + + if (possibleConfigType < Assignment::AllTypes + && !excludedTypes.contains((Assignment::Type) possibleConfigType)) { + // this is an appropriate config type and isn't already in our excluded types + // we are good to parse it + Assignment::Type assignmentType = (Assignment::Type) possibleConfigType; + QString configString = readServerAssignmentConfig(configDocObject, rootStringValue); + createStaticAssignmentsForTypeGivenConfigString(assignmentType, configString); + + excludedTypes.insert(assignmentType); + } + } +} + +// find assignment configurations on the specified node name and json object +// returns a string in the form of its equivalent cmd line params +QString DomainServer::readServerAssignmentConfig(const QJsonObject& jsonObject, const QString& nodeName) { + QJsonArray nodeArray = jsonObject[nodeName].toArray(); + + QStringList serverConfig; + foreach (const QJsonValue& childValue, nodeArray) { + QString cmdParams; + QJsonObject childObject = childValue.toObject(); + QStringList keys = childObject.keys(); + for (int i = 0; i < keys.size(); i++) { + QString key = keys[i]; + QString value = childObject[key].toString(); + // both cmd line params and json keys are the same + cmdParams += QString("--%1 %2 ").arg(key, value); + } + serverConfig << cmdParams; + } + + // according to split() calls from DomainServer::prepopulateStaticAssignmentFile + // we shold simply join them with semicolons + return serverConfig.join(';'); +} + +void DomainServer::addStaticAssignmentToAssignmentHash(Assignment* newAssignment) { + qDebug() << "Inserting assignment" << *newAssignment << "to static assignment hash."; + _staticAssignmentHash.insert(newAssignment->getUUID(), SharedAssignmentPointer(newAssignment)); +} + +void DomainServer::createStaticAssignmentsForTypeGivenConfigString(Assignment::Type type, const QString& configString) { + // we have a string for config for this type + qDebug() << "Parsing command line config for assignment type" << type; + + QStringList multiConfigList = configString.split(";", QString::SkipEmptyParts); + + const QString ASSIGNMENT_CONFIG_POOL_REGEX = "--pool\\s*(\\w+)"; + QRegExp poolRegex(ASSIGNMENT_CONFIG_POOL_REGEX); + + // read each config to a payload for this type of assignment + for (int i = 0; i < multiConfigList.size(); i++) { + QString config = multiConfigList.at(i); + + // check the config string for a pool + QString assignmentPool; + + int poolIndex = poolRegex.indexIn(config); + + if (poolIndex != -1) { + assignmentPool = poolRegex.cap(1); + + // remove the pool from the config string, the assigned node doesn't need it + config.remove(poolIndex, poolRegex.matchedLength()); + } + + qDebug("Type %d config[%d] = %s", type, i, config.toLocal8Bit().constData()); + + Assignment* configAssignment = new Assignment(Assignment::CreateCommand, type, assignmentPool); + + configAssignment->setPayload(config.toUtf8()); + + addStaticAssignmentToAssignmentHash(configAssignment); + } +} + +void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet& excludedTypes) { + // enumerate over all assignment types and see if we've already excluded it + for (int defaultedType = Assignment::AudioMixerType; defaultedType != Assignment::AllTypes; defaultedType++) { + if (!excludedTypes.contains((Assignment::Type) defaultedType)) { + // type has not been set from a command line or config file config, use the default + // by clearing whatever exists and writing a single default assignment with no payload + Assignment* newAssignment = new Assignment(Assignment::CreateCommand, (Assignment::Type) defaultedType); + addStaticAssignmentToAssignmentHash(newAssignment); + } + } } void DomainServer::readAvailableDatagrams() { NodeList* nodeList = NodeList::getInstance(); HifiSockAddr senderSockAddr, nodePublicAddress, nodeLocalAddress; - - static unsigned char packetData[MAX_PACKET_SIZE]; - - static unsigned char broadcastPacket[MAX_PACKET_SIZE]; - - static unsigned char* currentBufferPos; - static unsigned char* startPointer; - - int receivedBytes = 0; + + static QByteArray broadcastPacket = byteArrayWithPopluatedHeader(PacketTypeDomainList); + static int numBroadcastPacketHeaderBytes = broadcastPacket.size(); + + static QByteArray assignmentPacket = byteArrayWithPopluatedHeader(PacketTypeCreateAssignment); + static int numAssignmentPacketHeaderBytes = assignmentPacket.size(); + + QByteArray receivedPacket; + NodeType_t nodeType; + QUuid nodeUUID; while (nodeList->getNodeSocket().hasPendingDatagrams()) { - if ((receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE, - senderSockAddr.getAddressPointer(), - senderSockAddr.getPortPointer())) - && packetVersionMatch((unsigned char*) packetData, senderSockAddr)) { - if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_TYPE_DOMAIN_LIST_REQUEST) { + receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); + nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(), + senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); + + if (packetVersionMatch(receivedPacket)) { + PacketType requestType = packetTypeForPacket(receivedPacket); + if (requestType == PacketTypeDomainListRequest) { + // this is an RFD or domain list request packet, and there is a version match - - int numBytesSenderHeader = numBytesForPacketHeader((unsigned char*) packetData); - - NODE_TYPE nodeType = *(packetData + numBytesSenderHeader); - - int packetIndex = numBytesSenderHeader + sizeof(NODE_TYPE); - QUuid nodeUUID = QUuid::fromRfc4122(QByteArray(((char*) packetData + packetIndex), NUM_BYTES_RFC4122_UUID)); - packetIndex += NUM_BYTES_RFC4122_UUID; - - int numBytesPrivateSocket = HifiSockAddr::unpackSockAddr(packetData + packetIndex, nodePublicAddress); - packetIndex += numBytesPrivateSocket; - + QDataStream packetStream(receivedPacket); + packetStream.skipRawData(numBytesForPacketHeader(receivedPacket)); + + deconstructPacketHeader(receivedPacket, nodeUUID); + + packetStream >> nodeType; + packetStream >> nodePublicAddress >> nodeLocalAddress; + if (nodePublicAddress.getAddress().isNull()) { // this node wants to use us its STUN server // so set the node public address to whatever we perceive the public address to be - + // if the sender is on our box then leave its public address to 0 so that // other users attempt to reach it on the same address they have for the domain-server if (senderSockAddr.getAddress().isLoopback()) { @@ -143,105 +254,90 @@ void DomainServer::readAvailableDatagrams() { nodePublicAddress.setAddress(senderSockAddr.getAddress()); } } - - int numBytesPublicSocket = HifiSockAddr::unpackSockAddr(packetData + packetIndex, nodeLocalAddress); - packetIndex += numBytesPublicSocket; - - const char STATICALLY_ASSIGNED_NODES[] = { - NODE_TYPE_AUDIO_MIXER, - NODE_TYPE_AVATAR_MIXER, - NODE_TYPE_VOXEL_SERVER, - NODE_TYPE_METAVOXEL_SERVER - }; - - Assignment* matchingStaticAssignment = NULL; - - if (memchr(STATICALLY_ASSIGNED_NODES, nodeType, sizeof(STATICALLY_ASSIGNED_NODES)) == NULL - || ((matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType)) - || checkInWithUUIDMatchesExistingNode(nodePublicAddress, - nodeLocalAddress, - nodeUUID))) + + const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer + << NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer + << NodeType::MetavoxelServer; + + SharedAssignmentPointer matchingStaticAssignment; + + // check if this is a non-statically assigned node, a node that is assigned and checking in for the first time + // or a node that has already checked in and is continuing to report for duty + if (!STATICALLY_ASSIGNED_NODES.contains(nodeType) + || (matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType)) + || nodeList->getInstance()->nodeWithUUID(nodeUUID)) { SharedNodePointer checkInNode = nodeList->addOrUpdateNode(nodeUUID, nodeType, nodePublicAddress, nodeLocalAddress); - + + // resize our broadcast packet in preparation to set it up again + broadcastPacket.resize(numBroadcastPacketHeaderBytes); + if (matchingStaticAssignment) { // this was a newly added node with a matching static assignment - + + // remove the matching assignment from the assignment queue so we don't take the next check in + // (if it exists) if (_hasCompletedRestartHold) { - // remove the matching assignment from the assignment queue so we don't take the next check in - removeAssignmentFromQueue(matchingStaticAssignment); + removeMatchingAssignmentFromQueue(matchingStaticAssignment); } - - // set the linked data for this node to a copy of the matching assignment - // so we can re-queue it should the node die - Assignment* nodeCopyOfMatchingAssignment = new Assignment(*matchingStaticAssignment); - - checkInNode->setLinkedData(nodeCopyOfMatchingAssignment); } - - int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_DOMAIN); - - currentBufferPos = broadcastPacket + numHeaderBytes; - startPointer = currentBufferPos; - - unsigned char* nodeTypesOfInterest = packetData + packetIndex + sizeof(unsigned char); - int numInterestTypes = *(nodeTypesOfInterest - 1); - + + quint8 numInterestTypes = 0; + packetStream >> numInterestTypes; + + NodeType_t* nodeTypesOfInterest = reinterpret_cast(receivedPacket.data() + + packetStream.device()->pos()); + if (numInterestTypes > 0) { + QDataStream broadcastDataStream(&broadcastPacket, QIODevice::Append); + // if the node has sent no types of interest, assume they want nothing but their own ID back foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { if (node->getUUID() != nodeUUID && memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) { - + // don't send avatar nodes to other avatars, that will come from avatar mixer - if (nodeType != NODE_TYPE_AGENT || node->getType() != NODE_TYPE_AGENT) { - currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, node.data()); - } - + broadcastDataStream << *node.data(); } } } - + // update last receive to now - uint64_t timeNow = usecTimestampNow(); + quint64 timeNow = usecTimestampNow(); checkInNode->setLastHeardMicrostamp(timeNow); - + // send the constructed list back to this node - nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, - (currentBufferPos - startPointer) + numHeaderBytes, + nodeList->getNodeSocket().writeDatagram(broadcastPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); } - } else if (packetData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) { - - if (_assignmentQueue.size() > 0) { - // construct the requested assignment from the packet data - Assignment requestAssignment(packetData, receivedBytes); - - qDebug("Received a request for assignment type %i from %s.", - requestAssignment.getType(), qPrintable(senderSockAddr.getAddress().toString())); - - Assignment* assignmentToDeploy = deployableAssignmentForRequest(requestAssignment); - - if (assignmentToDeploy) { - - // give this assignment out, either the type matches or the requestor said they will take any - int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_CREATE_ASSIGNMENT); - int numAssignmentBytes = assignmentToDeploy->packToBuffer(broadcastPacket + numHeaderBytes); - - nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, numHeaderBytes + numAssignmentBytes, - senderSockAddr.getAddress(), senderSockAddr.getPort()); - - if (assignmentToDeploy->getNumberOfInstances() == 0) { - // there are no more instances of this script to send out, delete it - delete assignmentToDeploy; - } - } - + } else if (requestType == PacketTypeRequestAssignment) { + + // construct the requested assignment from the packet data + Assignment requestAssignment(receivedPacket); + + qDebug() << "Received a request for assignment type" << requestAssignment.getType() + << "from" << senderSockAddr; + + SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment); + + if (assignmentToDeploy) { + qDebug() << "Deploying assignment -" << *assignmentToDeploy.data() << "- to" << senderSockAddr; + + // give this assignment out, either the type matches or the requestor said they will take any + assignmentPacket.resize(numAssignmentPacketHeaderBytes); + + QDataStream assignmentStream(&assignmentPacket, QIODevice::Append); + + assignmentStream << *assignmentToDeploy.data(); + + nodeList->getNodeSocket().writeDatagram(assignmentPacket, + senderSockAddr.getAddress(), senderSockAddr.getPort()); } else { - qDebug() << "Received an invalid assignment request from" << senderSockAddr.getAddress(); + qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType() + << "from" << senderSockAddr; } } } @@ -266,7 +362,7 @@ QJsonObject jsonObjectForNode(Node* node) { QJsonObject nodeJson; // re-format the type name so it matches the target name - QString nodeTypeName(node->getTypeName()); + QString nodeTypeName = NodeType::getNodeTypeName(node->getType()); nodeTypeName = nodeTypeName.toLower(); nodeTypeName.replace(' ', '-'); @@ -278,77 +374,13 @@ QJsonObject jsonObjectForNode(Node* node) { nodeJson[JSON_KEY_LOCAL_SOCKET] = jsonForSocket(node->getLocalSocket()); // if the node has pool information, add it - if (node->getLinkedData() && ((Assignment*) node->getLinkedData())->hasPool()) { - nodeJson[JSON_KEY_POOL] = QString(((Assignment*) node->getLinkedData())->getPool()); + if (node->getLinkedData() && !((Assignment*) node->getLinkedData())->getPool().isEmpty()) { + nodeJson[JSON_KEY_POOL] = ((Assignment*) node->getLinkedData())->getPool(); } return nodeJson; } -// Attempts to read configuration from specified path -// returns true on success, false otherwise -bool DomainServer::readConfigFile(const char* path) { - if (!path) { - // config file not specified - return false; - } - - if (!QFile::exists(path)) { - qWarning("Specified configuration file does not exist!\n"); - return false; - } - - QFile configFile(path); - if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - qWarning("Can't open specified configuration file!\n"); - return false; - } else { - qDebug("Reading configuration from %s\n", path); - } - QTextStream configStream(&configFile); - QByteArray configStringByteArray = configStream.readAll().toUtf8(); - QJsonObject configDocObject = QJsonDocument::fromJson(configStringByteArray).object(); - configFile.close(); - - QString voxelServerConfig = readServerAssignmentConfig(configDocObject, VOXEL_SERVER_CONFIG); - _voxelServerConfig = new char[strlen(voxelServerConfig.toLocal8Bit().constData()) +1]; - _voxelServerConfig = strcpy((char *) _voxelServerConfig, voxelServerConfig.toLocal8Bit().constData() + '\0'); - - QString particleServerConfig = readServerAssignmentConfig(configDocObject, PARTICLE_SERVER_CONFIG); - _particleServerConfig = new char[strlen(particleServerConfig.toLocal8Bit().constData()) +1]; - _particleServerConfig = strcpy((char *) _particleServerConfig, particleServerConfig.toLocal8Bit().constData() + '\0'); - - QString metavoxelServerConfig = readServerAssignmentConfig(configDocObject, METAVOXEL_SERVER_CONFIG); - _metavoxelServerConfig = new char[strlen(metavoxelServerConfig.toLocal8Bit().constData()) +1]; - _metavoxelServerConfig = strcpy((char *) _metavoxelServerConfig, metavoxelServerConfig.toLocal8Bit().constData() + '\0'); - - return true; -} - -// find assignment configurations on the specified node name and json object -// returns a string in the form of its equivalent cmd line params -QString DomainServer::readServerAssignmentConfig(QJsonObject jsonObject, const char* nodeName) { - QJsonArray nodeArray = jsonObject[nodeName].toArray(); - - QStringList serverConfig; - foreach (const QJsonValue & childValue, nodeArray) { - QString cmdParams; - QJsonObject childObject = childValue.toObject(); - QStringList keys = childObject.keys(); - for (int i = 0; i < keys.size(); i++) { - QString key = keys[i]; - QString value = childObject[key].toString(); - // both cmd line params and json keys are the same - cmdParams += QString("--%1 %2 ").arg(key, value); - } - serverConfig << cmdParams; - } - - // according to split() calls from DomainServer::prepopulateStaticAssignmentFile - // we shold simply join them with semicolons - return serverConfig.join(';'); -} - bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString& path) { const QString JSON_MIME_TYPE = "application/json"; @@ -377,24 +409,19 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString& QJsonObject queuedAssignmentsJSON; // add the queued but unfilled assignments to the json - std::deque::iterator assignment = _assignmentQueue.begin(); - - while (assignment != _assignmentQueue.end()) { + foreach(const SharedAssignmentPointer& assignment, _assignmentQueue) { QJsonObject queuedAssignmentJSON; - QString uuidString = uuidStringWithoutCurlyBraces((*assignment)->getUUID()); - queuedAssignmentJSON[JSON_KEY_TYPE] = QString((*assignment)->getTypeName()); + QString uuidString = uuidStringWithoutCurlyBraces(assignment->getUUID()); + queuedAssignmentJSON[JSON_KEY_TYPE] = QString(assignment->getTypeName()); // if the assignment has a pool, add it - if ((*assignment)->hasPool()) { - queuedAssignmentJSON[JSON_KEY_POOL] = QString((*assignment)->getPool()); + if (!assignment->getPool().isEmpty()) { + queuedAssignmentJSON[JSON_KEY_POOL] = assignment->getPool(); } // add this queued assignment to the JSON queuedAssignmentsJSON[uuidString] = queuedAssignmentJSON; - - // push forward the iterator to check the next assignment - assignment++; } assignmentJSON["queued"] = queuedAssignmentsJSON; @@ -432,11 +459,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString& // this is a script upload - ask the HTTPConnection to parse the form data QList formData = connection->parseFormData(); - // create an assignment for this saved script, for now make it local only - Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand, - Assignment::AgentType, - NULL, - Assignment::LocalLocation); + // create an assignment for this saved script + Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType); // check how many instances of this assignment the user wants by checking the ASSIGNMENT-INSTANCES header const QString ASSIGNMENT_INSTANCES_HEADER = "ASSIGNMENT-INSTANCES"; @@ -470,10 +494,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString& connection->respond(HTTPConnection::StatusCode200); // add the script assigment to the assignment queue - // lock the assignment queue mutex since we're operating on a different thread than DS main - _assignmentQueueMutex.lock(); - _assignmentQueue.push_back(scriptAssignment); - _assignmentQueueMutex.unlock(); + _assignmentQueue.enqueue(SharedAssignmentPointer(scriptAssignment)); } } else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) { if (path.startsWith(URI_NODE)) { @@ -508,326 +529,137 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString& return false; } -void DomainServer::addReleasedAssignmentBackToQueue(Assignment* releasedAssignment) { - qDebug() << "Adding assignment" << *releasedAssignment << " back to queue."; - - // find this assignment in the static file - for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) { - if (_staticAssignments[i].getUUID() == releasedAssignment->getUUID()) { - // reset the UUID on the static assignment - _staticAssignments[i].resetUUID(); - - // put this assignment back in the queue so it goes out - _assignmentQueueMutex.lock(); - _assignmentQueue.push_back(&_staticAssignments[i]); - _assignmentQueueMutex.unlock(); - - } else if (_staticAssignments[i].getUUID().isNull()) { - // we are at the blank part of the static assignments - break out - break; - } - } +void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment) { + QUuid oldUUID = assignment->getUUID(); + assignment->resetUUID(); + + qDebug() << "Reset UUID for assignment -" << *assignment.data() << "- and added to queue. Old UUID was" + << uuidStringWithoutCurlyBraces(oldUUID); + + // add the static assignment back under the right UUID, and to the queue + _staticAssignmentHash.insert(assignment->getUUID(), assignment); + + _assignmentQueue.enqueue(assignment); + + // remove the old assignment from the _staticAssignmentHash + // this must be done last so copies are created before the assignment passed by reference is killed + _staticAssignmentHash.remove(oldUUID); } void DomainServer::nodeKilled(SharedNodePointer node) { - // if this node has linked data it was from an assignment - if (node->getLinkedData()) { - Assignment* nodeAssignment = (Assignment*) node->getLinkedData(); - - addReleasedAssignmentBackToQueue(nodeAssignment); + // if this node's UUID matches a static assignment we need to throw it back in the assignment queue + SharedAssignmentPointer matchedAssignment = _staticAssignmentHash.value(node->getUUID()); + + if (matchedAssignment) { + refreshStaticAssignmentAndAddToQueue(matchedAssignment); } } -unsigned char* DomainServer::addNodeToBroadcastPacket(unsigned char* currentPosition, Node* nodeToAdd) { - *currentPosition++ = nodeToAdd->getType(); - - - QByteArray rfcUUID = nodeToAdd->getUUID().toRfc4122(); - memcpy(currentPosition, rfcUUID.constData(), rfcUUID.size()); - currentPosition += rfcUUID.size(); - - currentPosition += HifiSockAddr::packSockAddr(currentPosition, nodeToAdd->getPublicSocket()); - currentPosition += HifiSockAddr::packSockAddr(currentPosition, nodeToAdd->getLocalSocket()); - - // return the new unsigned char * for broadcast packet - return currentPosition; -} - -void DomainServer::prepopulateStaticAssignmentFile() { - int numFreshStaticAssignments = 0; - - // write a fresh static assignment array to file - - Assignment freshStaticAssignments[MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS]; - - // pre-populate the first static assignment list with assignments for root AuM, AvM, VS - freshStaticAssignments[numFreshStaticAssignments++] = Assignment(Assignment::CreateCommand, Assignment::AudioMixerType); - freshStaticAssignments[numFreshStaticAssignments++] = Assignment(Assignment::CreateCommand, Assignment::AvatarMixerType); - - // Handle Domain/Voxel Server configuration command line arguments - if (_voxelServerConfig) { - qDebug("Reading Voxel Server Configuration."); - qDebug() << "config: " << _voxelServerConfig; - - QString multiConfig((const char*) _voxelServerConfig); - QStringList multiConfigList = multiConfig.split(";"); - - // read each config to a payload for a VS assignment - for (int i = 0; i < multiConfigList.size(); i++) { - QString config = multiConfigList.at(i); - - qDebug("config[%d]=%s", i, config.toLocal8Bit().constData()); - - // Now, parse the config to check for a pool - const char ASSIGNMENT_CONFIG_POOL_OPTION[] = "--pool"; - QString assignmentPool; - - int poolIndex = config.indexOf(ASSIGNMENT_CONFIG_POOL_OPTION); - - if (poolIndex >= 0) { - int spaceBeforePoolIndex = config.indexOf(' ', poolIndex); - int spaceAfterPoolIndex = config.indexOf(' ', spaceBeforePoolIndex); - - assignmentPool = config.mid(spaceBeforePoolIndex + 1, spaceAfterPoolIndex); - qDebug() << "The pool for this voxel-assignment is" << assignmentPool; - } - - Assignment voxelServerAssignment(Assignment::CreateCommand, - Assignment::VoxelServerType, - (assignmentPool.isEmpty() ? NULL : assignmentPool.toLocal8Bit().constData())); - - int payloadLength = config.length() + sizeof(char); - voxelServerAssignment.setPayload((uchar*)config.toLocal8Bit().constData(), payloadLength); - - freshStaticAssignments[numFreshStaticAssignments++] = voxelServerAssignment; - } - } else { - Assignment rootVoxelServerAssignment(Assignment::CreateCommand, Assignment::VoxelServerType); - freshStaticAssignments[numFreshStaticAssignments++] = rootVoxelServerAssignment; - } - - // Handle Domain/Particle Server configuration command line arguments - if (_particleServerConfig) { - qDebug("Reading Particle Server Configuration."); - qDebug() << "config: " << _particleServerConfig; - - QString multiConfig((const char*) _particleServerConfig); - QStringList multiConfigList = multiConfig.split(";"); - - // read each config to a payload for a VS assignment - for (int i = 0; i < multiConfigList.size(); i++) { - QString config = multiConfigList.at(i); - - qDebug("config[%d]=%s", i, config.toLocal8Bit().constData()); - - // Now, parse the config to check for a pool - const char ASSIGNMENT_CONFIG_POOL_OPTION[] = "--pool"; - QString assignmentPool; - - int poolIndex = config.indexOf(ASSIGNMENT_CONFIG_POOL_OPTION); - - if (poolIndex >= 0) { - int spaceBeforePoolIndex = config.indexOf(' ', poolIndex); - int spaceAfterPoolIndex = config.indexOf(' ', spaceBeforePoolIndex); - - assignmentPool = config.mid(spaceBeforePoolIndex + 1, spaceAfterPoolIndex); - qDebug() << "The pool for this particle-assignment is" << assignmentPool; - } - - Assignment particleServerAssignment(Assignment::CreateCommand, - Assignment::ParticleServerType, - (assignmentPool.isEmpty() ? NULL : assignmentPool.toLocal8Bit().constData())); - - int payloadLength = config.length() + sizeof(char); - particleServerAssignment.setPayload((uchar*)config.toLocal8Bit().constData(), payloadLength); - - freshStaticAssignments[numFreshStaticAssignments++] = particleServerAssignment; - } - } else { - Assignment rootParticleServerAssignment(Assignment::CreateCommand, Assignment::ParticleServerType); - freshStaticAssignments[numFreshStaticAssignments++] = rootParticleServerAssignment; - } - - // handle metavoxel configuration command line argument - Assignment& metavoxelAssignment = (freshStaticAssignments[numFreshStaticAssignments++] = - Assignment(Assignment::CreateCommand, Assignment::MetavoxelServerType)); - if (_metavoxelServerConfig) { - metavoxelAssignment.setPayload((const unsigned char*)_metavoxelServerConfig, strlen(_metavoxelServerConfig)); - } - - qDebug() << "Adding" << numFreshStaticAssignments << "static assignments to fresh file."; - - _staticAssignmentFile.open(QIODevice::WriteOnly); - _staticAssignmentFile.write((char*) &freshStaticAssignments, sizeof(freshStaticAssignments)); - _staticAssignmentFile.resize(MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS * sizeof(Assignment)); - _staticAssignmentFile.close(); -} - -Assignment* DomainServer::matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NODE_TYPE nodeType) { - // pull the UUID passed with the check in - +SharedAssignmentPointer DomainServer::matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType) { if (_hasCompletedRestartHold) { - _assignmentQueueMutex.lock(); - - // iterate the assignment queue to check for a match - std::deque::iterator assignment = _assignmentQueue.begin(); - while (assignment != _assignmentQueue.end()) { - if ((*assignment)->getUUID() == checkInUUID) { - // return the matched assignment - _assignmentQueueMutex.unlock(); - return *assignment; + // look for a match in the assignment hash + + QQueue::iterator i = _assignmentQueue.begin(); + + while (i != _assignmentQueue.end()) { + if (i->data()->getType() == Assignment::typeForNodeType(nodeType) && i->data()->getUUID() == checkInUUID) { + return _assignmentQueue.takeAt(i - _assignmentQueue.begin()); } else { - // no match, push deque iterator forwards - assignment++; + ++i; } } - - _assignmentQueueMutex.unlock(); } else { - for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) { - if (_staticAssignments[i].getUUID() == checkInUUID) { - // return matched assignment - return &_staticAssignments[i]; - } else if (_staticAssignments[i].getUUID().isNull()) { - // end of static assignments, no match - return NULL - return NULL; - } + SharedAssignmentPointer matchingStaticAssignment = _staticAssignmentHash.value(checkInUUID); + if (matchingStaticAssignment && matchingStaticAssignment->getType() == nodeType) { + return matchingStaticAssignment; } } - return NULL; + return SharedAssignmentPointer(); } -Assignment* DomainServer::deployableAssignmentForRequest(Assignment& requestAssignment) { - _assignmentQueueMutex.lock(); - +SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assignment& requestAssignment) { // this is an unassigned client talking to us directly for an assignment // go through our queue and see if there are any assignments to give out - std::deque::iterator assignment = _assignmentQueue.begin(); + QQueue::iterator sharedAssignment = _assignmentQueue.begin(); - while (assignment != _assignmentQueue.end()) { + while (sharedAssignment != _assignmentQueue.end()) { + Assignment* assignment = sharedAssignment->data(); bool requestIsAllTypes = requestAssignment.getType() == Assignment::AllTypes; - bool assignmentTypesMatch = (*assignment)->getType() == requestAssignment.getType(); - bool nietherHasPool = !(*assignment)->hasPool() && !requestAssignment.hasPool(); - bool assignmentPoolsMatch = memcmp((*assignment)->getPool(), - requestAssignment.getPool(), - MAX_ASSIGNMENT_POOL_BYTES) == 0; - + bool assignmentTypesMatch = assignment->getType() == requestAssignment.getType(); + bool nietherHasPool = assignment->getPool().isEmpty() && requestAssignment.getPool().isEmpty(); + bool assignmentPoolsMatch = assignment->getPool() == requestAssignment.getPool(); + if ((requestIsAllTypes || assignmentTypesMatch) && (nietherHasPool || assignmentPoolsMatch)) { - Assignment* deployableAssignment = *assignment; - - if ((*assignment)->getType() == Assignment::AgentType) { + if (assignment->getType() == Assignment::AgentType) { // if there is more than one instance to send out, simply decrease the number of instances - if ((*assignment)->getNumberOfInstances() == 1) { - _assignmentQueue.erase(assignment); + if (assignment->getNumberOfInstances() == 1) { + return _assignmentQueue.takeAt(sharedAssignment - _assignmentQueue.begin()); + } else { + assignment->decrementNumberOfInstances(); + return *sharedAssignment; } - deployableAssignment->decrementNumberOfInstances(); - } else { // remove the assignment from the queue - _assignmentQueue.erase(assignment); + SharedAssignmentPointer deployableAssignment = _assignmentQueue.takeAt(sharedAssignment + - _assignmentQueue.begin()); // until we get a check-in from that GUID // put assignment back in queue but stick it at the back so the others have a chance to go out - _assignmentQueue.push_back(deployableAssignment); + _assignmentQueue.enqueue(deployableAssignment); + + // stop looping, we've handed out an assignment + return deployableAssignment; } - - // stop looping, we've handed out an assignment - _assignmentQueueMutex.unlock(); - return deployableAssignment; } else { // push forward the iterator to check the next assignment - assignment++; + ++sharedAssignment; } } - - _assignmentQueueMutex.unlock(); - return NULL; + + return SharedAssignmentPointer(); } -void DomainServer::removeAssignmentFromQueue(Assignment* removableAssignment) { - - _assignmentQueueMutex.lock(); - - std::deque::iterator assignment = _assignmentQueue.begin(); - - while (assignment != _assignmentQueue.end()) { - if ((*assignment)->getUUID() == removableAssignment->getUUID()) { - _assignmentQueue.erase(assignment); +void DomainServer::removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment) { + QQueue::iterator potentialMatchingAssignment = _assignmentQueue.begin(); + while (potentialMatchingAssignment != _assignmentQueue.end()) { + if (potentialMatchingAssignment->data()->getUUID() == removableAssignment->getUUID()) { + _assignmentQueue.erase(potentialMatchingAssignment); + + // we matched and removed an assignment, bail out break; } else { - // push forward the iterator to check the next assignment - assignment++; + ++potentialMatchingAssignment; } } - - _assignmentQueueMutex.unlock(); -} - -bool DomainServer::checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket, - const HifiSockAddr& nodeLocalSocket, - const QUuid& checkInUUID) { - NodeList* nodeList = NodeList::getInstance(); - - foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { - if (node->getLinkedData() - && nodePublicSocket == node->getPublicSocket() - && nodeLocalSocket == node->getLocalSocket() - && node->getUUID() == checkInUUID) { - // this is a matching existing node if the public socket, local socket, and UUID match - return true; - } - } - - return false; } void DomainServer::addStaticAssignmentsBackToQueueAfterRestart() { _hasCompletedRestartHold = true; // if the domain-server has just restarted, - // check if there are static assignments in the file that we need to - // throw into the assignment queue - - // pull anything in the static assignment file that isn't spoken for and add to the assignment queue - for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) { - if (_staticAssignments[i].getUUID().isNull()) { - // reached the end of static assignments, bail - break; - } - + // check if there are static assignments that we need to throw into the assignment queue + QHash staticHashCopy = _staticAssignmentHash; + QHash::iterator staticAssignment = staticHashCopy.begin(); + while (staticAssignment != staticHashCopy.end()) { + // add any of the un-matched static assignments to the queue bool foundMatchingAssignment = false; - - NodeList* nodeList = NodeList::getInstance(); - + // enumerate the nodes and check if there is one with an attached assignment with matching UUID - foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { - if (node->getLinkedData()) { - Assignment* linkedAssignment = (Assignment*) node->getLinkedData(); - if (linkedAssignment->getUUID() == _staticAssignments[i].getUUID()) { - foundMatchingAssignment = true; - break; - } + foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { + if (node->getUUID() == staticAssignment->data()->getUUID()) { + foundMatchingAssignment = true; } } - + if (!foundMatchingAssignment) { // this assignment has not been fulfilled - reset the UUID and add it to the assignment queue - _staticAssignments[i].resetUUID(); - - qDebug() << "Adding static assignment to queue -" << _staticAssignments[i]; - - _assignmentQueueMutex.lock(); - _assignmentQueue.push_back(&_staticAssignments[i]); - _assignmentQueueMutex.unlock(); + refreshStaticAssignmentAndAddToQueue(*staticAssignment); } + + ++staticAssignment; } } - -void DomainServer::cleanup() { - _staticAssignmentFile.unmap(_staticAssignmentFileData); - _staticAssignmentFile.close(); -} diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index fed5aaaa43..60251b3bb4 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -9,17 +9,16 @@ #ifndef __hifi__DomainServer__ #define __hifi__DomainServer__ -#include - #include -#include -#include +#include +#include +#include #include #include #include -const int MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS = 1000; +typedef QSharedPointer SharedAssignmentPointer; class DomainServer : public QCoreApplication, public HTTPRequestHandler { Q_OBJECT @@ -35,39 +34,27 @@ public slots: void nodeKilled(SharedNodePointer node); private: - bool readConfigFile(const char* path); - QString readServerAssignmentConfig(QJsonObject jsonObj, const char* nodeName); - - void prepopulateStaticAssignmentFile(); - Assignment* matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NODE_TYPE nodeType); - Assignment* deployableAssignmentForRequest(Assignment& requestAssignment); - void removeAssignmentFromQueue(Assignment* removableAssignment); - bool checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket, - const HifiSockAddr& nodeLocalSocket, - const QUuid& checkInUUI); - void addReleasedAssignmentBackToQueue(Assignment* releasedAssignment); + void parseCommandLineTypeConfigs(const QStringList& argumentList, QSet& excludedTypes); + void readConfigFile(const QString& path, QSet& excludedTypes); + QString readServerAssignmentConfig(const QJsonObject& jsonObject, const QString& nodeName); + void addStaticAssignmentToAssignmentHash(Assignment* newAssignment); + void createStaticAssignmentsForTypeGivenConfigString(Assignment::Type type, const QString& configString); + void populateDefaultStaticAssignmentsExcludingTypes(const QSet& excludedTypes); - unsigned char* addNodeToBroadcastPacket(unsigned char* currentPosition, Node* nodeToAdd); + SharedAssignmentPointer matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType); + SharedAssignmentPointer deployableAssignmentForRequest(const Assignment& requestAssignment); + void removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment); + void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment); HTTPManager _HTTPManager; - QMutex _assignmentQueueMutex; - std::deque _assignmentQueue; - - QFile _staticAssignmentFile; - uchar* _staticAssignmentFileData; - - Assignment* _staticAssignments; - - const char* _voxelServerConfig; - const char* _particleServerConfig; - const char* _metavoxelServerConfig; + QHash _staticAssignmentHash; + QQueue _assignmentQueue; bool _hasCompletedRestartHold; private slots: void readAvailableDatagrams(); void addStaticAssignmentsBackToQueueAfterRestart(); - void cleanup(); }; #endif /* defined(__hifi__DomainServer__) */ diff --git a/examples/clap.js b/examples/clap.js index fdd2b29aa2..81ccda64b7 100644 --- a/examples/clap.js +++ b/examples/clap.js @@ -62,4 +62,4 @@ function maybePlaySound() { } // Connect a call back that happens every frame -Agent.willSendVisualDataCallback.connect(maybePlaySound); \ No newline at end of file +Script.willSendVisualDataCallback.connect(maybePlaySound); \ No newline at end of file diff --git a/examples/collidingParticles.js b/examples/collidingParticles.js index cf1fce5660..81ccfe108b 100644 --- a/examples/collidingParticles.js +++ b/examples/collidingParticles.js @@ -132,7 +132,7 @@ function draw() { print(scriptB); numberParticlesAdded++; } else { - Agent.stop(); + Script.stop(); } print("Particles Stats: " + Particles.getLifetimeInSeconds() + " seconds," + @@ -150,5 +150,5 @@ function draw() { // register the call back so it fires before each data send print("here...\n"); Particles.setPacketsPerSecond(40000); -Agent.willSendVisualDataCallback.connect(draw); +Script.willSendVisualDataCallback.connect(draw); print("and here...\n"); diff --git a/examples/controllerExample.js b/examples/controllerExample.js new file mode 100644 index 0000000000..43eb516cee --- /dev/null +++ b/examples/controllerExample.js @@ -0,0 +1,202 @@ +// +// controllerExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 1/28/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates use of the Controller class +// +// + +// initialize our triggers +var triggerPulled = new Array(); +var numberOfTriggers = Controller.getNumberOfTriggers(); +for (t = 0; t < numberOfTriggers; t++) { + triggerPulled[t] = false; +} + +function checkController() { + var numberOfTriggers = Controller.getNumberOfTriggers(); + var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); + var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; + + // this is expected for hydras + if (numberOfTriggers == 2 && controllersPerTrigger == 2) { + for (var t = 0; t < numberOfTriggers; t++) { + var triggerValue = Controller.getTriggerValue(t); + + if (triggerPulled[t]) { + // must release to at least 0.1 + if (triggerValue < 0.1) { + triggerPulled[t] = false; // unpulled + } + } else { + // must pull to at least 0.9 + if (triggerValue > 0.9) { + triggerPulled[t] = true; // pulled + triggerToggled = true; + } + } + + if (triggerToggled) { + print("a trigger was toggled"); + } + } + } +} + +function keyPressEvent(event) { + print("keyPressEvent event.key=" + event.key); + print("keyPressEvent event.text=" + event.text); + + print("keyPressEvent event.isShifted=" + event.isShifted); + print("keyPressEvent event.isControl=" + event.isControl); + print("keyPressEvent event.isMeta=" + event.isMeta); + print("keyPressEvent event.isAlt=" + event.isAlt); + print("keyPressEvent event.isKeypad=" + event.isKeypad); + + + if (event.key == "A".charCodeAt(0)) { + print("the A key was pressed"); + } + if (event.key == " ".charCodeAt(0)) { + print("the key was pressed"); + } +} + +function mouseMoveEvent(event) { + print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y); +} + +function touchBeginEvent(event) { + print("touchBeginEvent event.x,y=" + event.x + ", " + event.y); +} + +function touchUpdateEvent(event) { + print("touchUpdateEvent event.x,y=" + event.x + ", " + event.y); +} + +function touchEndEvent(event) { + print("touchEndEvent event.x,y=" + event.x + ", " + event.y); +} + +// register the call back so it fires before each data send +Script.willSendVisualDataCallback.connect(checkController); + +// Map keyPress and mouse move events to our callbacks +Controller.keyPressEvent.connect(keyPressEvent); +var KeyEvent_A = { + key: "A".charCodeAt(0), + text: "A", + isShifted: false, + isMeta: false +}; + +var KeyEvent_a = { + text: "a", + isShifted: false, + isMeta: false +}; + +var KeyEvent_a2 = { + key: "a".charCodeAt(0), + isShifted: false, + isMeta: false +}; + +var KeyEvent_a3 = { + text: "a" +}; + +var KeyEvent_A2 = { + text: "A" +}; + + +var KeyEvent_9 = { + text: "9" +}; + +var KeyEvent_Num = { + text: "#" +}; + +var KeyEvent_At = { + text: "@" +}; + +var KeyEvent_MetaAt = { + text: "@", + isMeta: true +}; + +var KeyEvent_Up = { + text: "up" +}; +var KeyEvent_Down = { + text: "down" +}; +var KeyEvent_Left = { + text: "left" +}; +var KeyEvent_Right = { + text: "right" +}; + +// prevent the A key from going through to the application +print("KeyEvent_A"); +Controller.captureKeyEvents(KeyEvent_A); + +print("KeyEvent_A2"); +Controller.captureKeyEvents(KeyEvent_A2); + +print("KeyEvent_a"); +Controller.captureKeyEvents(KeyEvent_a); + +print("KeyEvent_a2"); +Controller.captureKeyEvents(KeyEvent_a2); + +print("KeyEvent_a3"); +Controller.captureKeyEvents(KeyEvent_a3); + +print("KeyEvent_9"); +Controller.captureKeyEvents(KeyEvent_9); + +print("KeyEvent_Num"); +Controller.captureKeyEvents(KeyEvent_Num); + +print("KeyEvent_At"); +Controller.captureKeyEvents(KeyEvent_At); + +print("KeyEvent_MetaAt"); +Controller.captureKeyEvents(KeyEvent_MetaAt); + +print("KeyEvent_Up"); +Controller.captureKeyEvents(KeyEvent_Up); +print("KeyEvent_Down"); +Controller.captureKeyEvents(KeyEvent_Down); +print("KeyEvent_Left"); +Controller.captureKeyEvents(KeyEvent_Left); +print("KeyEvent_Right"); +Controller.captureKeyEvents(KeyEvent_Right); + + + + +Controller.mouseMoveEvent.connect(mouseMoveEvent); + +// Map touch events to our callbacks +Controller.touchBeginEvent.connect(touchBeginEvent); +Controller.touchUpdateEvent.connect(touchUpdateEvent); +Controller.touchEndEvent.connect(touchEndEvent); + +// disable the standard application for touch events +Controller.captureTouchEvents(); + +function scriptEnding() { + // re-enabled the standard application for touch events + Controller.releaseTouchEvents(); +} + +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/count.js b/examples/count.js index 917bec342d..29799a8271 100644 --- a/examples/count.js +++ b/examples/count.js @@ -20,7 +20,7 @@ function scriptEnding() { } // register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(displayCount); +Script.willSendVisualDataCallback.connect(displayCount); // register our scriptEnding callback -Agent.scriptEnding.connect(scriptEnding); +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/drumStick.js b/examples/drumStick.js index 955fddbdee..78de8351db 100644 --- a/examples/drumStick.js +++ b/examples/drumStick.js @@ -69,4 +69,4 @@ function checkSticks() { } // Connect a call back that happens every frame -Agent.willSendVisualDataCallback.connect(checkSticks); \ No newline at end of file +Script.willSendVisualDataCallback.connect(checkSticks); \ No newline at end of file diff --git a/examples/editParticleExample.js b/examples/editParticleExample.js index 4eb5dfe907..152bb18fca 100644 --- a/examples/editParticleExample.js +++ b/examples/editParticleExample.js @@ -9,6 +9,9 @@ // var count = 0; +var moveUntil = 2000; +var stopAfter = moveUntil + 100; +var expectedLifetime = (moveUntil/60) + 1; // 1 second after done moving... var originalProperties = { position: { x: 10, @@ -28,7 +31,9 @@ var originalProperties = { color: { red: 0, green: 255, - blue: 0 } + blue: 0 }, + + lifetime: expectedLifetime }; @@ -38,19 +43,18 @@ var positionDelta = { x: 0.05, y: 0, z: 0 }; var particleID = Particles.addParticle(originalProperties); function moveParticle() { - if (count >= 100) { - //Agent.stop(); + if (count >= moveUntil) { // delete it... - if (count == 100) { + if (count == moveUntil) { print("calling Particles.deleteParticle()"); Particles.deleteParticle(particleID); } // stop it... - if (count >= 200) { - print("calling Agent.stop()"); - Agent.stop(); + if (count >= stopAfter) { + print("calling Script.stop()"); + Script.stop(); } count++; @@ -77,19 +81,9 @@ function moveParticle() { print("newProperties.position = " + newProperties.position.x + "," + newProperties.position.y+ "," + newProperties.position.z); Particles.editParticle(particleID, newProperties); - - // also check to see if we can "find" particles... - var searchAt = { x: 9, y: 0, z: 0}; - var searchRadius = 2; - var foundParticle = Particles.findClosestParticle(searchAt, searchRadius); - if (foundParticle.isKnownID) { - print("found particle:" + foundParticle.id); - } else { - print("could not find particle in or around x=9 to x=11:"); - } } // register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(moveParticle); +Script.willSendVisualDataCallback.connect(moveParticle); diff --git a/examples/findParticleExample.js b/examples/findParticleExample.js index f582ee6469..5eb257d502 100644 --- a/examples/findParticleExample.js +++ b/examples/findParticleExample.js @@ -1,5 +1,5 @@ // -// editParticleExample.js +// findParticleExample.js // hifi // // Created by Brad Hefta-Gaub on 1/24/14. @@ -65,8 +65,8 @@ function findParticles() { // run for a while, then clean up // stop it... if (iteration >= 100) { - print("calling Agent.stop()"); - Agent.stop(); + print("calling Script.stop()"); + Script.stop(); } print("--------------------------"); @@ -122,7 +122,7 @@ function findParticles() { // register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(findParticles); +Script.willSendVisualDataCallback.connect(findParticles); // register our scriptEnding callback -Agent.scriptEnding.connect(scriptEnding); +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/fountain.js b/examples/fountain.js index a095f91ed3..3c943d72a0 100644 --- a/examples/fountain.js +++ b/examples/fountain.js @@ -60,8 +60,8 @@ function makeFountain() { totalParticles++; } if (totalParticles > 100) { - Agent.stop(); + Script.stop(); } } // register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(makeFountain); \ No newline at end of file +Script.willSendVisualDataCallback.connect(makeFountain); \ No newline at end of file diff --git a/examples/gameoflife.js b/examples/gameoflife.js new file mode 100644 index 0000000000..6779941dc7 --- /dev/null +++ b/examples/gameoflife.js @@ -0,0 +1,133 @@ +// Add your JavaScript for assignment below this line + +// The following is an example of Conway's Game of Life (http://en.wikipedia.org/wiki/Conway's_Game_of_Life) + +var NUMBER_OF_CELLS_EACH_DIMENSION = 64; +var NUMBER_OF_CELLS = NUMBER_OF_CELLS_EACH_DIMENSION * NUMBER_OF_CELLS_EACH_DIMENSION; + +var currentCells = []; +var nextCells = []; + +var METER_LENGTH = 1; +var cellScale = (NUMBER_OF_CELLS_EACH_DIMENSION * METER_LENGTH) / NUMBER_OF_CELLS_EACH_DIMENSION; + +// randomly populate the cell start values +for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + // create the array to hold this row + currentCells[i] = []; + + // create the array to hold this row in the nextCells array + nextCells[i] = []; + + for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + currentCells[i][j] = Math.floor(Math.random() * 2); + + // put the same value in the nextCells array for first board draw + nextCells[i][j] = currentCells[i][j]; + } +} + +function isNeighbourAlive(i, j) { + if (i < 0 || i >= NUMBER_OF_CELLS_EACH_DIMENSION + || i < 0 || j >= NUMBER_OF_CELLS_EACH_DIMENSION) { + return 0; + } else { + return currentCells[i][j]; + } +} + +function updateCells() { + var i = 0; + var j = 0; + + for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + // figure out the number of live neighbours for the i-j cell + var liveNeighbours = + isNeighbourAlive(i + 1, j - 1) + isNeighbourAlive(i + 1, j) + isNeighbourAlive(i + 1, j + 1) + + isNeighbourAlive(i, j - 1) + isNeighbourAlive(i, j + 1) + + isNeighbourAlive(i - 1, j - 1) + isNeighbourAlive(i - 1, j) + isNeighbourAlive(i - 1, j + 1); + + if (currentCells[i][j]) { + // live cell + + if (liveNeighbours < 2) { + // rule #1 - under-population - this cell will die + // mark it zero to mark the change + nextCells[i][j] = 0; + } else if (liveNeighbours < 4) { + // rule #2 - this cell lives + // mark it -1 to mark no change + nextCells[i][j] = -1; + } else { + // rule #3 - overcrowding - this cell dies + // mark it zero to mark the change + nextCells[i][j] = 0; + } + } else { + // dead cell + if (liveNeighbours == 3) { + // rule #4 - reproduction - this cell revives + // mark it one to mark the change + nextCells[i][j] = 1; + } else { + // this cell stays dead + // mark it -1 for no change + nextCells[i][j] = -1; + } + } + + if (Math.random() < 0.001) { + // Random mutation to keep things interesting in there. + nextCells[i][j] = 1; + } + } + } + + for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + if (nextCells[i][j] != -1) { + // there has been a change to this cell, change the value in the currentCells array + currentCells[i][j] = nextCells[i][j]; + } + } + } +} + +function sendNextCells() { + for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + if (nextCells[i][j] != -1) { + // there has been a change to the state of this cell, send it + + // find the x and y position for this voxel, z = 0 + var x = j * cellScale; + var y = i * cellScale; + + // queue a packet to add a voxel for the new cell + var color = (nextCells[i][j] == 1) ? 255 : 1; + Voxels.setVoxel(x, y, 0, cellScale, color, color, color); + } + } + } +} + +var sentFirstBoard = false; + +function step() { +print("step()..."); + if (sentFirstBoard) { + // we've already sent the first full board, perform a step in time + updateCells(); + } else { + // this will be our first board send + sentFirstBoard = true; + } + + sendNextCells(); +} + +print("here"); +Script.willSendVisualDataCallback.connect(step); +Voxels.setPacketsPerSecond(200); +print("now here"); diff --git a/examples/globalCollisionsExample.js b/examples/globalCollisionsExample.js new file mode 100644 index 0000000000..4db4c808e5 --- /dev/null +++ b/examples/globalCollisionsExample.js @@ -0,0 +1,26 @@ +// +// globalCollisionsExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 1/29/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates use of the Controller class +// +// + + +function particleCollisionWithVoxel(particle, voxel) { + print("particleCollisionWithVoxel().."); + print(" particle.getID()=" + particle.id); + print(" voxel color...=" + voxel.red + ", " + voxel.green + ", " + voxel.blue); +} + +function particleCollisionWithParticle(particleA, particleB) { + print("particleCollisionWithParticle().."); + print(" particleA.getID()=" + particleA.id); + print(" particleB.getID()=" + particleB.id); +} + +Particles.particleCollisionWithVoxel.connect(particleCollisionWithVoxel); +Particles.particleCollisionWithParticle.connect(particleCollisionWithParticle); diff --git a/examples/gun.js b/examples/gun.js index 30d2b41449..e7cd2973e2 100644 --- a/examples/gun.js +++ b/examples/gun.js @@ -72,13 +72,13 @@ function checkController() { " function collisionWithVoxel(voxel) { " + " print('collisionWithVoxel(voxel)... '); " + " print('myID=' + Particle.getID() + '\\n'); " + - " var voxelColor = voxel.getColor();" + - " print('voxelColor=' + voxelColor.red + ', ' + voxelColor.green + ', ' + voxelColor.blue + '\\n'); " + + " var voxelColor = { red: voxel.red, green: voxel.green, blue: voxel.blue };" + + " var voxelAt = { x: voxel.x, y: voxel.y, z: voxel.z };" + + " var voxelScale = voxel.s;" + + " print('voxelColor=' + voxel.red + ', ' + voxel.green + ', ' + voxel.blue + '\\n'); " + " var myColor = Particle.getColor();" + " print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); " + " Particle.setColor(voxelColor); " + - " var voxelAt = voxel.getPosition();" + - " var voxelScale = voxel.getScale();" + " Voxels.eraseVoxel(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale); " + " print('Voxels.eraseVoxel(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " + " } " + @@ -99,4 +99,4 @@ function checkController() { // register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(checkController); +Script.willSendVisualDataCallback.connect(checkController); diff --git a/examples/lookWithMouse.js b/examples/lookWithMouse.js new file mode 100644 index 0000000000..460663c054 --- /dev/null +++ b/examples/lookWithMouse.js @@ -0,0 +1,95 @@ +// +// lookWithMouse.js +// hifi +// +// Created by Brad Hefta-Gaub on 1/28/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates use of the Controller class +// +// + +var alwaysLook = true; // if you want the mouse look to happen only when you click, change this to false +var isMouseDown = false; +var lastX = 0; +var lastY = 0; +var yawFromMouse = 0; +var pitchFromMouse = 0; +var wantDebugging = false; + +function mousePressEvent(event) { + if (wantDebugging) { + print("mousePressEvent event.x,y=" + event.x + ", " + event.y); + } + isMouseDown = true; + lastX = event.x; + lastY = event.y; +} + +function mouseReleaseEvent(event) { + if (wantDebugging) { + print("mouseReleaseEvent event.x,y=" + event.x + ", " + event.y); + } + isMouseDown = false; +} + +function mouseMoveEvent(event) { + if (wantDebugging) { + print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y); + } + + if (alwaysLook || isMouseDown) { + var MOUSE_YAW_SCALE = -0.25; + var MOUSE_PITCH_SCALE = -12.5; + var FIXED_MOUSE_TIMESTEP = 0.016; + yawFromMouse += ((event.x - lastX) * MOUSE_YAW_SCALE * FIXED_MOUSE_TIMESTEP); + pitchFromMouse += ((event.y - lastY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP); + lastX = event.x; + lastY = event.y; + } +} + +function update() { + if (wantDebugging) { + print("update()..."); + } + // rotate body yaw for yaw received from mouse + var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMouse, z: 0 } )); + if (wantDebugging) { + print("changing orientation" + + " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + "," + + MyAvatar.orientation.z + "," + MyAvatar.orientation.w + + " newOrientation="+newOrientation.x + "," + newOrientation.y + "," + newOrientation.z + "," + newOrientation.w); + } + MyAvatar.orientation = newOrientation; + yawFromMouse = 0; + + // apply pitch from mouse + var newPitch = MyAvatar.headPitch + pitchFromMouse; + if (wantDebugging) { + print("changing pitch [old]MyAvatar.headPitch="+MyAvatar.headPitch+ " newPitch="+newPitch); + } + MyAvatar.headPitch = newPitch; + pitchFromMouse = 0; +} + +// Map the mouse events to our functions +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); + +// disable the standard application for mouse events +Controller.captureMouseEvents(); + +function scriptEnding() { + // re-enabled the standard application for mouse events + Controller.releaseMouseEvents(); +} + +MyAvatar.bodyYaw = 0; +MyAvatar.bodyPitch = 0; +MyAvatar.bodyRoll = 0; + +// would be nice to change to update +Script.willSendVisualDataCallback.connect(update); +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/lookWithTouch.js b/examples/lookWithTouch.js new file mode 100644 index 0000000000..bb54a055c4 --- /dev/null +++ b/examples/lookWithTouch.js @@ -0,0 +1,86 @@ +// +// lookWithTouch.js +// hifi +// +// Created by Brad Hefta-Gaub on 1/28/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates use of the Controller class +// +// + +var lastX = 0; +var lastY = 0; +var yawFromMouse = 0; +var pitchFromMouse = 0; +var wantDebugging = false; + +function touchBeginEvent(event) { + if (wantDebugging) { + print("touchBeginEvent event.x,y=" + event.x + ", " + event.y); + } + lastX = event.x; + lastY = event.y; +} + +function touchEndEvent(event) { + if (wantDebugging) { + print("touchEndEvent event.x,y=" + event.x + ", " + event.y); + } +} + +function touchUpdateEvent(event) { + if (wantDebugging) { + print("touchUpdateEvent event.x,y=" + event.x + ", " + event.y); + } + + var MOUSE_YAW_SCALE = -0.25; + var MOUSE_PITCH_SCALE = -12.5; + var FIXED_MOUSE_TIMESTEP = 0.016; + yawFromMouse += ((event.x - lastX) * MOUSE_YAW_SCALE * FIXED_MOUSE_TIMESTEP); + pitchFromMouse += ((event.y - lastY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP); + lastX = event.x; + lastY = event.y; +} + +function update() { + // rotate body yaw for yaw received from mouse + var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMouse, z: 0 } )); + if (wantDebugging) { + print("changing orientation" + + " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + "," + + MyAvatar.orientation.z + "," + MyAvatar.orientation.w + + " newOrientation="+newOrientation.x + "," + newOrientation.y + "," + newOrientation.z + "," + newOrientation.w); + } + MyAvatar.orientation = newOrientation; + yawFromMouse = 0; + + // apply pitch from mouse + var newPitch = MyAvatar.headPitch + pitchFromMouse; + if (wantDebugging) { + print("changing pitch [old]MyAvatar.headPitch="+MyAvatar.headPitch+ " newPitch="+newPitch); + } + MyAvatar.headPitch = newPitch; + pitchFromMouse = 0; +} + +// Map the mouse events to our functions +Controller.touchBeginEvent.connect(touchBeginEvent); +Controller.touchUpdateEvent.connect(touchUpdateEvent); +Controller.touchEndEvent.connect(touchEndEvent); + +// disable the standard application for mouse events +Controller.captureTouchEvents(); + +function scriptEnding() { + // re-enabled the standard application for mouse events + Controller.releaseTouchEvents(); +} + +MyAvatar.bodyYaw = 0; +MyAvatar.bodyPitch = 0; +MyAvatar.bodyRoll = 0; + +// would be nice to change to update +Script.willSendVisualDataCallback.connect(update); +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/movingVoxel.js b/examples/movingVoxel.js index 0aadf7b30c..14a7e671c6 100644 --- a/examples/movingVoxel.js +++ b/examples/movingVoxel.js @@ -41,4 +41,4 @@ function moveVoxel() { Voxels.setPacketsPerSecond(300); // Connect a call back that happens every frame -Agent.willSendVisualDataCallback.connect(moveVoxel); \ No newline at end of file +Script.willSendVisualDataCallback.connect(moveVoxel); \ No newline at end of file diff --git a/examples/paintGun.js b/examples/paintGun.js index 0cbe3ff366..56e916a183 100644 --- a/examples/paintGun.js +++ b/examples/paintGun.js @@ -65,13 +65,13 @@ function checkController() { " function collisionWithVoxel(voxel) { " + " print('collisionWithVoxel(voxel)... '); " + " print('myID=' + Particle.getID() + '\\n'); " + - " var voxelColor = voxel.getColor();" + + " var voxelColor = { red: voxel.red, green: voxel.green, blue: voxel.blue };" + + " var voxelAt = { x: voxel.x, y: voxel.y, z: voxel.z };" + + " var voxelScale = voxel.s;" + " print('voxelColor=' + voxelColor.red + ', ' + voxelColor.green + ', ' + voxelColor.blue + '\\n'); " + " var myColor = Particle.getColor();" + " print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); " + " Particle.setColor(voxelColor); " + - " var voxelAt = voxel.getPosition();" + - " var voxelScale = voxel.getScale();" + " Voxels.setVoxel(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale, 255, 255, 0); " + " print('Voxels.setVoxel(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " + " } " + @@ -93,4 +93,4 @@ function checkController() { // register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(checkController); +Script.willSendVisualDataCallback.connect(checkController); diff --git a/examples/particleBird.js b/examples/particleBird.js index 036b86bf0b..440a91166c 100644 --- a/examples/particleBird.js +++ b/examples/particleBird.js @@ -104,8 +104,9 @@ function moveBird() { // check to see if we've been running long enough that our bird is dead var nowTimeInSeconds = new Date().getTime() / 1000; if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) { - print("This bird has died, how sad."); - Agent.stop(); + + print("our bird is dying, stop our script"); + Script.stop(); return; } @@ -193,4 +194,4 @@ function moveBird() { } // register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(moveBird); +Script.willSendVisualDataCallback.connect(moveBird); diff --git a/examples/particleModelExample.js b/examples/particleModelExample.js new file mode 100644 index 0000000000..e95cc0c2bf --- /dev/null +++ b/examples/particleModelExample.js @@ -0,0 +1,51 @@ +// +// particleModelExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 1/28/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates creating and editing a particle +// + +var count = 0; +var stopAfter = 100; + +var modelProperties = { + position: { x: 1, y: 1, z: 1 }, + velocity: { x: 0.5, y: 0, z: 0.5 }, + gravity: { x: 0, y: 0, z: 0 }, + damping: 0, + radius : 0.25, + modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX", + lifetime: 20 +}; + +var ballProperties = { + position: { x: 1, y: 0.5, z: 1 }, + velocity: { x: 0.5, y: 0, z: 0.5 }, + gravity: { x: 0, y: 0, z: 0 }, + damping: 0, + radius : 0.25, + color: { red: 255, green: 0, blue: 0 }, + lifetime: 20 +}; + +var modelParticleID = Particles.addParticle(modelProperties); +var ballParticleID = Particles.addParticle(ballProperties); + +function endAfterAWhile() { + // stop it... + if (count >= stopAfter) { + print("calling Script.stop()"); + Script.stop(); + } + + print("count =" + count); + count++; +} + + +// register the call back so it fires before each data send +Script.willSendVisualDataCallback.connect(endAfterAWhile); + diff --git a/examples/playSound.js b/examples/playSound.js index 6631d5526a..657f052aa5 100644 --- a/examples/playSound.js +++ b/examples/playSound.js @@ -17,4 +17,4 @@ function maybePlaySound() { } // Connect a call back that happens every frame -Agent.willSendVisualDataCallback.connect(maybePlaySound); \ No newline at end of file +Script.willSendVisualDataCallback.connect(maybePlaySound); \ No newline at end of file diff --git a/examples/rideAlongWithAParticleExample.js b/examples/rideAlongWithAParticleExample.js new file mode 100644 index 0000000000..b2b6627063 --- /dev/null +++ b/examples/rideAlongWithAParticleExample.js @@ -0,0 +1,50 @@ +// +// rideAlongWithAParticleExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 1/24/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates "finding" particles +// + +var iteration = 0; +var lengthOfRide = 2000; // in iterations + +var particleA = Particles.addParticle( + { + position: { x: 10, y: 0, z: 10 }, + velocity: { x: 5, y: 0, z: 5 }, + gravity: { x: 0, y: 0, z: 0 }, + radius : 0.1, + color: { red: 0, green: 255, blue: 0 }, + damping: 0, + lifetime: (lengthOfRide * 60) + 1 + }); + +function rideWithParticle() { + + if (iteration <= lengthOfRide) { + + // Check to see if we've been notified of the actual identity of the particles we created + if (!particleA.isKnownID) { + particleA = Particles.identifyParticle(particleA); + } + + var propertiesA = Particles.getParticleProperties(particleA); + var newPosition = propertiesA.position; + MyAvatar.position = { x: propertiesA.position.x, + y: propertiesA.position.y + 2, + z: propertiesA.position.z }; + } else { + Script.stop(); + } + + iteration++; + print("iteration="+iteration); +} + + +// register the call back so it fires before each data send +Script.willSendVisualDataCallback.connect(rideWithParticle); + diff --git a/examples/spaceInvadersExample.js b/examples/spaceInvadersExample.js new file mode 100644 index 0000000000..3a13a1c52f --- /dev/null +++ b/examples/spaceInvadersExample.js @@ -0,0 +1,246 @@ +// +// spaceInvadersExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 1/30/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates a simple space invaders style of game +// + +var iteration = 0; + +var invaderStepsPerCycle = 30; // the number of update steps it takes then invaders to move one column to the right +var invaderStepOfCycle = 0; // current iteration in the cycle +var invaderMoveDirection = 1; // 1 for moving to right, -1 for moving to left + +var itemLifetimes = 60; +var gameAt = { x: 10, y: 0, z: 10 }; +var gameSize = { x: 10, y: 20, z: 1 }; +var middleX = gameAt.x + (gameSize.x/2); +var middleY = gameAt.y + (gameSize.y/2); + +var shipSize = 0.2; +var missileSize = 0.1; +var myShip; +var myShipProperties; + +// create the rows of space invaders +var invaders = new Array(); +var numberOfRows = 5; +var invadersPerRow = 8; +var emptyColumns = 2; // number of invader width columns not filled with invaders +var invadersBottomCorner = { x: gameAt.x, y: middleY , z: gameAt.z }; +var rowHeight = ((gameAt.y + gameSize.y) - invadersBottomCorner.y) / numberOfRows; +var columnWidth = gameSize.x / (invadersPerRow + emptyColumns); + +var missileFired = false; +var myMissile; + +function initializeMyShip() { + myShipProperties = { + position: { x: middleX , y: gameAt.y, z: gameAt.z }, + velocity: { x: 0, y: 0, z: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + damping: 0, + radius: shipSize, + color: { red: 0, green: 255, blue: 0 }, + lifetime: itemLifetimes + }; + myShip = Particles.addParticle(myShipProperties); +} + +// calculate the correct invaderPosition for an column row +function getInvaderPosition(row, column) { + var xMovePart = 0; + var xBasePart = invadersBottomCorner.x + (column * columnWidth); + if (invaderMoveDirection > 0) { + xMovePart = (invaderMoveDirection * columnWidth * (invaderStepOfCycle/invaderStepsPerCycle)); + } else { + xMovePart = columnWidth + (invaderMoveDirection * columnWidth * (invaderStepOfCycle/invaderStepsPerCycle)); + } + var invaderPosition = { + x: xBasePart + xMovePart, + y: invadersBottomCorner.y + (row * rowHeight), + z: invadersBottomCorner.z }; + + return invaderPosition; +} + +function initializeInvaders() { + for (var row = 0; row < numberOfRows; row++) { + invaders[row] = new Array(); + for (var column = 0; column < invadersPerRow; column++) { + invaderPosition = getInvaderPosition(row, column); + invaders[row][column] = Particles.addParticle({ + position: invaderPosition, + velocity: { x: 0, y: 0, z: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + damping: 0, + radius: shipSize, + color: { red: 255, green: 0, blue: 0 }, + lifetime: itemLifetimes + }); + + print("invaders[row][column].creatorTokenID=" + invaders[row][column].creatorTokenID); + } + } +} + +function moveInvaders() { + print("moveInvaders()..."); + for (var row = 0; row < numberOfRows; row++) { + for (var column = 0; column < invadersPerRow; column++) { + props = Particles.getParticleProperties(invaders[row][column]); + if (props.isKnownID) { + invaderPosition = getInvaderPosition(row, column); + Particles.editParticle(invaders[row][column], { position: invaderPosition }); + } + } + } +} + +function update() { + print("updating space invaders... iteration="+iteration); + iteration++; + invaderStepOfCycle++; + if (invaderStepOfCycle > invaderStepsPerCycle) { + invaderStepOfCycle = 0; + if (invaderMoveDirection > 0) { + invaderMoveDirection = -1; + } else { + invaderMoveDirection = 1; + } + } + moveInvaders(); +} + +// register the call back so it fires before each data send +Script.willSendVisualDataCallback.connect(update); + +function cleanupGame() { + print("cleaning up game..."); + Particles.deleteParticle(myShip); + print("cleanupGame() ... Particles.deleteParticle(myShip)... myShip.id="+myShip.id); + for (var row = 0; row < numberOfRows; row++) { + for (var column = 0; column < invadersPerRow; column++) { + Particles.deleteParticle(invaders[row][column]); + print("cleanupGame() ... Particles.deleteParticle(invaders[row][column])... invaders[row][column].id=" + +invaders[row][column].id); + } + } + + // clean up our missile + if (missileFired) { + Particles.deleteParticle(myMissile); + } + Script.stop(); +} +Script.scriptEnding.connect(cleanupGame); + +function endGame() { + print("ending game..."); + Script.stop(); +} + +function moveShipTo(position) { + myShip = Particles.identifyParticle(myShip); + Particles.editParticle(myShip, { position: position }); +} + +function fireMissile() { + // we only allow one missile at a time... + var canFire = false; + + // If we've fired a missile, then check to see if it's still alive + if (missileFired) { + var missileProperties = Particles.getParticleProperties(myMissile); + print("missileProperties.isKnownID=" + missileProperties.isKnownID); + + if (!missileProperties.isKnownID) { + print("canFire = true"); + canFire = true; + } + } else { + canFire = true; + } + + if (canFire) { + print("firing missile"); + var missilePosition = { x: myShipProperties.position.x, + y: myShipProperties.position.y + (2* shipSize), + z: myShipProperties.position.z }; + + myMissile = Particles.addParticle( + { + position: missilePosition, + velocity: { x: 0, y: 5, z: 0}, + gravity: { x: 0, y: 0, z: 0 }, + damping: 0, + radius: missileSize, + color: { red: 0, green: 0, blue: 255 }, + lifetime: 5 + }); + + missileFired = true; + } +} + +function keyPressEvent(key) { + //print("keyPressEvent key.text="+key.text); + if (key.text == ",") { + myShipProperties.position.x -= 0.1; + if (myShipProperties.position.x < gameAt.x) { + myShipProperties.position.x = gameAt.x; + } + moveShipTo(myShipProperties.position); + } else if (key.text == ".") { + myShipProperties.position.x += 0.1; + if (myShipProperties.position.x > gameAt.x + gameSize.x) { + myShipProperties.position.x = gameAt.x + gameSize.x; + } + moveShipTo(myShipProperties.position); + } else if (key.text == " ") { + fireMissile(); + } else if (key.text == "q") { + endGame(); + } +} + +// remap the keys... +Controller.keyPressEvent.connect(keyPressEvent); +Controller.captureKeyEvents({text: " "}); + +function deleteIfInvader(possibleInvaderParticle) { + for (var row = 0; row < numberOfRows; row++) { + for (var column = 0; column < invadersPerRow; column++) { + invaders[row][column] = Particles.identifyParticle(invaders[row][column]); + if (invaders[row][column].isKnownID) { + if (invaders[row][column].id == possibleInvaderParticle.id) { + Particles.deleteParticle(possibleInvaderParticle); + Particles.deleteParticle(myMissile); + } + } + } + } +} + +function particleCollisionWithParticle(particleA, particleB) { + print("particleCollisionWithParticle() a.id="+particleA.id + " b.id=" + particleB.id); + if (missileFired) { + myMissile = Particles.identifyParticle(myMissile); + if (myMissile.id == particleA.id) { + deleteIfInvader(particleB); + } else if (myMissile.id == particleB.id) { + deleteIfInvader(particleA); + } + } +} +Particles.particleCollisionWithParticle.connect(particleCollisionWithParticle); + + +// initialize the game... +initializeMyShip(); +initializeInvaders(); + + diff --git a/examples/timer.js b/examples/timer.js new file mode 100644 index 0000000000..c7ad0290ab --- /dev/null +++ b/examples/timer.js @@ -0,0 +1,8 @@ +var one_timer = Script.setTimeout(function() { print("One time timer fired!"); }, 10000); +var multiple_timer = Script.setInterval(function() { print("Repeating timer fired!"); }, 1000); + +// this would stop a scheduled single shot timer +Script.clearTimeout(one_timer); + +// this stops the repeating timer +Script.clearInterval(multiple_timer); \ No newline at end of file diff --git a/examples/toyball.js b/examples/toyball.js index 6c40fc2932..c5672877f7 100644 --- a/examples/toyball.js +++ b/examples/toyball.js @@ -226,4 +226,4 @@ function checkController() { // register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(checkController); +Script.willSendVisualDataCallback.connect(checkController); diff --git a/examples/voxelBird.js b/examples/voxelBird.js index 54c0129045..254f93c21e 100644 --- a/examples/voxelBird.js +++ b/examples/voxelBird.js @@ -130,4 +130,4 @@ function moveBird() { Voxels.setPacketsPerSecond(10000); // Connect a call back that happens every frame -Agent.willSendVisualDataCallback.connect(moveBird); \ No newline at end of file +Script.willSendVisualDataCallback.connect(moveBird); \ No newline at end of file diff --git a/interface/resources/shaders/model.frag b/interface/resources/shaders/model.frag index 877cdca885..e035f9bfe9 100644 --- a/interface/resources/shaders/model.frag +++ b/interface/resources/shaders/model.frag @@ -25,5 +25,5 @@ void main(void) { // modulate texture by base color and add specular contribution gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + - pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular; + vec4(pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular.rgb, 0.0); } diff --git a/interface/resources/shaders/model_normal_map.frag b/interface/resources/shaders/model_normal_map.frag index 9740a4d4b1..9a0e964500 100644 --- a/interface/resources/shaders/model_normal_map.frag +++ b/interface/resources/shaders/model_normal_map.frag @@ -37,5 +37,5 @@ void main(void) { // modulate texture by base color and add specular contribution gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + - pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular; + vec4(pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular.rgb, 0.0); } diff --git a/interface/resources/sounds/snap.wav b/interface/resources/sounds/snap.wav new file mode 100644 index 0000000000..bb562e1db9 Binary files /dev/null and b/interface/resources/sounds/snap.wav differ diff --git a/interface/src/AbstractLoggerInterface.h b/interface/src/AbstractLoggerInterface.h index d8a48fc2fd..cedab1fad2 100644 --- a/interface/src/AbstractLoggerInterface.h +++ b/interface/src/AbstractLoggerInterface.h @@ -17,6 +17,7 @@ class AbstractLoggerInterface : public QObject { Q_OBJECT public: + AbstractLoggerInterface(QObject* parent = NULL) : QObject(parent) {}; inline bool extraDebugging() { return _extraDebugging; }; inline void setExtraDebugging(bool debugging) { _extraDebugging = debugging; }; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 285a395c07..ca87c812d3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -47,9 +47,9 @@ #include #include #include +#include #include -#include #include #include #include @@ -70,6 +70,7 @@ #include "renderer/ProgramObject.h" #include "ui/TextRenderer.h" #include "InfoView.h" +#include "ui/Snapshot.h" using namespace std; @@ -110,15 +111,16 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : QApplication(argc, argv), _window(new QMainWindow(desktop())), _glWidget(new GLCanvas()), + _statsExpanded(false), _nodeThread(new QThread(this)), _datagramProcessor(), _frameCount(0), _fps(120.0f), _justStarted(true), - _voxelImporter(_window), + _voxelImporter(NULL), _wantToKillLocalVoxels(false), _audioScope(256, 200, true), - _avatarManager(), + _myAvatar(), _profile(QString()), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _mouseX(0), @@ -129,8 +131,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _touchAvgX(0.0f), _touchAvgY(0.0f), _isTouchPressed(false), - _yawFromTouch(0.0f), - _pitchFromTouch(0.0f), _mousePressed(false), _isHoverVoxel(false), _isHoverVoxelSounding(false), @@ -152,10 +152,11 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _resetRecentMaxPacketsSoon(true), _swatch(NULL), _pasteMode(false), - _logger(new FileLogger()), - _persistThread(NULL), - _statsExpanded(false) + _logger(new FileLogger(this)), + _persistThread(NULL) { + _myAvatar = _avatarManager.getMyAvatar(); + _applicationStartupTime = startup_time; switchToResourcesParentIfRequired(); @@ -181,7 +182,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _nodeThread->setPriority(QThread::TimeCriticalPriority); // put the NodeList and datagram processing on the node thread - NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AGENT, listenPort); + NodeList* nodeList = NodeList::createInstance(NodeType::Agent, listenPort); nodeList->moveToThread(_nodeThread); _datagramProcessor.moveToThread(_nodeThread); @@ -228,9 +229,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : #endif // tell the NodeList instance who to tell the domain server we care about - nodeList->addSetOfNodeTypesToNodeInterestSet(QSet() << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER - << NODE_TYPE_VOXEL_SERVER << NODE_TYPE_PARTICLE_SERVER - << NODE_TYPE_METAVOXEL_SERVER); + nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer + << NodeType::VoxelServer << NodeType::ParticleServer + << NodeType::MetavoxelServer); // connect to the packet sent signal of the _voxelEditSender and the _particleEditSender connect(&_voxelEditSender, &VoxelEditPacketSender::packetSent, this, &Application::packetSent); @@ -252,7 +253,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _window->setCentralWidget(_glWidget); restoreSizeAndPosition(); - loadScripts(); QFontDatabase fontDatabase; fontDatabase.addApplicationFont("resources/styles/Inconsolata.otf"); @@ -282,6 +282,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense)); checkVersion(); + + // do this as late as possible so that all required subsystems are inialized + loadScripts(); } Application::~Application() { @@ -316,7 +319,7 @@ Application::~Application() { _persistThread->deleteLater(); _persistThread = NULL; } - + storeSizeAndPosition(); saveScripts(); _sharedVoxelSystem.changeTree(new VoxelTree); @@ -324,8 +327,8 @@ Application::~Application() { VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown Menu::getInstance()->deleteLater(); - delete _logger; - delete _settings; + _myAvatar = NULL; + delete _glWidget; } @@ -438,25 +441,25 @@ void Application::paintGL() { _myCamera.setUpShift (0.0f); _myCamera.setDistance (0.0f); _myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing - _myCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition()); - _myCamera.setTargetRotation(_myAvatar.getHead().getOrientation()); + _myCamera.setTargetPosition(_myAvatar->getHead().calculateAverageEyePosition()); + _myCamera.setTargetRotation(_myAvatar->getHead().getOrientation()); } else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { _myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay - _myCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition()); - _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation()); + _myCamera.setTargetPosition(_myAvatar->getHead().calculateAverageEyePosition()); + _myCamera.setTargetRotation(_myAvatar->getHead().getCameraOrientation()); } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { _myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing - _myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition()); - _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation()); + _myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition()); + _myCamera.setTargetRotation(_myAvatar->getHead().getCameraOrientation()); } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myCamera.setTightness(0.0f); - float headHeight = _myAvatar.getHead().calculateAverageEyePosition().y - _myAvatar.getPosition().y; - _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar.getScale()); - _myCamera.setTargetPosition(_myAvatar.getPosition() + glm::vec3(0, headHeight, 0)); - _myCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); + float headHeight = _myAvatar->getHead().calculateAverageEyePosition().y - _myAvatar->getPosition().y; + _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar->getScale()); + _myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight, 0)); + _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); } // Update camera position @@ -513,22 +516,22 @@ void Application::paintGL() { bool eyeRelativeCamera = false; if (_rearMirrorTools->getZoomLevel() == BODY) { - _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar.getScale()); - _mirrorCamera.setTargetPosition(_myAvatar.getChestPosition()); + _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); + _mirrorCamera.setTargetPosition(_myAvatar->getChestPosition()); } else { // HEAD zoom level - _mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar.getScale()); - if (_myAvatar.getSkeletonModel().isActive() && _myAvatar.getHead().getFaceModel().isActive()) { + _mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); + if (_myAvatar->getSkeletonModel().isActive() && _myAvatar->getHead().getFaceModel().isActive()) { // as a hack until we have a better way of dealing with coordinate precision issues, reposition the // face/body so that the average eye position lies at the origin eyeRelativeCamera = true; _mirrorCamera.setTargetPosition(glm::vec3()); } else { - _mirrorCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition()); + _mirrorCamera.setTargetPosition(_myAvatar->getHead().calculateAverageEyePosition()); } } - _mirrorCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); + _mirrorCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); _mirrorCamera.update(1.0f/_fps); // set the bounds of rear mirror view @@ -545,27 +548,27 @@ void Application::paintGL() { glPushMatrix(); if (eyeRelativeCamera) { // save absolute translations - glm::vec3 absoluteSkeletonTranslation = _myAvatar.getSkeletonModel().getTranslation(); - glm::vec3 absoluteFaceTranslation = _myAvatar.getHead().getFaceModel().getTranslation(); + glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation(); + glm::vec3 absoluteFaceTranslation = _myAvatar->getHead().getFaceModel().getTranslation(); // get the eye positions relative to the neck and use them to set the face translation glm::vec3 leftEyePosition, rightEyePosition; - _myAvatar.getHead().getFaceModel().setTranslation(glm::vec3()); - _myAvatar.getHead().getFaceModel().getEyePositions(leftEyePosition, rightEyePosition); - _myAvatar.getHead().getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f); + _myAvatar->getHead().getFaceModel().setTranslation(glm::vec3()); + _myAvatar->getHead().getFaceModel().getEyePositions(leftEyePosition, rightEyePosition); + _myAvatar->getHead().getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f); // get the neck position relative to the body and use it to set the skeleton translation glm::vec3 neckPosition; - _myAvatar.getSkeletonModel().setTranslation(glm::vec3()); - _myAvatar.getSkeletonModel().getNeckPosition(neckPosition); - _myAvatar.getSkeletonModel().setTranslation(_myAvatar.getHead().getFaceModel().getTranslation() - + _myAvatar->getSkeletonModel().setTranslation(glm::vec3()); + _myAvatar->getSkeletonModel().getNeckPosition(neckPosition); + _myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead().getFaceModel().getTranslation() - neckPosition); displaySide(_mirrorCamera, true); // restore absolute translations - _myAvatar.getSkeletonModel().setTranslation(absoluteSkeletonTranslation); - _myAvatar.getHead().getFaceModel().setTranslation(absoluteFaceTranslation); + _myAvatar->getSkeletonModel().setTranslation(absoluteSkeletonTranslation); + _myAvatar->getHead().getFaceModel().setTranslation(absoluteFaceTranslation); } else { displaySide(_mirrorCamera, true); } @@ -647,45 +650,51 @@ void Application::resetProfile(const QString& username) { updateWindowTitle(); } -void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, - const QSet& destinationNodeTypes) { - foreach(NODE_TYPE type, destinationNodeTypes) { +void Application::controlledBroadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes) { + foreach(NodeType_t type, destinationNodeTypes) { // Intercept data to voxel server when voxels are disabled - if (type == NODE_TYPE_VOXEL_SERVER && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { + if (type == NodeType::VoxelServer && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { continue; } // Perform the broadcast for one type - int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(broadcastData, dataBytes, - QSet() << type); + int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(packet, NodeSet() << type); // Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise) BandwidthMeter::ChannelIndex channel; switch (type) { - case NODE_TYPE_AGENT: - case NODE_TYPE_AVATAR_MIXER: + case NodeType::Agent: + case NodeType::AvatarMixer: channel = BandwidthMeter::AVATARS; break; - case NODE_TYPE_VOXEL_SERVER: + case NodeType::VoxelServer: channel = BandwidthMeter::VOXELS; break; default: continue; } - _bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes); + _bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * packet.size()); } } void Application::keyPressEvent(QKeyEvent* event) { + + _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isKeyCaptured(event)) { + return; + } + if (activeWindow() == _window) { if (_chatEntryOn) { if (_chatEntry.keyPressEvent(event)) { - _myAvatar.setKeyState(event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete ? + _myAvatar->setKeyState(event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete ? DELETE_KEY_DOWN : INSERT_KEY_DOWN); - _myAvatar.setChatMessage(string(_chatEntry.getContents().size(), SOLID_BLOCK_CHAR)); + _myAvatar->setChatMessage(string(_chatEntry.getContents().size(), SOLID_BLOCK_CHAR)); } else { - _myAvatar.setChatMessage(_chatEntry.getContents()); + _myAvatar->setChatMessage(_chatEntry.getContents()); _chatEntry.clear(); _chatEntryOn = false; setMenuShortcutsEnabled(true); @@ -729,10 +738,10 @@ void Application::keyPressEvent(QKeyEvent* event) { if (_nudgeStarted) { _nudgeGuidePosition.y += _mouseVoxel.s; } else { - if (!_myAvatar.getDriveKeys(UP)) { - _myAvatar.jump(); + if (!_myAvatar->getDriveKeys(UP)) { + _myAvatar->jump(); } - _myAvatar.setDriveKeys(UP, 1); + _myAvatar->setDriveKeys(UP, 1); } break; @@ -744,7 +753,7 @@ void Application::keyPressEvent(QKeyEvent* event) { if (_nudgeStarted) { _nudgeGuidePosition.y -= _mouseVoxel.s; } else { - _myAvatar.setDriveKeys(DOWN, 1); + _myAvatar->setDriveKeys(DOWN, 1); } break; @@ -764,7 +773,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } } } else { - _myAvatar.setDriveKeys(FWD, 1); + _myAvatar->setDriveKeys(FWD, 1); } break; @@ -773,6 +782,8 @@ void Application::keyPressEvent(QKeyEvent* event) { _voxels.collectStatsForTreesAndVBOs(); } else if (isShifted && isMeta) { Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings); + } else if (!isShifted && isMeta) { + takeSnapshot(); } else if (_nudgeStarted) { if (_lookingAlongX) { if (_lookingAwayFromOrigin) { @@ -788,7 +799,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } } } else { - _myAvatar.setDriveKeys(BACK, 1); + _myAvatar->setDriveKeys(BACK, 1); } break; @@ -822,7 +833,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } } } else { - _myAvatar.setDriveKeys(ROT_LEFT, 1); + _myAvatar->setDriveKeys(ROT_LEFT, 1); } break; @@ -842,7 +853,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } } } else { - _myAvatar.setDriveKeys(ROT_RIGHT, 1); + _myAvatar->setDriveKeys(ROT_RIGHT, 1); } break; @@ -852,8 +863,8 @@ void Application::keyPressEvent(QKeyEvent* event) { nudgeVoxels(); } else { _chatEntryOn = true; - _myAvatar.setKeyState(NO_KEY_DOWN); - _myAvatar.setChatMessage(string()); + _myAvatar->setKeyState(NO_KEY_DOWN); + _myAvatar->setChatMessage(string()); setMenuShortcutsEnabled(false); } break; @@ -876,7 +887,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } else if (_nudgeStarted && isShifted) { _nudgeGuidePosition.y += _mouseVoxel.s; } else { - _myAvatar.setDriveKeys(isShifted ? UP : FWD, 1); + _myAvatar->setDriveKeys(isShifted ? UP : FWD, 1); } break; @@ -898,7 +909,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } else if (_nudgeStarted && isShifted) { _nudgeGuidePosition.y -= _mouseVoxel.s; } else { - _myAvatar.setDriveKeys(isShifted ? DOWN : BACK, 1); + _myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1); } break; @@ -918,7 +929,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } } } else { - _myAvatar.setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1); + _myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1); } break; @@ -938,7 +949,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } } } else { - _myAvatar.setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1); + _myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1); } break; @@ -1056,13 +1067,13 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; case Qt::Key_Plus: - _myAvatar.increaseSize(); + _myAvatar->increaseSize(); break; case Qt::Key_Minus: - _myAvatar.decreaseSize(); + _myAvatar->decreaseSize(); break; case Qt::Key_Equal: - _myAvatar.resetSize(); + _myAvatar->resetSize(); break; case Qt::Key_1: @@ -1076,7 +1087,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _swatch.handleEvent(event->key(), Menu::getInstance()->isOptionChecked(MenuOption::VoxelGetColorMode)); break; case Qt::Key_At: - Menu::getInstance()->goToUser(); + Menu::getInstance()->goTo(); break; default: event->ignore(); @@ -1086,9 +1097,18 @@ void Application::keyPressEvent(QKeyEvent* event) { } void Application::keyReleaseEvent(QKeyEvent* event) { + + _controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isKeyCaptured(event)) { + return; + } + + if (activeWindow() == _window) { if (_chatEntryOn) { - _myAvatar.setKeyState(NO_KEY_DOWN); + _myAvatar->setKeyState(NO_KEY_DOWN); return; } @@ -1097,47 +1117,47 @@ void Application::keyReleaseEvent(QKeyEvent* event) { _pasteMode = false; break; case Qt::Key_E: - _myAvatar.setDriveKeys(UP, 0); + _myAvatar->setDriveKeys(UP, 0); break; case Qt::Key_C: - _myAvatar.setDriveKeys(DOWN, 0); + _myAvatar->setDriveKeys(DOWN, 0); break; case Qt::Key_W: - _myAvatar.setDriveKeys(FWD, 0); + _myAvatar->setDriveKeys(FWD, 0); break; case Qt::Key_S: - _myAvatar.setDriveKeys(BACK, 0); + _myAvatar->setDriveKeys(BACK, 0); break; case Qt::Key_A: - _myAvatar.setDriveKeys(ROT_LEFT, 0); + _myAvatar->setDriveKeys(ROT_LEFT, 0); break; case Qt::Key_D: - _myAvatar.setDriveKeys(ROT_RIGHT, 0); + _myAvatar->setDriveKeys(ROT_RIGHT, 0); break; case Qt::Key_Up: - _myAvatar.setDriveKeys(FWD, 0); - _myAvatar.setDriveKeys(UP, 0); + _myAvatar->setDriveKeys(FWD, 0); + _myAvatar->setDriveKeys(UP, 0); break; case Qt::Key_Down: - _myAvatar.setDriveKeys(BACK, 0); - _myAvatar.setDriveKeys(DOWN, 0); + _myAvatar->setDriveKeys(BACK, 0); + _myAvatar->setDriveKeys(DOWN, 0); break; case Qt::Key_Left: - _myAvatar.setDriveKeys(LEFT, 0); - _myAvatar.setDriveKeys(ROT_LEFT, 0); + _myAvatar->setDriveKeys(LEFT, 0); + _myAvatar->setDriveKeys(ROT_LEFT, 0); break; case Qt::Key_Right: - _myAvatar.setDriveKeys(RIGHT, 0); - _myAvatar.setDriveKeys(ROT_RIGHT, 0); + _myAvatar->setDriveKeys(RIGHT, 0); + _myAvatar->setDriveKeys(ROT_RIGHT, 0); break; default: @@ -1148,6 +1168,14 @@ void Application::keyReleaseEvent(QKeyEvent* event) { } void Application::mouseMoveEvent(QMouseEvent* event) { + _controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isMouseCaptured()) { + return; + } + + _lastMouseMove = usecTimestampNow(); if (_mouseHidden) { getGLWidget()->setCursor(Qt::ArrowCursor); @@ -1163,12 +1191,12 @@ void Application::mouseMoveEvent(QMouseEvent* event) { if (activeWindow() == _window) { // orbit behavior if (_mousePressed && !Menu::getInstance()->isVoxelModeActionChecked()) { - if (_avatarManager.getLookAtTargetAvatar()) { - _myAvatar.orbit(_avatarManager.getLookAtTargetAvatar()->getPosition(), deltaX, deltaY); + if (_myAvatar->getLookAtTargetAvatar()) { + _myAvatar->orbit(_myAvatar->getLookAtTargetAvatar()->getPosition(), deltaX, deltaY); return; } if (_isHoverVoxel) { - _myAvatar.orbit(getMouseVoxelWorldCoordinates(_hoverVoxel), deltaX, deltaY); + _myAvatar->orbit(getMouseVoxelWorldCoordinates(_hoverVoxel), deltaX, deltaY); return; } } @@ -1194,6 +1222,14 @@ const float HOVER_VOXEL_FREQUENCY = 7040.f; const float HOVER_VOXEL_DECAY = 0.999f; void Application::mousePressEvent(QMouseEvent* event) { + _controllerScriptingInterface.emitMousePressEvent(event); // send events to any registered scripts + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isMouseCaptured()) { + return; + } + + if (activeWindow() == _window) { if (event->button() == Qt::LeftButton) { _mouseX = event->x(); @@ -1215,7 +1251,7 @@ void Application::mousePressEvent(QMouseEvent* event) { return; } - if (!_palette.isActive() && (!_isHoverVoxel || _avatarManager.getLookAtTargetAvatar())) { + if (!_palette.isActive() && (!_isHoverVoxel || _myAvatar->getLookAtTargetAvatar())) { // disable for now // _pieMenu.mousePressEvent(_mouseX, _mouseY); } @@ -1243,14 +1279,14 @@ void Application::mousePressEvent(QMouseEvent* event) { const float PERCENTAGE_TO_MOVE_TOWARD = 0.90f; glm::vec3 newTarget = getMouseVoxelWorldCoordinates(_hoverVoxel); - glm::vec3 myPosition = _myAvatar.getPosition(); + glm::vec3 myPosition = _myAvatar->getPosition(); // If there is not an action tool set (add, delete, color), move to this voxel if (Menu::getInstance()->isOptionChecked(MenuOption::ClickToFly) && !(Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode) || Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode) || Menu::getInstance()->isOptionChecked(MenuOption::VoxelColorMode))) { - _myAvatar.setMoveTarget(myPosition + (newTarget - myPosition) * PERCENTAGE_TO_MOVE_TOWARD); + _myAvatar->setMoveTarget(myPosition + (newTarget - myPosition) * PERCENTAGE_TO_MOVE_TOWARD); } } @@ -1261,6 +1297,13 @@ void Application::mousePressEvent(QMouseEvent* event) { } void Application::mouseReleaseEvent(QMouseEvent* event) { + _controllerScriptingInterface.emitMouseReleaseEvent(event); // send events to any registered scripts + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isMouseCaptured()) { + return; + } + if (activeWindow() == _window) { if (event->button() == Qt::LeftButton) { _mouseX = event->x(); @@ -1277,6 +1320,13 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { } void Application::touchUpdateEvent(QTouchEvent* event) { + _controllerScriptingInterface.emitTouchUpdateEvent(event); // send events to any registered scripts + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isTouchCaptured()) { + return; + } + bool validTouch = false; if (activeWindow() == _window) { const QList& tPoints = event->touchPoints(); @@ -1301,19 +1351,46 @@ void Application::touchUpdateEvent(QTouchEvent* event) { } void Application::touchBeginEvent(QTouchEvent* event) { + _controllerScriptingInterface.emitTouchBeginEvent(event); // send events to any registered scripts + touchUpdateEvent(event); + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isTouchCaptured()) { + return; + } + + // put any application specific touch behavior below here.. _lastTouchAvgX = _touchAvgX; _lastTouchAvgY = _touchAvgY; + } void Application::touchEndEvent(QTouchEvent* event) { + _controllerScriptingInterface.emitTouchEndEvent(event); // send events to any registered scripts + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isTouchCaptured()) { + return; + } + + // put any application specific touch behavior below here.. _touchDragStartedAvgX = _touchAvgX; _touchDragStartedAvgY = _touchAvgY; _isTouchPressed = false; + } const bool USE_MOUSEWHEEL = false; void Application::wheelEvent(QWheelEvent* event) { + + _controllerScriptingInterface.emitWheelEvent(event); // send events to any registered scripts + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isWheelCaptured()) { + return; + } + // Wheel Events disabled for now because they are also activated by touch look pitch up/down. if (USE_MOUSEWHEEL && (activeWindow() == _window)) { if (!Menu::getInstance()->isVoxelModeActionChecked()) { @@ -1329,13 +1406,11 @@ void Application::wheelEvent(QWheelEvent* event) { } void Application::sendPingPackets() { - unsigned char pingPacket[MAX_PACKET_SIZE]; - int length = NodeList::getInstance()->fillPingPacket(pingPacket); - - getInstance()->controlledBroadcastToNodes(pingPacket, length, QSet() - << NODE_TYPE_VOXEL_SERVER << NODE_TYPE_PARTICLE_SERVER - << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER - << NODE_TYPE_METAVOXEL_SERVER); + QByteArray pingPacket = NodeList::getInstance()->constructPingPacket(); + controlledBroadcastToNodes(pingPacket, NodeSet() << NodeType::VoxelServer + << NodeType::ParticleServer + << NodeType::AudioMixer << NodeType::AvatarMixer + << NodeType::MetavoxelServer); } // Every second, check the frame rates and other stuff @@ -1363,8 +1438,8 @@ void Application::timer() { DataServerClient::resendUnmatchedPackets(); // give the MyAvatar object position, orientation to the Profile so it can propagate to the data-server - _profile.updatePosition(_myAvatar.getPosition()); - _profile.updateOrientation(_myAvatar.getOrientation()); + _profile.updatePosition(_myAvatar->getPosition()); + _profile.updateOrientation(_myAvatar->getOrientation()); } static glm::vec3 getFaceVector(BoxFace face) { @@ -1473,7 +1548,7 @@ void Application::removeVoxel(glm::vec3 position, voxel.y = position.y / TREE_SCALE; voxel.z = position.z / TREE_SCALE; voxel.s = scale / TREE_SCALE; - _voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_VOXEL_ERASE, voxel); + _voxelEditSender.sendVoxelEditMessage(PacketTypeVoxelErase, voxel); // delete it locally to see the effect immediately (and in case no voxel server is present) _voxels.deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s); @@ -1494,7 +1569,7 @@ void Application::makeVoxel(glm::vec3 position, voxel.red = red; voxel.green = green; voxel.blue = blue; - PACKET_TYPE message = isDestructive ? PACKET_TYPE_VOXEL_SET_DESTRUCTIVE : PACKET_TYPE_VOXEL_SET; + PacketType message = isDestructive ? PacketTypeVoxelSetDestructive : PacketTypeVoxelSet; _voxelEditSender.sendVoxelEditMessage(message, voxel); // create the voxel locally so it appears immediately @@ -1564,7 +1639,7 @@ bool Application::sendVoxelsOperation(OctreeElement* element, void* extraData) { codeColorBuffer[bytesInCode + RED_INDEX] = voxel->getColor()[RED_INDEX]; codeColorBuffer[bytesInCode + GREEN_INDEX] = voxel->getColor()[GREEN_INDEX]; codeColorBuffer[bytesInCode + BLUE_INDEX] = voxel->getColor()[BLUE_INDEX]; - getInstance()->_voxelEditSender.queueVoxelEditMessage(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE, + getInstance()->_voxelEditSender.queueVoxelEditMessage(PacketTypeVoxelSetDestructive, codeColorBuffer, codeAndColorLength); delete[] codeColorBuffer; @@ -1592,7 +1667,12 @@ void Application::exportVoxels() { } void Application::importVoxels() { - if (_voxelImporter.exec()) { + if (!_voxelImporter) { + _voxelImporter = new VoxelImporter(_window); + _voxelImporter->init(_settings); + } + + if (_voxelImporter->exec()) { qDebug("[DEBUG] Import succeeded."); } else { qDebug("[DEBUG] Import failed."); @@ -1659,7 +1739,7 @@ void Application::pasteVoxels() { } void Application::findAxisAlignment() { - glm::vec3 direction = _myAvatar.getMouseRayDirection(); + glm::vec3 direction = _myAvatar->getMouseRayDirection(); if (fabs(direction.z) > fabs(direction.x)) { _lookingAlongX = false; if (direction.z < 0) { @@ -1732,8 +1812,6 @@ void Application::init() { _sharedVoxelSystem.changeTree(&_clipboard); delete tmpTree; - _voxelImporter.init(_settings); - _environment.init(); _glowEffect.init(); @@ -1741,15 +1819,14 @@ void Application::init() { _voxelShader.init(); _pointShader.init(); - _headMouseX = _mouseX = _glWidget->width() / 2; - _headMouseY = _mouseY = _glWidget->height() / 2; - QCursor::setPos(_headMouseX, _headMouseY); + _mouseX = _glWidget->width() / 2; + _mouseY = _glWidget->height() / 2; + QCursor::setPos(_mouseX, _mouseY); - _myAvatar.init(); - _myAvatar.setPosition(START_LOCATION); + // TODO: move _myAvatar out of Application. Move relevant code to MyAvataar or AvatarManager + _avatarManager.init(); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _myCamera.setModeShiftRate(1.0f); - _myAvatar.setDisplayingLookatVectors(false); _mirrorCamera.setMode(CAMERA_MODE_MIRROR); _mirrorCamera.setAspectRatio((float)MIRROR_VIEW_WIDTH / (float)MIRROR_VIEW_HEIGHT); @@ -1796,7 +1873,18 @@ void Application::init() { _metavoxels.init(); - _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_myAvatar); + _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_avatarManager); + + // connect the _particleCollisionSystem to our script engine's ParticleScriptingInterface + connect(&_particleCollisionSystem, + SIGNAL(particleCollisionWithVoxel(const ParticleID&, const VoxelDetail&)), + ScriptEngine::getParticlesScriptingInterface(), + SLOT(forwardParticleCollisionWithVoxel(const ParticleID&, const VoxelDetail&))); + + connect(&_particleCollisionSystem, + SIGNAL(particleCollisionWithParticle(const ParticleID&, const ParticleID&)), + ScriptEngine::getParticlesScriptingInterface(), + SLOT(forwardParticleCollisionWithParticle(const ParticleID&, const ParticleID&))); _palette.init(_glWidget->width(), _glWidget->height()); _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0); @@ -1854,9 +1942,9 @@ const float HEAD_SPHERE_RADIUS = 0.07f; bool Application::isLookingAtMyAvatar(Avatar* avatar) { glm::vec3 theirLookat = avatar->getHead().getLookAtPosition(); - glm::vec3 myHeadPosition = _myAvatar.getHead().getPosition(); + glm::vec3 myHeadPosition = _myAvatar->getHead().getPosition(); - if (pointInSphere(theirLookat, myHeadPosition, HEAD_SPHERE_RADIUS * _myAvatar.getScale())) { + if (pointInSphere(theirLookat, myHeadPosition, HEAD_SPHERE_RADIUS * _myAvatar->getScale())) { return true; } return false; @@ -1894,10 +1982,10 @@ void Application::updateMouseRay() { } // tell my avatar if the mouse is being pressed... - _myAvatar.setMousePressed(_mousePressed); + _myAvatar->setMousePressed(_mousePressed); // tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position - _myAvatar.setMouseRay(_mouseRayOrigin, _mouseRayDirection); + _myAvatar->setMouseRay(_mouseRayOrigin, _mouseRayDirection); } void Application::updateFaceshift() { @@ -1910,7 +1998,7 @@ void Application::updateFaceshift() { // Copy angular velocity if measured by faceshift, to the head if (_faceshift.isActive()) { - _myAvatar.getHead().setAngularVelocity(_faceshift.getHeadAngularVelocity()); + _myAvatar->getHead().setAngularVelocity(_faceshift.getHeadAngularVelocity()); } } @@ -1934,14 +2022,14 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot) { } if (_faceshift.isActive()) { // deflect using Faceshift gaze data - glm::vec3 origin = _myAvatar.getHead().calculateAverageEyePosition(); + glm::vec3 origin = _myAvatar->getHead().calculateAverageEyePosition(); float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f; float deflection = Menu::getInstance()->getFaceshiftEyeDeflection(); lookAtSpot = origin + _myCamera.getRotation() * glm::quat(glm::radians(glm::vec3( _faceshift.getEstimatedEyePitch() * pitchSign * deflection, _faceshift.getEstimatedEyeYaw() * deflection, 0.0f))) * glm::inverse(_myCamera.getRotation()) * (lookAtSpot - origin); } - _myAvatar.getHead().setLookAtPosition(lookAtSpot); + _myAvatar->getHead().setLookAtPosition(lookAtSpot); } void Application::updateHoverVoxels(float deltaTime, float& distance, BoxFace& face) { @@ -1999,9 +2087,9 @@ void Application::updateMouseVoxels(float deltaTime, float& distance, BoxFace& f _mouseVoxel.s = 0.0f; bool wasInitialized = _mouseVoxelScaleInitialized; if (Menu::getInstance()->isVoxelModeActionChecked() && - (fabs(_myAvatar.getVelocity().x) + - fabs(_myAvatar.getVelocity().y) + - fabs(_myAvatar.getVelocity().z)) / 3 < MAX_AVATAR_EDIT_VELOCITY) { + (fabs(_myAvatar->getVelocity().x) + + fabs(_myAvatar->getVelocity().y) + + fabs(_myAvatar->getVelocity().z)) / 3 < MAX_AVATAR_EDIT_VELOCITY) { if (_voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _mouseVoxel, distance, face)) { if (distance < MAX_VOXEL_EDIT_DISTANCE) { @@ -2085,11 +2173,6 @@ void Application::updateHandAndTouch(float deltaTime) { // Update from Touch if (_isTouchPressed) { - float TOUCH_YAW_SCALE = -0.25f; - float TOUCH_PITCH_SCALE = -12.5f; - float FIXED_TOUCH_TIMESTEP = 0.016f; - _yawFromTouch += ((_touchAvgX - _lastTouchAvgX) * TOUCH_YAW_SCALE * FIXED_TOUCH_TIMESTEP); - _pitchFromTouch += ((_touchAvgY - _lastTouchAvgY) * TOUCH_PITCH_SCALE * FIXED_TOUCH_TIMESTEP); _lastTouchAvgX = _touchAvgX; _lastTouchAvgY = _touchAvgY; } @@ -2128,24 +2211,6 @@ void Application::updateThreads(float deltaTime) { } } -void Application::updateMyAvatarSimulation(float deltaTime) { - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateMyAvatarSimulation()"); - - if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) { - _myAvatar.setGravity(_environment.getGravity(_myAvatar.getPosition())); - } - else { - _myAvatar.setGravity(glm::vec3(0.0f, 0.0f, 0.0f)); - } - - if (Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) { - _myAvatar.simulate(deltaTime, &_myTransmitter); - } else { - _myAvatar.simulate(deltaTime, NULL); - } -} - void Application::updateParticles(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateParticles()"); @@ -2164,32 +2229,6 @@ void Application::updateMetavoxels(float deltaTime) { } } -void Application::updateTransmitter(float deltaTime) { - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateTransmitter()"); - - // no transmitter drive implies transmitter pick - if (!Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) { - _transmitterPickStart = _myAvatar.getChestPosition(); - glm::vec3 direction = _myAvatar.getOrientation() * - glm::quat(glm::radians(_myTransmitter.getEstimatedRotation())) * IDENTITY_FRONT; - - // check against voxels, avatars - const float MAX_PICK_DISTANCE = 100.0f; - float minDistance = MAX_PICK_DISTANCE; - VoxelDetail detail; - float distance; - BoxFace face; - if (_voxels.findRayIntersection(_transmitterPickStart, direction, detail, distance, face)) { - minDistance = min(minDistance, distance); - } - _transmitterPickEnd = _transmitterPickStart + direction * minDistance; - - } else { - _transmitterPickStart = _transmitterPickEnd = glm::vec3(); - } -} - void Application::updateCamera(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateCamera()"); @@ -2245,8 +2284,8 @@ void Application::updateAudio(float deltaTime) { PerformanceWarning warn(showWarnings, "Application::updateAudio()"); // Update audio stats for procedural sounds - _audio.setLastAcceleration(_myAvatar.getThrust()); - _audio.setLastVelocity(_myAvatar.getVelocity()); + _audio.setLastAcceleration(_myAvatar->getThrust()); + _audio.setLastVelocity(_myAvatar->getVelocity()); } void Application::updateCursor(float deltaTime) { @@ -2256,7 +2295,7 @@ void Application::updateCursor(float deltaTime) { // watch mouse position, if it hasn't moved, hide the cursor bool underMouse = _glWidget->underMouse(); if (!_mouseHidden) { - uint64_t now = usecTimestampNow(); + quint64 now = usecTimestampNow(); int elapsed = now - _lastMouseMove; const int HIDE_CURSOR_TIMEOUT = 1 * 1000 * 1000; // 1 second if (elapsed > HIDE_CURSOR_TIMEOUT && (underMouse || !_seenMouseMove)) { @@ -2284,7 +2323,7 @@ void Application::update(float deltaTime) { glm::vec3 lookAtSpot; updateFaceshift(); - _avatarManager.updateLookAtTargetAvatar(lookAtSpot); + _myAvatar->updateLookAtTargetAvatar(lookAtSpot); updateMyAvatarLookAtPosition(lookAtSpot); // Find the voxel we are hovering over, and respond if clicked @@ -2297,81 +2336,31 @@ void Application::update(float deltaTime) { updateLeap(deltaTime); // Leap finger-sensing device updateSixense(deltaTime); // Razer Hydra controllers updateSerialDevices(deltaTime); // Read serial port interface devices - updateAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes + updateMyAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... - _avatarManager.updateAvatars(deltaTime); //loop through all the other avatars and simulate them... - updateMyAvatarSimulation(deltaTime); // Simulate myself + _avatarManager.updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them... updateParticles(deltaTime); // Simulate particle cloud movements updateMetavoxels(deltaTime); // update metavoxels - updateTransmitter(deltaTime); // transmitter drive or pick updateCamera(deltaTime); // handle various camera tweaks like off axis projection updateDialogs(deltaTime); // update various stats dialogs if present updateAudio(deltaTime); // Update audio stats for procedural sounds updateCursor(deltaTime); // Handle cursor updates _particles.update(); // update the particles... - _particleCollisionSystem.update(); // handle collisions for the particles... + _particleCollisionSystem.update(); // collide the particles... } -void Application::updateAvatar(float deltaTime) { +void Application::updateMyAvatar(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateAvatar()"); + PerformanceWarning warn(showWarnings, "Application::updateMyAvatar()"); - // rotate body yaw for yaw received from multitouch - _myAvatar.setOrientation(_myAvatar.getOrientation() - * glm::quat(glm::vec3(0, _yawFromTouch, 0))); - _yawFromTouch = 0.f; - - // apply pitch from touch - _myAvatar.getHead().setMousePitch(_myAvatar.getHead().getMousePitch() + - _myAvatar.getHand().getPitchUpdate() + - _pitchFromTouch); - _myAvatar.getHand().setPitchUpdate(0.f); - _pitchFromTouch = 0.0f; - - // Update my avatar's state from gyros - _myAvatar.updateFromGyros(Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)); - - // Update head mouse from faceshift if active - if (_faceshift.isActive()) { - glm::vec3 headVelocity = _faceshift.getHeadAngularVelocity(); - - // sets how quickly head angular rotation moves the head mouse - const float HEADMOUSE_FACESHIFT_YAW_SCALE = 40.f; - const float HEADMOUSE_FACESHIFT_PITCH_SCALE = 30.f; - _headMouseX -= headVelocity.y * HEADMOUSE_FACESHIFT_YAW_SCALE; - _headMouseY -= headVelocity.x * HEADMOUSE_FACESHIFT_PITCH_SCALE; - } - - // Constrain head-driven mouse to edges of screen - _headMouseX = glm::clamp(_headMouseX, 0, _glWidget->width()); - _headMouseY = glm::clamp(_headMouseY, 0, _glWidget->height()); - - if (OculusManager::isConnected()) { - float yaw, pitch, roll; - OculusManager::getEulerAngles(yaw, pitch, roll); - - _myAvatar.getHead().setYaw(yaw); - _myAvatar.getHead().setPitch(pitch); - _myAvatar.getHead().setRoll(roll); - } - - // Get audio loudness data from audio input device - _myAvatar.getHead().setAudioLoudness(_audio.getLastInputLoudness()); + _myAvatar->update(deltaTime); // send head/hand data to the avatar mixer and voxel server - unsigned char broadcastString[MAX_PACKET_SIZE]; - unsigned char* endOfBroadcastStringWrite = broadcastString; + QByteArray packet = byteArrayWithPopluatedHeader(PacketTypeAvatarData); + packet.append(_myAvatar->toByteArray()); - endOfBroadcastStringWrite += populateTypeAndVersion(endOfBroadcastStringWrite, PACKET_TYPE_HEAD_DATA); - - // pack the NodeList owner UUID - endOfBroadcastStringWrite += NodeList::getInstance()->packOwnerUUID(endOfBroadcastStringWrite); - - endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite); - - controlledBroadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString, - QSet() << NODE_TYPE_AVATAR_MIXER); + controlledBroadcastToNodes(packet, NodeSet() << NodeType::AvatarMixer); // Update _viewFrustum with latest camera and view frustum data... // NOTE: we get this from the view frustum, to make it simpler, since the @@ -2382,11 +2371,11 @@ void Application::updateAvatar(float deltaTime) { loadViewFrustum(_myCamera, _viewFrustum); // Update my voxel servers with my current voxel query... - queryOctree(NODE_TYPE_VOXEL_SERVER, PACKET_TYPE_VOXEL_QUERY, _voxelServerJurisdictions); - queryOctree(NODE_TYPE_PARTICLE_SERVER, PACKET_TYPE_PARTICLE_QUERY, _particleServerJurisdictions); + queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions); + queryOctree(NodeType::ParticleServer, PacketTypeParticleQuery, _particleServerJurisdictions); } -void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, NodeToJurisdictionMap& jurisdictions) { +void Application::queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions) { // if voxels are disabled, then don't send this at all... if (!Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { @@ -2554,10 +2543,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node unsigned char* endOfVoxelQueryPacket = voxelQueryPacket; // insert packet type/version and node UUID - endOfVoxelQueryPacket += populateTypeAndVersion(endOfVoxelQueryPacket, packetType); - QByteArray ownerUUID = nodeList->getOwnerUUID().toRfc4122(); - memcpy(endOfVoxelQueryPacket, ownerUUID.constData(), ownerUUID.size()); - endOfVoxelQueryPacket += ownerUUID.size(); + endOfVoxelQueryPacket += populatePacketHeader(reinterpret_cast(endOfVoxelQueryPacket), packetType); // encode the query data... endOfVoxelQueryPacket += _voxelQuery.getBroadcastData(endOfVoxelQueryPacket); @@ -2926,29 +2912,8 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } // render transmitter pick ray, if non-empty - if (_transmitterPickStart != _transmitterPickEnd) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::displaySide() ... transmitter pick ray..."); + _myAvatar->renderTransmitterPickRay(); - Glower glower; - const float TRANSMITTER_PICK_COLOR[] = { 1.0f, 1.0f, 0.0f }; - glColor3fv(TRANSMITTER_PICK_COLOR); - glLineWidth(3.0f); - glBegin(GL_LINES); - glVertex3f(_transmitterPickStart.x, _transmitterPickStart.y, _transmitterPickStart.z); - glVertex3f(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z); - glEnd(); - glLineWidth(1.0f); - - glPushMatrix(); - glTranslatef(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z); - - const float PICK_END_RADIUS = 0.025f; - glutSolidSphere(PICK_END_RADIUS, 8, 8); - - glPopMatrix(); - } - // give external parties a change to hook in emit renderingInWorldInterface(); } @@ -2972,71 +2937,38 @@ void Application::displayOverlay() { // Render 2D overlay: I/O level bar graphs and text glMatrixMode(GL_PROJECTION); glPushMatrix(); - glLoadIdentity(); - gluOrtho2D(0, _glWidget->width(), _glWidget->height(), 0); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - // Display a single screen-size quad to create an alpha blended 'collision' flash - if (_audio.getCollisionFlashesScreen()) { - float collisionSoundMagnitude = _audio.getCollisionSoundMagnitude(); - const float VISIBLE_COLLISION_SOUND_MAGNITUDE = 0.5f; - if (collisionSoundMagnitude > VISIBLE_COLLISION_SOUND_MAGNITUDE) { - renderCollisionOverlay(_glWidget->width(), _glWidget->height(), _audio.getCollisionSoundMagnitude()); - } + glLoadIdentity(); + gluOrtho2D(0, _glWidget->width(), _glWidget->height(), 0); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + + // Display a single screen-size quad to create an alpha blended 'collision' flash + if (_audio.getCollisionFlashesScreen()) { + float collisionSoundMagnitude = _audio.getCollisionSoundMagnitude(); + const float VISIBLE_COLLISION_SOUND_MAGNITUDE = 0.5f; + if (collisionSoundMagnitude > VISIBLE_COLLISION_SOUND_MAGNITUDE) { + renderCollisionOverlay(_glWidget->width(), _glWidget->height(), _audio.getCollisionSoundMagnitude()); } + } - if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { - displayStatsBackground(0x33333399, 0, _glWidget->height() - 68, 296, 68); - _audio.render(_glWidget->width(), _glWidget->height()); - if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) { - int oscilloscopeTop = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) ? 130 : 25; - _audioScope.render(25, oscilloscopeTop); - } + if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { + displayStatsBackground(0x33333399, 0, _glWidget->height() - 68, 296, 68); + _audio.render(_glWidget->width(), _glWidget->height()); + if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) { + int oscilloscopeTop = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) ? 130 : 25; + _audioScope.render(25, oscilloscopeTop); } + } - //noiseTest(_glWidget->width(), _glWidget->height()); + //noiseTest(_glWidget->width(), _glWidget->height()); if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) { - // Display small target box at center or head mouse target that can also be used to measure LOD - glColor3f(1.0, 1.0, 1.0); - glDisable(GL_LINE_SMOOTH); - const int PIXEL_BOX = 16; - glBegin(GL_LINES); - glVertex2f(_headMouseX - PIXEL_BOX/2, _headMouseY); - glVertex2f(_headMouseX + PIXEL_BOX/2, _headMouseY); - glVertex2f(_headMouseX, _headMouseY - PIXEL_BOX/2); - glVertex2f(_headMouseX, _headMouseY + PIXEL_BOX/2); - glEnd(); - glEnable(GL_LINE_SMOOTH); - glColor3f(1.f, 0.f, 0.f); - glPointSize(3.0f); - glDisable(GL_POINT_SMOOTH); - glBegin(GL_POINTS); - glVertex2f(_headMouseX - 1, _headMouseY + 1); - glEnd(); - // If Faceshift is active, show eye pitch and yaw as separate pointer - if (_faceshift.isActive()) { - const float EYE_TARGET_PIXELS_PER_DEGREE = 40.0; - int eyeTargetX = (_glWidget->width() / 2) - _faceshift.getEstimatedEyeYaw() * EYE_TARGET_PIXELS_PER_DEGREE; - int eyeTargetY = (_glWidget->height() / 2) - _faceshift.getEstimatedEyePitch() * EYE_TARGET_PIXELS_PER_DEGREE; - - glColor3f(0.0, 1.0, 1.0); - glDisable(GL_LINE_SMOOTH); - glBegin(GL_LINES); - glVertex2f(eyeTargetX - PIXEL_BOX/2, eyeTargetY); - glVertex2f(eyeTargetX + PIXEL_BOX/2, eyeTargetY); - glVertex2f(eyeTargetX, eyeTargetY - PIXEL_BOX/2); - glVertex2f(eyeTargetX, eyeTargetY + PIXEL_BOX/2); - glEnd(); - - } + _myAvatar->renderHeadMouse(); } - // Show hand transmitter data if detected - if (_myTransmitter.isConnected()) { - _myTransmitter.renderLevels(_glWidget->width(), _glWidget->height()); - } + _myAvatar->renderTransmitterLevels(_glWidget->width(), _glWidget->height()); + // Display stats and log text onscreen glLineWidth(1.0f); glPointSize(1.0f); @@ -3068,7 +3000,7 @@ void Application::displayOverlay() { // Show on-screen msec timer if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) { char frameTimer[10]; - uint64_t mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5); + quint64 mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5); sprintf(frameTimer, "%d\n", (int)(mSecsNow % 1000)); int timerBottom = (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && @@ -3159,10 +3091,9 @@ void Application::displayStats() { glPointSize(1.0f); - int totalAvatars = 0, totalServers = 0; - foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { - node->getType() == NODE_TYPE_AGENT ? totalAvatars++ : totalServers++; - } + // we need to take one avatar out so we don't include ourselves + int totalAvatars = _avatarManager.size() - 1; + int totalServers = NodeList::getInstance()->size(); if (mirrorEnabled) { horizontalOffset += MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; @@ -3205,8 +3136,8 @@ void Application::displayStats() { int pingAudio = 0, pingAvatar = 0, pingVoxel = 0, pingVoxelMax = 0; NodeList* nodeList = NodeList::getInstance(); - SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); - SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NODE_TYPE_AVATAR_MIXER); + SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); + SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : 0; pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : 0; @@ -3216,7 +3147,7 @@ void Application::displayStats() { int voxelServerCount = 0; foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { - if (node->getType() == NODE_TYPE_VOXEL_SERVER) { + if (node->getType() == NodeType::VoxelServer) { totalPingVoxel += node->getPingMs(); voxelServerCount++; if (pingVoxelMax < node->getPingMs()) { @@ -3259,7 +3190,7 @@ void Application::displayStats() { horizontalOffset += 171; } - glm::vec3 avatarPos = _myAvatar.getPosition(); + glm::vec3 avatarPos = _myAvatar->getPosition(); lines = _statsExpanded ? 4 : 3; displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - (mirrorEnabled ? 301 : 411) - horizontalOffset, lines * STATS_PELS_PER_LINE + 10); @@ -3274,9 +3205,9 @@ void Application::displayStats() { sprintf(avatarPosition, "Position: %.3f, %.3f, %.3f", avatarPos.x, avatarPos.y, avatarPos.z); } char avatarVelocity[30]; - sprintf(avatarVelocity, "Velocity: %.1f", glm::length(_myAvatar.getVelocity())); + sprintf(avatarVelocity, "Velocity: %.1f", glm::length(_myAvatar->getVelocity())); char avatarBodyYaw[30]; - sprintf(avatarBodyYaw, "Yaw: %.2f", _myAvatar.getBodyYaw()); + sprintf(avatarBodyYaw, "Yaw: %.2f", _myAvatar->getBodyYaw()); char avatarMixerStats[200]; verticalOffset += STATS_PELS_PER_LINE; @@ -3287,7 +3218,7 @@ void Application::displayStats() { drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, avatarBodyYaw, .93f, .93f, .93f); if (_statsExpanded) { - SharedNodePointer avatarMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AVATAR_MIXER); + SharedNodePointer avatarMixer = NodeList::getInstance()->soloNodeOfType(NodeType::AvatarMixer); if (avatarMixer) { sprintf(avatarMixerStats, "Avatar Mixer: %.f kbps, %.f pps", roundf(avatarMixer->getAverageKilobitsPerSecond()), @@ -3861,7 +3792,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 - _voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_VOXEL_ERASE, _mouseVoxel); + _voxelEditSender.sendVoxelEditMessage(PacketTypeVoxelErase, _mouseVoxel); // delete it locally to see the effect immediately (and in case no voxel server is present) _voxels.deleteVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); @@ -3887,8 +3818,8 @@ void Application::eyedropperVoxelUnderCursor() { } void Application::resetSensors() { - _headMouseX = _mouseX = _glWidget->width() / 2; - _headMouseY = _mouseY = _glWidget->height() / 2; + _mouseX = _glWidget->width() / 2; + _mouseY = _glWidget->height() / 2; _faceshift.reset(); @@ -3896,11 +3827,8 @@ void Application::resetSensors() { OculusManager::reset(); } - QCursor::setPos(_headMouseX, _headMouseY); - _myAvatar.reset(); - _myTransmitter.resetLevels(); - _myAvatar.setVelocity(glm::vec3(0,0,0)); - _myAvatar.setThrust(glm::vec3(0,0,0)); + QCursor::setPos(_mouseX, _mouseY); + _myAvatar->reset(); QMetaObject::invokeMethod(&_audio, "reset", Qt::QueuedConnection); } @@ -3925,18 +3853,12 @@ void Application::setMenuShortcutsEnabled(bool enabled) { } void Application::updateWindowTitle(){ - QString title = ""; - - QString buildVersion = " (build " + applicationVersion() + ")"; - - QString username = _profile.getUsername(); - if(!username.isEmpty()){ - title += username; - title += " @ "; - } - title += NodeList::getInstance()->getDomainHostname(); - title += buildVersion; + QString buildVersion = " (build " + applicationVersion() + ")"; + NodeList* nodeList = NodeList::getInstance(); + + QString title = QString() + _profile.getUsername() + " " + nodeList->getOwnerUUID().toString() + + " @ " + nodeList->getDomainHostname() + buildVersion; qDebug("Application title set to: %s", title.toStdString().c_str()); _window->setWindowTitle(title); @@ -3965,7 +3887,7 @@ void Application::domainChanged(const QString& domainHostname) { } void Application::nodeKilled(SharedNodePointer node) { - if (node->getType() == NODE_TYPE_VOXEL_SERVER) { + if (node->getType() == NodeType::VoxelServer) { QUuid nodeUUID = node->getUUID(); // see if this is the first we've heard of this node... if (_voxelServerJurisdictions.find(nodeUUID) != _voxelServerJurisdictions.end()) { @@ -3996,7 +3918,7 @@ void Application::nodeKilled(SharedNodePointer node) { } _voxelSceneStatsLock.unlock(); - } else if (node->getType() == NODE_TYPE_PARTICLE_SERVER) { + } else if (node->getType() == NodeType::ParticleServer) { QUuid nodeUUID = node->getUUID(); // see if this is the first we've heard of this node... if (_particleServerJurisdictions.find(nodeUUID) != _particleServerJurisdictions.end()) { @@ -4027,14 +3949,13 @@ void Application::nodeKilled(SharedNodePointer node) { } _voxelSceneStatsLock.unlock(); - } else if (node->getType() == NODE_TYPE_AVATAR_MIXER) { + } else if (node->getType() == NodeType::AvatarMixer) { // our avatar mixer has gone away - clear the hash of avatars - _avatarManager.clearHash(); + _avatarManager.clearOtherAvatars(); } } -void Application::trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength, - const HifiSockAddr& senderSockAddr, bool wasStatsPacket) { +void Application::trackIncomingVoxelPacket(const QByteArray& packet, const HifiSockAddr& senderSockAddr, bool wasStatsPacket) { // Attempt to identify the sender from it's address. SharedNodePointer serverNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr); @@ -4045,13 +3966,13 @@ void Application::trackIncomingVoxelPacket(unsigned char* messageData, ssize_t m _voxelSceneStatsLock.lockForWrite(); if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { VoxelSceneStats& stats = _octreeServerSceneStats[nodeUUID]; - stats.trackIncomingOctreePacket(messageData, messageLength, wasStatsPacket, serverNode->getClockSkewUsec()); + stats.trackIncomingOctreePacket(packet, wasStatsPacket, serverNode->getClockSkewUsec()); } _voxelSceneStatsLock.unlock(); } } -int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderSockAddr) { +int Application::parseOctreeStats(const QByteArray& packet, const HifiSockAddr& senderSockAddr) { // But, also identify the sender, and keep track of the contained jurisdiction root for this server SharedNodePointer server = NodeList::getInstance()->nodeWithAddress(senderSockAddr); @@ -4059,7 +3980,7 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen // parse the incoming stats datas stick it in a temporary object for now, while we // determine which server it belongs to VoxelSceneStats temp; - int statsMessageLength = temp.unpackFromMessage(messageData, messageLength); + int statsMessageLength = temp.unpackFromMessage(reinterpret_cast(packet.data()), packet.size()); // quick fix for crash... why would voxelServer be NULL? if (server) { @@ -4068,7 +3989,8 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen // now that we know the node ID, let's add these stats to the stats for that node... _voxelSceneStatsLock.lockForWrite(); if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { - _octreeServerSceneStats[nodeUUID].unpackFromMessage(messageData, messageLength); + _octreeServerSceneStats[nodeUUID].unpackFromMessage(reinterpret_cast(packet.data()), + packet.size()); } else { _octreeServerSceneStats[nodeUUID] = temp; } @@ -4079,7 +4001,7 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen // see if this is the first we've heard of this node... NodeToJurisdictionMap* jurisdiction = NULL; - if (server->getType() == NODE_TYPE_VOXEL_SERVER) { + if (server->getType() == NodeType::VoxelServer) { jurisdiction = &_voxelServerJurisdictions; } else { jurisdiction = &_particleServerJurisdictions; @@ -4114,37 +4036,37 @@ void Application::packetSent(quint64 length) { _bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(length); } -void Application::loadScripts(){ - // loads all saved scripts - QSettings* settings = new QSettings(this); - int size = settings->beginReadArray("Settings"); - for(int i=0; isetArrayIndex(i); - QString string = settings->value("script").toString(); - loadScript(string); - } - settings->endArray(); - +void Application::loadScripts() { + // loads all saved scripts + QSettings* settings = new QSettings(this); + int size = settings->beginReadArray("Settings"); + + for (int i = 0; i < size; ++i){ + settings->setArrayIndex(i); + QString string = settings->value("script").toString(); + loadScript(string); + } + + settings->endArray(); } -void Application::saveScripts(){ - // saves all current running scripts - QSettings* settings = new QSettings(this); - settings->beginWriteArray("Settings"); - for(int i=0; i<_activeScripts.size(); ++i){ - settings->setArrayIndex(i); - settings->setValue("script", _activeScripts.at(i)); - } - settings->endArray(); - +void Application::saveScripts() { + // saves all current running scripts + QSettings* settings = new QSettings(this); + settings->beginWriteArray("Settings"); + for (int i = 0; i < _activeScripts.size(); ++i){ + settings->setArrayIndex(i); + settings->setValue("script", _activeScripts.at(i)); + } + + settings->endArray(); } -void Application::removeScriptName(const QString& fileNameString) -{ +void Application::removeScriptName(const QString& fileNameString) { _activeScripts.removeOne(fileNameString); } -void Application::loadScript(const QString& fileNameString){ +void Application::loadScript(const QString& fileNameString) { _activeScripts.append(fileNameString); QByteArray fileNameAscii = fileNameString.toLocal8Bit(); const char* fileName = fileNameAscii.data(); @@ -4172,7 +4094,8 @@ void Application::loadScript(const QString& fileNameString){ // start the script on a new thread... bool wantMenuItems = true; // tells the ScriptEngine object to add menu items for itself - ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, Menu::getInstance(), &_controllerScriptingInterface); + ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, Menu::getInstance(), + &_controllerScriptingInterface); scriptEngine->setupMenuItems(); // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so @@ -4182,12 +4105,12 @@ void Application::loadScript(const QString& fileNameString){ scriptEngine->getParticlesScriptingInterface()->setParticleTree(_particles.getTree()); // hook our avatar object into this script engine - scriptEngine->setAvatarData(&_myAvatar, "MyAvatar"); + scriptEngine->setAvatarData( static_cast(_myAvatar), "MyAvatar"); QThread* workerThread = new QThread(this); // when the worker thread is started, call our engine's run.. - connect(workerThread, SIGNAL(started()), scriptEngine, SLOT(run())); + connect(workerThread, &QThread::started, scriptEngine, &ScriptEngine::run); // when the thread is terminated, add both scriptEngine and thread to the deleteLater queue connect(scriptEngine, SIGNAL(finished(const QString&)), scriptEngine, SLOT(deleteLater())); @@ -4227,7 +4150,7 @@ void Application::toggleLogDialog() { } void Application::initAvatarAndViewFrustum() { - updateAvatar(0.f); + updateMyAvatar(0.f); } QString Application::getLocalVoxelCacheFileName() { @@ -4341,3 +4264,14 @@ void Application::skipVersion(QString latestVersion) { skipFile.seek(0); skipFile.write(latestVersion.toStdString().c_str()); } + +void Application::takeSnapshot() { + switchToResourcesParentIfRequired(); + QMediaPlayer* player = new QMediaPlayer(); + QFileInfo inf = QFileInfo("resources/sounds/snap.wav"); + player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath())); + player->play(); + + Snapshot::saveSnapshot(_glWidget, _profile.getUsername(), _myAvatar->getPosition()); +} + diff --git a/interface/src/Application.h b/interface/src/Application.h index c3404b4035..bf9981a160 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -142,7 +142,7 @@ public: glm::vec3 getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVoxel); QGLWidget* getGLWidget() { return _glWidget; } - MyAvatar* getAvatar() { return &_myAvatar; } + MyAvatar* getAvatar() { return _myAvatar; } Audio* getAudio() { return &_audio; } Camera* getCamera() { return &_myCamera; } ViewFrustum* getViewFrustum() { return &_viewFrustum; } @@ -175,8 +175,7 @@ public: Profile* getProfile() { return &_profile; } void resetProfile(const QString& username); - void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, - const QSet& destinationNodeTypes); + void controlledBroadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes); void setupWorldLight(); @@ -265,7 +264,7 @@ private: void updateProjectionMatrix(Camera& camera, bool updateViewFrustum = true); static bool sendVoxelsOperation(OctreeElement* node, void* extraData); - static void sendPingPackets(); + void sendPingPackets(); void initDisplay(); void init(); @@ -283,10 +282,8 @@ private: void updateSixense(float deltaTime); void updateSerialDevices(float deltaTime); void updateThreads(float deltaTime); - void updateMyAvatarSimulation(float deltaTime); void updateParticles(float deltaTime); void updateMetavoxels(float deltaTime); - void updateTransmitter(float deltaTime); void updateCamera(float deltaTime); void updateDialogs(float deltaTime); void updateAudio(float deltaTime); @@ -298,8 +295,8 @@ private: void renderLookatIndicator(glm::vec3 pointOfInterest); void renderHighlightVoxel(VoxelDetail voxel); - void updateAvatar(float deltaTime); - void queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, NodeToJurisdictionMap& jurisdictions); + void updateMyAvatar(float deltaTime); + void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions); void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum); glm::vec3 getSunDirection(); @@ -356,7 +353,7 @@ private: VoxelSystem _voxels; VoxelTree _clipboard; // if I copy/paste - VoxelImporter _voxelImporter; + VoxelImporter* _voxelImporter; VoxelSystem _sharedVoxelSystem; ViewFrustum _sharedVoxelSystemViewFrustum; @@ -375,10 +372,8 @@ private: VoxelQuery _voxelQuery; // NodeData derived class for querying voxels from voxel server AvatarManager _avatarManager; - MyAvatar _myAvatar; // The rendered avatar of oneself - Profile _profile; // The data-server linked profile for this user - - Transmitter _myTransmitter; // Gets UDP data from transmitter app used to animate the avatar + MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be) + Profile _profile; // The data-server linked profile for this user Faceshift _faceshift; @@ -398,13 +393,11 @@ private: Environment _environment; - int _headMouseX, _headMouseY; - int _mouseX; int _mouseY; int _mouseDragStartedX; int _mouseDragStartedY; - uint64_t _lastMouseMove; + quint64 _lastMouseMove; bool _mouseHidden; bool _seenMouseMove; @@ -418,8 +411,6 @@ private: float _touchDragStartedAvgX; float _touchDragStartedAvgY; bool _isTouchPressed; // true if multitouch has been pressed (clear when finished) - float _yawFromTouch; - float _pitchFromTouch; VoxelDetail _mouseVoxelDragging; bool _mousePressed; // true if mouse has been pressed (clear when finished) @@ -444,9 +435,6 @@ private: bool _lookingAwayFromOrigin; glm::vec3 _nudgeGuidePosition; - glm::vec3 _transmitterPickStart; - glm::vec3 _transmitterPickEnd; - ChatEntry _chatEntry; // chat entry field bool _chatEntryOn; // Whether to show the chat entry @@ -482,9 +470,8 @@ private: PieMenu _pieMenu; - int parseOctreeStats(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderAddress); - void trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength, - const HifiSockAddr& senderSockAddr, bool wasStatsPacket); + int parseOctreeStats(const QByteArray& packet, const HifiSockAddr& senderAddress); + void trackIncomingVoxelPacket(const QByteArray& packet, const HifiSockAddr& senderSockAddr, bool wasStatsPacket); NodeToJurisdictionMap _voxelServerJurisdictions; NodeToJurisdictionMap _particleServerJurisdictions; @@ -505,6 +492,7 @@ private: void checkVersion(); void displayUpdateDialog(); bool shouldSkipVersion(QString latestVersion); + void takeSnapshot(); }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index ea6bc80712..9fede84a93 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -286,8 +285,8 @@ void Audio::start() { void Audio::handleAudioInput() { static char monoAudioDataPacket[MAX_PACKET_SIZE]; - static int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO); - static int leadingBytes = numBytesPacketHeader + sizeof(glm::vec3) + sizeof(glm::quat) + NUM_BYTES_RFC4122_UUID; + static int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(PacketTypeMicrophoneAudioNoEcho); + static int leadingBytes = numBytesPacketHeader + sizeof(glm::vec3) + sizeof(glm::quat); static int16_t* monoAudioSamples = (int16_t*) (monoAudioDataPacket + leadingBytes); @@ -366,7 +365,7 @@ void Audio::handleAudioInput() { NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); NodeList* nodeList = NodeList::getInstance(); - SharedNodePointer audioMixer = nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); + SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); if (audioMixer && nodeList->getNodeActiveSocketOrPing(audioMixer.data())) { MyAvatar* interfaceAvatar = Application::getInstance()->getAvatar(); @@ -376,16 +375,10 @@ void Audio::handleAudioInput() { // we need the amount of bytes in the buffer + 1 for type // + 12 for 3 floats for position + float for bearing + 1 attenuation byte - PACKET_TYPE packetType = Menu::getInstance()->isOptionChecked(MenuOption::EchoServerAudio) - ? PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO : PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO; + PacketType packetType = Menu::getInstance()->isOptionChecked(MenuOption::EchoServerAudio) + ? PacketTypeMicrophoneAudioWithEcho : PacketTypeMicrophoneAudioNoEcho; - char* currentPacketPtr = monoAudioDataPacket + populateTypeAndVersion((unsigned char*) monoAudioDataPacket, - packetType); - - // pack Source Data - QByteArray rfcUUID = NodeList::getInstance()->getOwnerUUID().toRfc4122(); - memcpy(currentPacketPtr, rfcUUID.constData(), rfcUUID.size()); - currentPacketPtr += rfcUUID.size(); + char* currentPacketPtr = monoAudioDataPacket + populatePacketHeader(monoAudioDataPacket, packetType); // memcpy the three float positions memcpy(currentPacketPtr, &headPosition, sizeof(headPosition)); @@ -434,7 +427,7 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) { } } - _ringBuffer.parseData((unsigned char*) audioByteArray.data(), audioByteArray.size()); + _ringBuffer.parseData(audioByteArray); static float networkOutputToOutputRatio = (_desiredOutputFormat.sampleRate() / (float) _outputFormat.sampleRate()) * (_desiredOutputFormat.channelCount() / (float) _outputFormat.channelCount()); @@ -546,7 +539,7 @@ void Audio::toggleMute() { } void Audio::render(int screenWidth, int screenHeight) { - if (_audioInput) { + if (_audioInput && _audioOutput) { glLineWidth(2.0); glBegin(GL_LINES); glColor3f(.93f, .93f, .93f); diff --git a/interface/src/ControllerScriptingInterface.cpp b/interface/src/ControllerScriptingInterface.cpp index fd27eb2428..4d7540c06c 100644 --- a/interface/src/ControllerScriptingInterface.cpp +++ b/interface/src/ControllerScriptingInterface.cpp @@ -10,6 +10,14 @@ #include "Application.h" #include "ControllerScriptingInterface.h" +ControllerScriptingInterface::ControllerScriptingInterface() : + _mouseCaptured(false), + _touchCaptured(false), + _wheelCaptured(false) +{ +} + + const PalmData* ControllerScriptingInterface::getPrimaryPalm() const { int leftPalmIndex, rightPalmIndex; @@ -179,5 +187,35 @@ glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex return glm::vec3(0); // bad index } +bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const { + return isKeyCaptured(KeyEvent(*event)); +} +bool ControllerScriptingInterface::isKeyCaptured(const KeyEvent& event) const { + // if we've captured some combination of this key it will be in the map + if (_capturedKeys.contains(event.key, event)) { + return true; + } + return false; +} + +void ControllerScriptingInterface::captureKeyEvents(const KeyEvent& event) { + // if it's valid + if (event.isValid) { + // and not already captured + if (!isKeyCaptured(event)) { + // then add this KeyEvent record to the captured combos for this key + _capturedKeys.insert(event.key, event); + } + } +} + +void ControllerScriptingInterface::releaseKeyEvents(const KeyEvent& event) { + if (event.isValid) { + // and not already captured + if (isKeyCaptured(event)) { + _capturedKeys.remove(event.key, event); + } + } +} diff --git a/interface/src/ControllerScriptingInterface.h b/interface/src/ControllerScriptingInterface.h index 69daefa3fb..e84039bcb0 100644 --- a/interface/src/ControllerScriptingInterface.h +++ b/interface/src/ControllerScriptingInterface.h @@ -1,3 +1,4 @@ + // // ControllerScriptingInterface.h // hifi @@ -18,6 +19,27 @@ class PalmData; class ControllerScriptingInterface : public AbstractControllerScriptingInterface { Q_OBJECT +public: + ControllerScriptingInterface(); + void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } + void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); } + + void emitMouseMoveEvent(QMouseEvent* event) { emit mouseMoveEvent(MouseEvent(*event)); } + void emitMousePressEvent(QMouseEvent* event) { emit mousePressEvent(MouseEvent(*event)); } + void emitMouseReleaseEvent(QMouseEvent* event) { emit mouseReleaseEvent(MouseEvent(*event)); } + + void emitTouchBeginEvent(QTouchEvent* event) { emit touchBeginEvent(*event); } + void emitTouchEndEvent(QTouchEvent* event) { emit touchEndEvent(*event); } + void emitTouchUpdateEvent(QTouchEvent* event) { emit touchUpdateEvent(*event); } + void emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); } + + bool isKeyCaptured(QKeyEvent* event) const; + bool isKeyCaptured(const KeyEvent& event) const; + bool isMouseCaptured() const { return _mouseCaptured; } + bool isTouchCaptured() const { return _touchCaptured; } + bool isWheelCaptured() const { return _wheelCaptured; } + + public slots: virtual bool isPrimaryButtonPressed() const; virtual glm::vec2 getPrimaryJoystickPosition() const; @@ -36,11 +58,28 @@ public slots: virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const; virtual glm::vec3 getSpatialControlNormal(int controlIndex) const; + virtual void captureKeyEvents(const KeyEvent& event); + virtual void releaseKeyEvents(const KeyEvent& event); + + virtual void captureMouseEvents() { _mouseCaptured = true; } + virtual void releaseMouseEvents() { _mouseCaptured = false; } + + virtual void captureTouchEvents() { _touchCaptured = true; } + virtual void releaseTouchEvents() { _touchCaptured = false; } + + virtual void captureWheelEvents() { _wheelCaptured = true; } + virtual void releaseWheelEvents() { _wheelCaptured = false; } + private: const PalmData* getPrimaryPalm() const; const PalmData* getPalm(int palmIndex) const; int getNumberOfActivePalms() const; const PalmData* getActivePalm(int palmIndex) const; + + bool _mouseCaptured; + bool _touchCaptured; + bool _wheelCaptured; + QMultiMap _capturedKeys; }; const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index b78bd0c309..2938be80ea 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -26,52 +26,53 @@ void DatagramProcessor::processDatagrams() { "DatagramProcessor::processDatagrams()"); HifiSockAddr senderSockAddr; - ssize_t bytesReceived; - static unsigned char incomingPacket[MAX_PACKET_SIZE]; + static QByteArray incomingPacket; Application* application = Application::getInstance(); + NodeList* nodeList = NodeList::getInstance(); - while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams() && - (bytesReceived = NodeList::getInstance()->getNodeSocket().readDatagram((char*) incomingPacket, - MAX_PACKET_SIZE, - senderSockAddr.getAddressPointer(), - senderSockAddr.getPortPointer()))) { + while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams()) { + incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); + nodeList->getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size(), + senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); _packetCount++; - _byteCount += bytesReceived; + _byteCount += incomingPacket.size(); - if (packetVersionMatch(incomingPacket, senderSockAddr)) { + if (packetVersionMatch(incomingPacket)) { // only process this packet if we have a match on the packet version - switch (incomingPacket[0]) { - case PACKET_TYPE_TRANSMITTER_DATA_V2: + switch (packetTypeForPacket(incomingPacket)) { + case PacketTypeTransmitterData: // V2 = IOS transmitter app - application->_myTransmitter.processIncomingData(incomingPacket, bytesReceived); + application->getAvatar()->getTransmitter().processIncomingData(reinterpret_cast(incomingPacket.data()), + incomingPacket.size()); break; - case PACKET_TYPE_MIXED_AUDIO: + case PacketTypeMixedAudio: QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection, - Q_ARG(QByteArray, QByteArray((char*) incomingPacket, bytesReceived))); + Q_ARG(QByteArray, incomingPacket)); break; - case PACKET_TYPE_PARTICLE_ADD_RESPONSE: + case PacketTypeParticleAddResponse: // this will keep creatorTokenIDs to IDs mapped correctly - Particle::handleAddParticleResponse(incomingPacket, bytesReceived); + Particle::handleAddParticleResponse(incomingPacket); + application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket); break; - case PACKET_TYPE_PARTICLE_DATA: - case PACKET_TYPE_PARTICLE_ERASE: - case PACKET_TYPE_VOXEL_DATA: - case PACKET_TYPE_VOXEL_ERASE: - case PACKET_TYPE_OCTREE_STATS: - case PACKET_TYPE_ENVIRONMENT_DATA: { + case PacketTypeParticleData: + case PacketTypeParticleErase: + case PacketTypeVoxelData: + case PacketTypeVoxelErase: + case PacketTypeOctreeStats: + case PacketTypeEnvironmentData: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()... _voxelProcessor.queueReceivedPacket()"); bool wantExtraDebugging = application->getLogger()->extraDebugging(); - if (wantExtraDebugging && incomingPacket[0] == PACKET_TYPE_VOXEL_DATA) { + if (wantExtraDebugging && packetTypeForPacket(incomingPacket) == PacketTypeVoxelData) { int numBytesPacketHeader = numBytesForPacketHeader(incomingPacket); - unsigned char* dataAt = incomingPacket + numBytesPacketHeader; + unsigned char* dataAt = reinterpret_cast(incomingPacket.data()) + numBytesPacketHeader; dataAt += sizeof(VOXEL_PACKET_FLAGS); VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt); dataAt += sizeof(VOXEL_PACKET_SEQUENCE); @@ -80,52 +81,49 @@ void DatagramProcessor::processDatagrams() { VOXEL_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); int flightTime = arrivedAt - sentAt; - printf("got PACKET_TYPE_VOXEL_DATA, sequence:%d flightTime:%d\n", sequence, flightTime); + printf("got PacketType_VOXEL_DATA, sequence:%d flightTime:%d\n", sequence, flightTime); } // add this packet to our list of voxel packets and process them on the voxel processing - application->_voxelProcessor.queueReceivedPacket(senderSockAddr, incomingPacket, bytesReceived); + application->_voxelProcessor.queueReceivedPacket(senderSockAddr, incomingPacket); break; } - case PACKET_TYPE_METAVOXEL_DATA: - application->_metavoxels.processData(QByteArray((const char*) incomingPacket, bytesReceived), - senderSockAddr); + case PacketTypeMetavoxelData: + application->_metavoxels.processData(incomingPacket, senderSockAddr); break; - case PACKET_TYPE_BULK_AVATAR_DATA: - case PACKET_TYPE_KILL_AVATAR: { + case PacketTypeBulkAvatarData: + case PacketTypeKillAvatar: { // update having heard from the avatar-mixer and record the bytes received SharedNodePointer avatarMixer = NodeList::getInstance()->nodeWithAddress(senderSockAddr); if (avatarMixer) { avatarMixer->setLastHeardMicrostamp(usecTimestampNow()); - avatarMixer->recordBytesReceived(bytesReceived); + avatarMixer->recordBytesReceived(incomingPacket.size()); - QByteArray datagram(reinterpret_cast(incomingPacket), bytesReceived); - - if (incomingPacket[0] == PACKET_TYPE_BULK_AVATAR_DATA) { + if (packetTypeForPacket(incomingPacket) == PacketTypeBulkAvatarData) { QMetaObject::invokeMethod(&application->getAvatarManager(), "processAvatarMixerDatagram", - Q_ARG(const QByteArray&, datagram), + Q_ARG(const QByteArray&, incomingPacket), Q_ARG(const QWeakPointer&, avatarMixer)); } else { // this is an avatar kill, pass it to the application AvatarManager QMetaObject::invokeMethod(&application->getAvatarManager(), "processKillAvatar", - Q_ARG(const QByteArray&, datagram)); + Q_ARG(const QByteArray&, incomingPacket)); } } - application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived); + application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size()); break; - } - case PACKET_TYPE_DATA_SERVER_GET: - case PACKET_TYPE_DATA_SERVER_PUT: - case PACKET_TYPE_DATA_SERVER_SEND: - case PACKET_TYPE_DATA_SERVER_CONFIRM: - DataServerClient::processMessageFromDataServer(incomingPacket, bytesReceived); + } + case PacketTypeDataServerGet: + case PacketTypeDataServerPut: + case PacketTypeDataServerSend: + case PacketTypeDataServerConfirm: + DataServerClient::processMessageFromDataServer(incomingPacket); break; default: - NodeList::getInstance()->processNodeData(senderSockAddr, incomingPacket, bytesReceived); + NodeList::getInstance()->processNodeData(senderSockAddr, incomingPacket); break; } } } -} \ No newline at end of file +} diff --git a/interface/src/Environment.cpp b/interface/src/Environment.cpp index 46c156f860..59f16fc5eb 100644 --- a/interface/src/Environment.cpp +++ b/interface/src/Environment.cpp @@ -148,32 +148,28 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3 return found; } -int Environment::parseData(const HifiSockAddr& senderAddress, unsigned char* sourceBuffer, int numBytes) { +int Environment::parseData(const HifiSockAddr& senderAddress, const QByteArray& packet) { // push past the packet header - unsigned char* start = sourceBuffer; - - int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer); - sourceBuffer += numBytesPacketHeader; - numBytes -= numBytesPacketHeader; + int bytesRead = numBytesForPacketHeader(packet); // get the lock for the duration of the call QMutexLocker locker(&_mutex); EnvironmentData newData; - while (numBytes > 0) { - int dataLength = newData.parseData(sourceBuffer, numBytes); + while (bytesRead < packet.size()) { + int dataLength = newData.parseData(reinterpret_cast(packet.data()) + bytesRead, + packet.size() - bytesRead); // update the mapping by address/ID _data[senderAddress][newData.getID()] = newData; - sourceBuffer += dataLength; - numBytes -= dataLength; + bytesRead += dataLength; } // remove the default mapping, if any _data.remove(HifiSockAddr()); - return sourceBuffer - start; + return bytesRead; } ProgramObject* Environment::createSkyProgram(const char* from, int* locations) { diff --git a/interface/src/Environment.h b/interface/src/Environment.h index fc572c5e03..273ee54cee 100644 --- a/interface/src/Environment.h +++ b/interface/src/Environment.h @@ -34,7 +34,7 @@ public: bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration); - int parseData(const HifiSockAddr& senderSockAddr, unsigned char* sourceBuffer, int numBytes); + int parseData(const HifiSockAddr& senderSockAddr, const QByteArray& packet); private: diff --git a/interface/src/FileLogger.cpp b/interface/src/FileLogger.cpp index 4c96dfcfa5..81c626a46e 100644 --- a/interface/src/FileLogger.cpp +++ b/interface/src/FileLogger.cpp @@ -14,17 +14,20 @@ #include #include -FileLogger::FileLogger() : _logData(NULL) { - setExtraDebugging(false); - _fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - QDir logDir(_fileName); - if (!logDir.exists(_fileName)) { - logDir.mkdir(_fileName); - } +const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt"; +const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss"; +const QString LOGS_DIRECTORY = "Logs"; +FileLogger::FileLogger(QObject* parent) : + AbstractLoggerInterface(parent), + _logData(NULL) +{ + setExtraDebugging(false); + + _fileName = FileUtils::standardPath(LOGS_DIRECTORY); QHostAddress clientAddress = QHostAddress(getHostOrderLocalAddress()); QDateTime now = QDateTime::currentDateTime(); - _fileName.append(QString("/hifi-log_%1_%2.txt").arg(clientAddress.toString(), now.toString("yyyy-MM-dd_hh.mm.ss"))); + _fileName.append(QString(FILENAME_FORMAT).arg(clientAddress.toString(), now.toString(DATETIME_FORMAT))); } void FileLogger::addMessage(QString message) { @@ -40,5 +43,5 @@ void FileLogger::addMessage(QString message) { } void FileLogger::locateLog() { - FileUtils::LocateFile(_fileName); + FileUtils::locateFile(_fileName); } diff --git a/interface/src/FileLogger.h b/interface/src/FileLogger.h index 6a17032ae2..35cafa4db7 100644 --- a/interface/src/FileLogger.h +++ b/interface/src/FileLogger.h @@ -16,7 +16,7 @@ class FileLogger : public AbstractLoggerInterface { Q_OBJECT public: - FileLogger(); + FileLogger(QObject* parent = NULL); virtual void addMessage(QString); virtual QStringList getLogData() { return _logData; }; diff --git a/interface/src/ImportDialog.cpp b/interface/src/ImportDialog.cpp index c3f499b038..ac7853629c 100644 --- a/interface/src/ImportDialog.cpp +++ b/interface/src/ImportDialog.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -20,38 +19,38 @@ const QString IMPORT_BUTTON_NAME = QObject::tr("Import"); const QString IMPORT_INFO = QObject::tr("Import %1 as voxels"); const QString CANCEL_BUTTON_NAME = QObject::tr("Cancel"); const QString INFO_LABEL_TEXT = QObject::tr("
" - "This will load the selected file into Hifi and allow you
" - "to place it with %1-V; you must be in select or
" - "add mode (S or V keys will toggle mode) to place.
"); + "This will load the selected file into Hifi and allow you
" + "to place it with %1-V; you must be in select or
" + "add mode (S or V keys will toggle mode) to place."); const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); const int SHORT_FILE_EXTENSION = 4; const int SECOND_INDEX_LETTER = 1; QIcon HiFiIconProvider::icon(QFileIconProvider::IconType type) const { - + switchToResourcesParentIfRequired(); - + // types // Computer, Desktop, Trashcan, Network, Drive, Folder, File QString typeString; - + switch (type) { case QFileIconProvider::Computer: typeString = "computer"; break; - + case QFileIconProvider::Desktop: typeString = "desktop"; break; - + case QFileIconProvider::Trashcan: case QFileIconProvider::Network: case QFileIconProvider::Drive: case QFileIconProvider::Folder: typeString = "folder"; break; - + default: typeString = "file"; break; @@ -63,7 +62,7 @@ QIcon HiFiIconProvider::icon(QFileIconProvider::IconType type) const { QIcon HiFiIconProvider::icon(const QFileInfo &info) const { switchToResourcesParentIfRequired(); const QString ext = info.suffix().toLower(); - + if (info.isDir()) { if (info.absoluteFilePath() == QDir::homePath()) { return QIcon("resources/icons/home.svg"); @@ -74,12 +73,12 @@ QIcon HiFiIconProvider::icon(const QFileInfo &info) const { } return QIcon("resources/icons/folder.svg"); } - + QFileInfo iconFile("resources/icons/" + iconsMap[ext]); if (iconFile.exists() && iconFile.isFile()) { return QIcon(iconFile.filePath()); } - + return QIcon("resources/icons/file.svg"); } @@ -98,8 +97,9 @@ QString HiFiIconProvider::type(const QFileInfo &info) const { ImportDialog::ImportDialog(QWidget* parent) : QFileDialog(parent, WINDOW_NAME, DESKTOP_LOCATION, NULL), _importButton(IMPORT_BUTTON_NAME, this), - _cancelButton(CANCEL_BUTTON_NAME, this) { - + _cancelButton(CANCEL_BUTTON_NAME, this), + fileAccepted(false) { + setOption(QFileDialog::DontUseNativeDialog, true); setFileMode(QFileDialog::ExistingFile); setViewMode(QFileDialog::Detail); @@ -111,16 +111,18 @@ ImportDialog::ImportDialog(QWidget* parent) : #endif QLabel* infoLabel = new QLabel(QString(INFO_LABEL_TEXT).arg(cmdString)); infoLabel->setObjectName("infoLabel"); - + QGridLayout* gridLayout = (QGridLayout*) layout(); gridLayout->addWidget(infoLabel, 2, 0, 2, 1); gridLayout->addWidget(&_cancelButton, 2, 1, 2, 1); gridLayout->addWidget(&_importButton, 2, 2, 2, 1); - + setImportTypes(); setLayout(); connect(&_importButton, SIGNAL(pressed()), SLOT(import())); + connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString))); + connect(&_cancelButton, SIGNAL(pressed()), SLOT(close())); connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString))); } @@ -130,12 +132,22 @@ ImportDialog::~ImportDialog() { } void ImportDialog::import() { + fileAccepted = true; emit accepted(); - close(); } void ImportDialog::accept() { - QFileDialog::accept(); + // do nothing if import is not enable + if (!_importButton.isEnabled()) { + return; + } + + if (!fileAccepted) { + fileAccepted = true; + emit accepted(); + } else { + QFileDialog::accept(); + } } void ImportDialog::reject() { @@ -163,72 +175,68 @@ void ImportDialog::saveCurrentFile(QString filename) { } void ImportDialog::setLayout() { - + // set ObjectName used in qss for styling _importButton.setObjectName("importButton"); _cancelButton.setObjectName("cancelButton"); - + // set fixed size _importButton.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); _cancelButton.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - + // hide unused embedded widgets in QFileDialog QWidget* widget = findChild("lookInCombo"); widget->hide(); - + widget = findChild("backButton"); widget->hide(); - + widget = findChild("forwardButton"); widget->hide(); - + widget = findChild("toParentButton"); widget->hide(); - + widget = findChild("newFolderButton"); widget->hide(); - + widget = findChild("listModeButton"); widget->hide(); - + widget = findChild("detailModeButton"); widget->hide(); - + widget = findChild("fileNameEdit"); widget->hide(); - + widget = findChild("fileTypeCombo"); widget->hide(); - + widget = findChild("fileTypeLabel"); widget->hide(); - + widget = findChild("fileNameLabel"); widget->hide(); - + widget = findChild("buttonBox"); widget->hide(); - + QSplitter* splitter = findChild("splitter"); splitter->setHandleWidth(0); - + // remove blue outline on Mac widget = findChild("sidebar"); widget->setAttribute(Qt::WA_MacShowFocusRect, false); - + widget = findChild("treeView"); widget->setAttribute(Qt::WA_MacShowFocusRect, false); - - // remove reference to treeView - widget = NULL; - widget->deleteLater(); - + switchToResourcesParentIfRequired(); QFile styleSheet("resources/styles/import_dialog.qss"); if (styleSheet.open(QIODevice::ReadOnly)) { setStyleSheet(styleSheet.readAll()); } - + } void ImportDialog::setImportTypes() { @@ -251,7 +259,6 @@ void ImportDialog::setImportTypes() { QJsonObject fileFormatObject = fileFormat.toObject(); QString ext(fileFormatObject["extension"].toString()); - QString description(fileFormatObject.value("description").toString()); QString icon(fileFormatObject.value("icon").toString()); if (formatsCounter > 0) { @@ -273,7 +280,7 @@ void ImportDialog::setImportTypes() { // set custom file icons setIconProvider(new HiFiIconProvider(iconsMap)); setNameFilter(importFormatsFilterList); - + #ifdef Q_OS_MAC QString cmdString = ("Command"); #else diff --git a/interface/src/ImportDialog.h b/interface/src/ImportDialog.h index f66321c209..5cfc49e51e 100644 --- a/interface/src/ImportDialog.h +++ b/interface/src/ImportDialog.h @@ -56,6 +56,7 @@ private: void setLayout(); void setImportTypes(); + bool fileAccepted; }; #endif /* defined(__hifi__ImportDialog__) */ diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8fd268938e..67eaa8782c 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -116,10 +116,10 @@ Menu::Menu() : this, SLOT(goToLocation())); addActionToQMenuAndActionHash(fileMenu, - MenuOption::GoToUser, + MenuOption::GoTo, Qt::Key_At, this, - SLOT(goToUser())); + SLOT(goTo())); addDisabledActionAndSeparator(fileMenu, "Settings"); @@ -910,6 +910,60 @@ void Menu::goToDomain() { sendFakeEnterEvent(); } +void Menu::goTo() { + + QInputDialog gotoDialog(Application::getInstance()->getWindow()); + gotoDialog.setWindowTitle("Go to"); + gotoDialog.setLabelText("Destination:"); + QString destination = Application::getInstance()->getProfile()->getUsername(); + gotoDialog.setTextValue(destination); + gotoDialog.setWindowFlags(Qt::Sheet); + gotoDialog.resize(gotoDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, gotoDialog.size().height()); + + int dialogReturn = gotoDialog.exec(); + if (dialogReturn == QDialog::Accepted && !gotoDialog.textValue().isEmpty()) { + + destination = gotoDialog.textValue(); + + QStringList coordinateItems = destination.split(QRegExp("_|,"), QString::SkipEmptyParts); + + const int NUMBER_OF_COORDINATE_ITEMS = 3; + const int X_ITEM = 0; + const int Y_ITEM = 1; + const int Z_ITEM = 2; + if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) { + + double x = replaceLastOccurrence('-', '.', coordinateItems[X_ITEM].trimmed()).toDouble(); + double y = replaceLastOccurrence('-', '.', coordinateItems[Y_ITEM].trimmed()).toDouble(); + double z = replaceLastOccurrence('-', '.', coordinateItems[Z_ITEM].trimmed()).toDouble(); + + glm::vec3 newAvatarPos(x, y, z); + + MyAvatar* myAvatar = Application::getInstance()->getAvatar(); + glm::vec3 avatarPos = myAvatar->getPosition(); + if (newAvatarPos != avatarPos) { + // send a node kill request, indicating to other clients that they should play the "disappeared" effect + MyAvatar::sendKillAvatar(); + + qDebug("Going To Location: %f, %f, %f...", x, y, z); + myAvatar->setPosition(newAvatarPos); + } + + } else { + // there's a username entered by the user, make a request to the data-server + DataServerClient::getValuesForKeysAndUserString( + QStringList() + << DataServerKey::Domain + << DataServerKey::Position + << DataServerKey::Orientation, + destination, Application::getInstance()->getProfile()); + + } + } + + sendFakeEnterEvent(); +} + void Menu::goToLocation() { MyAvatar* myAvatar = Application::getInstance()->getAvatar(); glm::vec3 avatarPos = myAvatar->getPosition(); @@ -954,26 +1008,6 @@ void Menu::goToLocation() { sendFakeEnterEvent(); } -void Menu::goToUser() { - QInputDialog userDialog(Application::getInstance()->getWindow()); - userDialog.setWindowTitle("Go to User"); - userDialog.setLabelText("Destination user:"); - QString username = Application::getInstance()->getProfile()->getUsername(); - userDialog.setTextValue(username); - userDialog.setWindowFlags(Qt::Sheet); - userDialog.resize(userDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, userDialog.size().height()); - - int dialogReturn = userDialog.exec(); - if (dialogReturn == QDialog::Accepted && !userDialog.textValue().isEmpty()) { - // there's a username entered by the user, make a request to the data-server - DataServerClient::getValuesForKeysAndUserString( - QStringList() << DataServerKey::Domain << DataServerKey::Position << DataServerKey::Orientation, - userDialog.textValue(), Application::getInstance()->getProfile()); - } - - sendFakeEnterEvent(); -} - void Menu::pasteToVoxel() { QInputDialog pasteToOctalCodeDialog(Application::getInstance()->getWindow()); pasteToOctalCodeDialog.setWindowTitle("Paste to Voxel"); @@ -1137,3 +1171,14 @@ void Menu::updateFrustumRenderModeAction() { } } +QString Menu::replaceLastOccurrence(QChar search, QChar replace, QString string) { + int lastIndex; + lastIndex = string.lastIndexOf(search); + if (lastIndex > 0) { + lastIndex = string.lastIndexOf(search); + string.replace(lastIndex, 1, replace); + } + + return string; +} + diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 03149ce07c..5e49ca6fd1 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -92,7 +92,7 @@ public slots: void saveSettings(QSettings* settings = NULL); void importSettings(); void exportSettings(); - void goToUser(); + void goTo(); void pasteToVoxel(); private slots: @@ -152,6 +152,7 @@ private: QAction* _useVoxelShader; int _maxVoxelPacketsPerSecond; QMenu* _activeScriptsMenu; + QString replaceLastOccurrence(QChar search, QChar replace, QString string); }; namespace MenuOption { @@ -209,7 +210,7 @@ namespace MenuOption { const QString GlowMode = "Cycle Glow Mode"; const QString GoToDomain = "Go To Domain..."; const QString GoToLocation = "Go To Location..."; - const QString GoToUser = "Go To User..."; + const QString GoTo = "Go To..."; const QString ImportVoxels = "Import Voxels"; const QString ImportVoxelsClipboard = "Import Voxels to Clipboard"; const QString IncreaseAvatarSize = "Increase Avatar Size"; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 258db6da00..311c8f2556 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -6,11 +6,11 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +#include #include #include -#include #include #include "Application.h" @@ -24,6 +24,12 @@ MetavoxelSystem::MetavoxelSystem() : _buffer(QOpenGLBuffer::VertexBuffer) { } +MetavoxelSystem::~MetavoxelSystem() { + for (QHash::const_iterator it = _clients.begin(); it != _clients.end(); it++) { + delete it.value(); + } +} + void MetavoxelSystem::init() { if (!_program.isLinked()) { switchToResourcesParentIfRequired(); @@ -42,19 +48,23 @@ void MetavoxelSystem::init() { _buffer.create(); } +void MetavoxelSystem::applyEdit(const MetavoxelEditMessage& edit) { + foreach (MetavoxelClient* client, _clients) { + client->applyEdit(edit); + } +} + void MetavoxelSystem::processData(const QByteArray& data, const HifiSockAddr& sender) { QMetaObject::invokeMethod(this, "receivedData", Q_ARG(const QByteArray&, data), Q_ARG(const HifiSockAddr&, sender)); } void MetavoxelSystem::simulate(float deltaTime) { // simulate the clients + _points.clear(); foreach (MetavoxelClient* client, _clients) { - client->simulate(deltaTime); + client->simulate(deltaTime, _pointVisitor); } - _points.clear(); - _data.guide(_pointVisitor); - _buffer.bind(); int bytes = _points.size() * sizeof(Point); if (_buffer.size() < bytes) { @@ -107,21 +117,20 @@ void MetavoxelSystem::render() { } void MetavoxelSystem::nodeAdded(SharedNodePointer node) { - if (node->getType() == NODE_TYPE_METAVOXEL_SERVER) { - QMetaObject::invokeMethod(this, "addClient", Q_ARG(const QUuid&, node->getUUID()), - Q_ARG(const HifiSockAddr&, node->getLocalSocket())); + if (node->getType() == NodeType::MetavoxelServer) { + QMetaObject::invokeMethod(this, "addClient", Q_ARG(const SharedNodePointer&, node)); } } void MetavoxelSystem::nodeKilled(SharedNodePointer node) { - if (node->getType() == NODE_TYPE_METAVOXEL_SERVER) { + if (node->getType() == NodeType::MetavoxelServer) { QMetaObject::invokeMethod(this, "removeClient", Q_ARG(const QUuid&, node->getUUID())); } } -void MetavoxelSystem::addClient(const QUuid& uuid, const HifiSockAddr& address) { - MetavoxelClient* client = new MetavoxelClient(address); - _clients.insert(uuid, client); +void MetavoxelSystem::addClient(const SharedNodePointer& node) { + MetavoxelClient* client = new MetavoxelClient(node); + _clients.insert(node->getUUID(), client); _clientsBySessionID.insert(client->getSessionID(), client); } @@ -139,7 +148,7 @@ void MetavoxelSystem::receivedData(const QByteArray& data, const HifiSockAddr& s } MetavoxelClient* client = _clientsBySessionID.value(sessionID); if (client) { - client->receivedData(data, sender); + client->receivedData(data); } } @@ -167,17 +176,15 @@ bool MetavoxelSystem::PointVisitor::visit(MetavoxelInfo& info) { } static QByteArray createDatagramHeader(const QUuid& sessionID) { - QByteArray header(MAX_PACKET_HEADER_BYTES, 0); - populateTypeAndVersion(reinterpret_cast(header.data()), PACKET_TYPE_METAVOXEL_DATA); + QByteArray header = byteArrayWithPopluatedHeader(PacketTypeMetavoxelData); header += sessionID.toRfc4122(); return header; } -MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : - _address(address), +MetavoxelClient::MetavoxelClient(const SharedNodePointer& node) : + _node(node), _sessionID(QUuid::createUuid()), - _sequencer(createDatagramHeader(_sessionID)), - _data(new MetavoxelData()) { + _sequencer(createDatagramHeader(_sessionID)) { connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); @@ -188,23 +195,41 @@ MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : _receiveRecords.append(record); } -void MetavoxelClient::simulate(float deltaTime) { +MetavoxelClient::~MetavoxelClient() { + // close the session + Bitstream& out = _sequencer.startPacket(); + out << QVariant::fromValue(CloseSessionMessage()); + _sequencer.endPacket(); +} + +void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit) { + // apply immediately to local tree + edit.apply(_data); + + // start sending it out + _sequencer.sendHighPriorityMessage(QVariant::fromValue(edit)); +} + +void MetavoxelClient::simulate(float deltaTime, MetavoxelVisitor& visitor) { Bitstream& out = _sequencer.startPacket(); ClientStateMessage state = { Application::getInstance()->getCamera()->getPosition() }; out << QVariant::fromValue(state); _sequencer.endPacket(); + + _data.guide(visitor); } -void MetavoxelClient::receivedData(const QByteArray& data, const HifiSockAddr& sender) { - // save the most recent sender - _address = sender; - +void MetavoxelClient::receivedData(const QByteArray& data) { // process through sequencer _sequencer.receivedDatagram(data); } void MetavoxelClient::sendData(const QByteArray& data) { - NodeList::getInstance()->getNodeSocket().writeDatagram(data, _address.getAddress(), _address.getPort()); + QMutexLocker locker(&_node->getMutex()); + const HifiSockAddr* address = _node->getActiveSocket(); + if (address) { + NodeList::getInstance()->getNodeSocket().writeDatagram(data, address->getAddress(), address->getPort()); + } } void MetavoxelClient::readPacket(Bitstream& in) { @@ -215,6 +240,13 @@ void MetavoxelClient::readPacket(Bitstream& in) { // record the receipt ReceiveRecord record = { _sequencer.getIncomingPacketNumber(), _data }; _receiveRecords.append(record); + + // reapply local edits + foreach (const DatagramSequencer::HighPriorityMessage& message, _sequencer.getHighPriorityMessages()) { + if (message.data.userType() == MetavoxelEditMessage::Type) { + message.data.value().apply(_data); + } + } } void MetavoxelClient::clearReceiveRecordsBefore(int index) { @@ -224,7 +256,7 @@ void MetavoxelClient::clearReceiveRecordsBefore(int index) { void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { int userType = message.userType(); if (userType == MetavoxelDeltaMessage::Type) { - readDelta(_data, _receiveRecords.first().data, in); + _data.readDelta(_receiveRecords.first().data, in); } else if (userType == QMetaType::QVariantList) { foreach (const QVariant& element, message.toList()) { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 708b4d0839..0956a9d0d3 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -19,6 +19,7 @@ #include #include +#include #include "renderer/ProgramObject.h" @@ -31,10 +32,11 @@ class MetavoxelSystem : public QObject { public: MetavoxelSystem(); + ~MetavoxelSystem(); void init(); - MetavoxelData& getData() { return _data; } + void applyEdit(const MetavoxelEditMessage& edit); void processData(const QByteArray& data, const HifiSockAddr& sender); @@ -48,7 +50,7 @@ public slots: private: - Q_INVOKABLE void addClient(const QUuid& uuid, const HifiSockAddr& address); + Q_INVOKABLE void addClient(const SharedNodePointer& node); Q_INVOKABLE void removeClient(const QUuid& uuid); Q_INVOKABLE void receivedData(const QByteArray& data, const HifiSockAddr& sender); @@ -71,7 +73,6 @@ private: static ProgramObject _program; static int _pointScaleLocation; - MetavoxelData _data; QVector _points; PointVisitor _pointVisitor; QOpenGLBuffer _buffer; @@ -86,13 +87,16 @@ class MetavoxelClient : public QObject { public: - MetavoxelClient(const HifiSockAddr& address); + MetavoxelClient(const SharedNodePointer& node); + virtual ~MetavoxelClient(); const QUuid& getSessionID() const { return _sessionID; } - void simulate(float deltaTime); + void applyEdit(const MetavoxelEditMessage& edit); - void receivedData(const QByteArray& data, const HifiSockAddr& sender); + void simulate(float deltaTime, MetavoxelVisitor& visitor); + + void receivedData(const QByteArray& data); private slots: @@ -109,15 +113,15 @@ private: class ReceiveRecord { public: int packetNumber; - MetavoxelDataPointer data; + MetavoxelData data; }; - HifiSockAddr _address; + SharedNodePointer _node; QUuid _sessionID; DatagramSequencer _sequencer; - MetavoxelDataPointer _data; + MetavoxelData _data; QList _receiveRecords; }; diff --git a/interface/src/ParticleTreeRenderer.cpp b/interface/src/ParticleTreeRenderer.cpp index aa4978891f..d96ac2beaa 100644 --- a/interface/src/ParticleTreeRenderer.cpp +++ b/interface/src/ParticleTreeRenderer.cpp @@ -7,6 +7,8 @@ // // +#include + #include "InterfaceConfig.h" #include "ParticleTreeRenderer.h" @@ -16,17 +18,47 @@ ParticleTreeRenderer::ParticleTreeRenderer() : } ParticleTreeRenderer::~ParticleTreeRenderer() { + // delete the models in _particleModels + foreach(Model* model, _particleModels) { + delete model; + } + _particleModels.clear(); } +void ParticleTreeRenderer::init() { + OctreeRenderer::init(); +} + + void ParticleTreeRenderer::update() { if (_tree) { - ParticleTree* tree = (ParticleTree*)_tree; - _tree->lockForWrite(); - tree->update(); - _tree->unlock(); + ParticleTree* tree = static_cast(_tree); + if (tree->tryLockForWrite()) { + tree->update(); + tree->unlock(); + } } } +void ParticleTreeRenderer::render() { + OctreeRenderer::render(); +} + +Model* ParticleTreeRenderer::getModel(const QString& url) { + Model* model = NULL; + + // if we don't already have this model then create it and initialize it + if (_particleModels.find(url) == _particleModels.end()) { + model = new Model(); + model->init(); + model->setURL(QUrl(url)); + _particleModels[url] = model; + } else { + model = _particleModels[url]; + } + return model; +} + void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) { // actually render it here... // we need to iterate the actual particles of the element @@ -36,27 +68,48 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg uint16_t numberOfParticles = particles.size(); - bool drawAsSphere = true; - for (uint16_t i = 0; i < numberOfParticles; i++) { const Particle& particle = particles[i]; // render particle aspoints glm::vec3 position = particle.getPosition() * (float)TREE_SCALE; glColor3ub(particle.getColor()[RED_INDEX],particle.getColor()[GREEN_INDEX],particle.getColor()[BLUE_INDEX]); - float sphereRadius = particle.getRadius() * (float)TREE_SCALE; + float radius = particle.getRadius() * (float)TREE_SCALE; + + bool drawAsModel = particle.hasModel(); args->_renderedItems++; - if (drawAsSphere) { + if (drawAsModel) { glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - glutSolidSphere(sphereRadius, 15, 15); + const float alpha = 1.0f; + + Model* model = getModel(particle.getModelURL()); + + glm::vec3 translationAdjustment = particle.getModelTranslation(); + + // set the position + glm::vec3 translation(position.x, position.y, position.z); + model->setTranslation(translation + translationAdjustment); + + // set the rotation + glm::quat rotation = particle.getModelRotation(); + model->setRotation(rotation); + + // scale + // TODO: need to figure out correct scale adjust, this was arbitrarily set to make a couple models work + const float MODEL_SCALE = 0.00575f; + glm::vec3 scale(1.0f,1.0f,1.0f); + model->setScale(scale * MODEL_SCALE * radius * particle.getModelScale()); + + model->simulate(0.0f); + model->render(alpha); // TODO: should we allow particles to have alpha on their models? + glPopMatrix(); } else { - glPointSize(sphereRadius); - glBegin(GL_POINTS); - glVertex3f(position.x, position.y, position.z); - glEnd(); + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glutSolidSphere(radius, 15, 15); + glPopMatrix(); } } } diff --git a/interface/src/ParticleTreeRenderer.h b/interface/src/ParticleTreeRenderer.h index 1c76cf623d..48b15f9c67 100644 --- a/interface/src/ParticleTreeRenderer.h +++ b/interface/src/ParticleTreeRenderer.h @@ -20,6 +20,7 @@ #include #include #include +#include "renderer/Model.h" // Generic client side Octree renderer class. class ParticleTreeRenderer : public OctreeRenderer { @@ -28,9 +29,9 @@ public: virtual ~ParticleTreeRenderer(); virtual Octree* createTree() { return new ParticleTree(true); } - virtual NODE_TYPE getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; } - virtual PACKET_TYPE getMyQueryMessageType() const { return PACKET_TYPE_PARTICLE_QUERY; } - virtual PACKET_TYPE getExpectedPacketType() const { return PACKET_TYPE_PARTICLE_DATA; } + virtual NodeType_t getMyNodeType() const { return NodeType::ParticleServer; } + virtual PacketType getMyQueryMessageType() const { return PacketTypeParticleQuery; } + virtual PacketType getExpectedPacketType() const { return PacketTypeParticleData; } virtual void renderElement(OctreeElement* element, RenderArgs* args); void update(); @@ -39,7 +40,13 @@ public: void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode); + virtual void init(); + virtual void render(); + protected: + Model* getModel(const QString& url); + + QMap _particleModels; }; #endif /* defined(__hifi__ParticleTreeRenderer__) */ \ No newline at end of file diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index a2e518b2d7..ef7e049d75 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -105,34 +105,6 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { return glm::angleAxis(angle, axis); } -// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's -// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde, -// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java) -glm::vec3 safeEulerAngles(const glm::quat& q) { - float sy = 2.0f * (q.y * q.w - q.x * q.z); - if (sy < 1.0f - EPSILON) { - if (sy > -1.0f + EPSILON) { - return glm::degrees(glm::vec3( - atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)), - asinf(sy), - atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z)))); - - } else { - // not a unique solution; x + z = atan2(-m21, m11) - return glm::degrees(glm::vec3( - 0.0f, - PIf * -0.5f, - atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)))); - } - } else { - // not a unique solution; x - z = atan2(-m21, m11) - return glm::degrees(glm::vec3( - 0.0f, - PIf * 0.5f, - -atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)))); - } -} - // Safe version of glm::mix; based on the code in Nick Bobick's article, // http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde, // https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java) diff --git a/interface/src/Util.h b/interface/src/Util.h index 2a812120f0..09d1fa0484 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -54,8 +54,6 @@ float angleBetween(const glm::vec3& v1, const glm::vec3& v2); glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2); -glm::vec3 safeEulerAngles(const glm::quat& q); - glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha); glm::vec3 extractTranslation(const glm::mat4& matrix); diff --git a/interface/src/VoxelHideShowThread.cpp b/interface/src/VoxelHideShowThread.cpp index 2a42db7f70..d7a25b4b6d 100644 --- a/interface/src/VoxelHideShowThread.cpp +++ b/interface/src/VoxelHideShowThread.cpp @@ -21,15 +21,15 @@ VoxelHideShowThread::VoxelHideShowThread(VoxelSystem* theSystem) : } bool VoxelHideShowThread::process() { - const uint64_t MSECS_TO_USECS = 1000; - const uint64_t SECS_TO_USECS = 1000 * MSECS_TO_USECS; - const uint64_t FRAME_RATE = 60; - const uint64_t USECS_PER_FRAME = SECS_TO_USECS / FRAME_RATE; // every 60fps + const quint64 MSECS_TO_USECS = 1000; + const quint64 SECS_TO_USECS = 1000 * MSECS_TO_USECS; + const quint64 FRAME_RATE = 60; + const quint64 USECS_PER_FRAME = SECS_TO_USECS / FRAME_RATE; // every 60fps - uint64_t start = usecTimestampNow(); + quint64 start = usecTimestampNow(); _theSystem->checkForCulling(); - uint64_t end = usecTimestampNow(); - uint64_t elapsed = end - start; + quint64 end = usecTimestampNow(); + quint64 elapsed = end - start; bool showExtraDebugging = Application::getInstance()->getLogger()->extraDebugging(); if (showExtraDebugging && elapsed > USECS_PER_FRAME) { @@ -38,7 +38,7 @@ bool VoxelHideShowThread::process() { if (isStillRunning()) { if (elapsed < USECS_PER_FRAME) { - uint64_t sleepFor = USECS_PER_FRAME - elapsed; + quint64 sleepFor = USECS_PER_FRAME - elapsed; usleep(sleepFor); } } diff --git a/interface/src/VoxelImporter.cpp b/interface/src/VoxelImporter.cpp index 49b28c850f..653d04cee4 100644 --- a/interface/src/VoxelImporter.cpp +++ b/interface/src/VoxelImporter.cpp @@ -24,15 +24,15 @@ private: const QString SETTINGS_GROUP_NAME = "VoxelImport"; const QString IMPORT_DIALOG_SETTINGS_KEY = "ImportDialogSettings"; -VoxelImporter::VoxelImporter(QWidget* parent) - : QObject(parent), - _voxelTree(true), - _importDialog(parent), - _currentTask(NULL), - _nextTask(NULL) { - - connect(&_importDialog, SIGNAL(currentChanged(QString)), SLOT(preImport())); - connect(&_importDialog, SIGNAL(accepted()), SLOT(import())); +VoxelImporter::VoxelImporter(QWidget* parent) : + QObject(parent), + _voxelTree(true), + _importDialog(parent), + _currentTask(NULL), + _nextTask(NULL) +{ + connect(&_importDialog, &QFileDialog::currentChanged, this, &VoxelImporter::preImport); + connect(&_importDialog, &QFileDialog::accepted, this, &VoxelImporter::import); } void VoxelImporter::saveSettings(QSettings* settings) { @@ -102,7 +102,22 @@ int VoxelImporter::preImport() { if (!QFileInfo(filename).isFile()) { return 0; } - + + _filename = filename; + + if (_nextTask) { + delete _nextTask; + } + + _nextTask = new ImportTask(_filename); + connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask())); + + if (_currentTask != NULL) { + _voxelTree.cancelImport(); + } else { + launchTask(); + } + return 1; } diff --git a/interface/src/VoxelPacketProcessor.cpp b/interface/src/VoxelPacketProcessor.cpp index 6f51dc041d..bdb934430c 100644 --- a/interface/src/VoxelPacketProcessor.cpp +++ b/interface/src/VoxelPacketProcessor.cpp @@ -14,16 +14,18 @@ #include "Menu.h" #include "VoxelPacketProcessor.h" -void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength) { +void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, const QByteArray& packet) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "VoxelPacketProcessor::processPacket()"); + + QByteArray mutablePacket = packet; const int WAY_BEHIND = 300; if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) { qDebug("VoxelPacketProcessor::processPacket() packets to process=%d", packetsToProcessCount()); } - ssize_t messageLength = packetLength; + ssize_t messageLength = mutablePacket.size(); Application* app = Application::getInstance(); bool wasStatsPacket = false; @@ -34,18 +36,19 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns app->_voxels.killLocalVoxels(); app->_wantToKillLocalVoxels = false; } + + PacketType voxelPacketType = packetTypeForPacket(mutablePacket); - // note: PACKET_TYPE_OCTREE_STATS can have PACKET_TYPE_VOXEL_DATA - // immediately following them inside the same packet. So, we process the PACKET_TYPE_OCTREE_STATS first + // note: PacketType_OCTREE_STATS can have PacketType_VOXEL_DATA + // immediately following them inside the same packet. So, we process the PacketType_OCTREE_STATS first // then process any remaining bytes as if it was another packet - if (packetData[0] == PACKET_TYPE_OCTREE_STATS) { + if (voxelPacketType == PacketTypeOctreeStats) { - int statsMessageLength = app->parseOctreeStats(packetData, messageLength, senderSockAddr); + int statsMessageLength = app->parseOctreeStats(mutablePacket, senderSockAddr); wasStatsPacket = true; if (messageLength > statsMessageLength) { - packetData += statsMessageLength; - messageLength -= statsMessageLength; - if (!packetVersionMatch(packetData, senderSockAddr)) { + mutablePacket = mutablePacket.mid(statsMessageLength); + if (!packetVersionMatch(packet)) { return; // bail since piggyback data doesn't match our versioning } } else { @@ -53,31 +56,31 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns return; // bail since no piggyback data } } // fall through to piggyback message - + + voxelPacketType = packetTypeForPacket(mutablePacket); + if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { - app->trackIncomingVoxelPacket(packetData, messageLength, senderSockAddr, wasStatsPacket); + app->trackIncomingVoxelPacket(mutablePacket, senderSockAddr, wasStatsPacket); SharedNodePointer serverNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr); if (serverNode && serverNode->getActiveSocket() && *serverNode->getActiveSocket() == senderSockAddr) { - switch(packetData[0]) { - case PACKET_TYPE_PARTICLE_ERASE: { - app->_particles.processEraseMessage(QByteArray((char*) packetData, messageLength), - senderSockAddr, serverNode.data()); + switch(voxelPacketType) { + case PacketTypeParticleErase: { + app->_particles.processEraseMessage(mutablePacket, senderSockAddr, serverNode.data()); } break; - case PACKET_TYPE_PARTICLE_DATA: { - app->_particles.processDatagram(QByteArray((char*) packetData, messageLength), - senderSockAddr, serverNode.data()); + case PacketTypeParticleData: { + app->_particles.processDatagram(mutablePacket, senderSockAddr, serverNode.data()); } break; - case PACKET_TYPE_ENVIRONMENT_DATA: { - app->_environment.parseData(senderSockAddr, packetData, messageLength); + case PacketTypeEnvironmentData: { + app->_environment.parseData(senderSockAddr, mutablePacket); } break; default : { app->_voxels.setDataSourceUUID(serverNode->getUUID()); - app->_voxels.parseData(packetData, messageLength); + app->_voxels.parseData(mutablePacket); app->_voxels.setDataSourceUUID(QUuid()); } break; } diff --git a/interface/src/VoxelPacketProcessor.h b/interface/src/VoxelPacketProcessor.h index e8e77e6895..e0c3e9f542 100644 --- a/interface/src/VoxelPacketProcessor.h +++ b/interface/src/VoxelPacketProcessor.h @@ -17,6 +17,6 @@ /// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket() class VoxelPacketProcessor : public ReceivedPacketProcessor { protected: - virtual void processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength); + virtual void processPacket(const HifiSockAddr& senderSockAddr, const QByteArray& packet); }; #endif // __shared__VoxelPacketProcessor__ diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index c5071570d5..3297750fb9 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include "Application.h" #include "CoverageMap.h" @@ -550,17 +549,17 @@ bool VoxelSystem::readFromSchematicFile(const char* filename) { return result; } -int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { +int VoxelSystem::parseData(const QByteArray& packet) { bool showTimingDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showTimingDetails, "VoxelSystem::parseData()",showTimingDetails); - unsigned char command = *sourceBuffer; - int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer); + PacketType command = packetTypeForPacket(packet); + int numBytesPacketHeader = numBytesForPacketHeader(packet); switch(command) { - case PACKET_TYPE_VOXEL_DATA: { - PerformanceWarning warn(showTimingDetails, "VoxelSystem::parseData() PACKET_TYPE_VOXEL_DATA part...",showTimingDetails); - - unsigned char* dataAt = sourceBuffer + numBytesPacketHeader; + case PacketTypeVoxelData: { + PerformanceWarning warn(showTimingDetails, "VoxelSystem::parseData() PacketType_VOXEL_DATA part...",showTimingDetails); + + const unsigned char* dataAt = reinterpret_cast(packet.data()) + numBytesPacketHeader; VOXEL_PACKET_FLAGS flags = (*(VOXEL_PACKET_FLAGS*)(dataAt)); dataAt += sizeof(VOXEL_PACKET_FLAGS); @@ -577,7 +576,7 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { int flightTime = arrivedAt - sentAt; VOXEL_PACKET_INTERNAL_SECTION_SIZE sectionLength = 0; - int dataBytes = numBytes - VOXEL_PACKET_HEADER_SIZE; + int dataBytes = packet.size() - VOXEL_PACKET_HEADER_SIZE; int subsection = 1; while (dataBytes > 0) { @@ -605,7 +604,8 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { " color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d" " subsection:%d sectionLength:%d uncompressed:%d", debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed), - sequence, flightTime, numBytes, dataBytes, subsection, sectionLength, packetData.getUncompressedSize()); + sequence, flightTime, packet.size(), dataBytes, subsection, sectionLength, + packetData.getUncompressedSize()); } _tree->readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args); unlockTree(); @@ -616,7 +616,8 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { } subsection++; } - break; + default: + break; } if (!_useFastVoxelPipeline || _writeRenderFullVBO) { setupNewVoxelsForDrawing(); @@ -624,9 +625,9 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY); } - Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::VOXELS).updateValue(numBytes); + Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::VOXELS).updateValue(packet.size()); - return numBytes; + return packet.size(); } void VoxelSystem::setupNewVoxelsForDrawing() { @@ -637,8 +638,8 @@ void VoxelSystem::setupNewVoxelsForDrawing() { return; // bail early if we're not initialized } - uint64_t start = usecTimestampNow(); - uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000; + quint64 start = usecTimestampNow(); + quint64 sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000; bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging if (!iAmDebugging && sinceLastTime <= std::max((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) { @@ -684,7 +685,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { _bufferWriteLock.unlock(); - uint64_t end = usecTimestampNow(); + quint64 end = usecTimestampNow(); int elapsedmsec = (end - start) / 1000; _setupNewVoxelsForDrawingLastFinished = end; _setupNewVoxelsForDrawingLastElapsed = elapsedmsec; @@ -701,8 +702,8 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "setupNewVoxelsForDrawingSingleNode() xxxxx"); - uint64_t start = usecTimestampNow(); - uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000; + quint64 start = usecTimestampNow(); + quint64 sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000; bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging if (allowBailEarly && !iAmDebugging && @@ -727,7 +728,7 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { _bufferWriteLock.unlock(); - uint64_t end = usecTimestampNow(); + quint64 end = usecTimestampNow(); int elapsedmsec = (end - start) / 1000; _setupNewVoxelsForDrawingLastFinished = end; _setupNewVoxelsForDrawingLastElapsed = elapsedmsec; @@ -736,14 +737,14 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { void VoxelSystem::checkForCulling() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "checkForCulling()"); - uint64_t start = usecTimestampNow(); + quint64 start = usecTimestampNow(); // track how long its been since we were last moving. If we have recently moved then only use delta frustums, if // it's been a long time since we last moved, then go ahead and do a full frustum cull. if (isViewChanging()) { _lastViewIsChanging = start; } - uint64_t sinceLastMoving = (start - _lastViewIsChanging) / 1000; + quint64 sinceLastMoving = (start - _lastViewIsChanging) / 1000; bool enoughTime = (sinceLastMoving >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)); // These has changed events will occur before we stop. So we need to remember this for when we finally have stopped @@ -765,7 +766,7 @@ void VoxelSystem::checkForCulling() { hideOutOfView(forceFullFrustum); if (forceFullFrustum) { - uint64_t endViewCulling = usecTimestampNow(); + quint64 endViewCulling = usecTimestampNow(); _lastViewCullingElapsed = (endViewCulling - start) / 1000; } @@ -774,7 +775,7 @@ void VoxelSystem::checkForCulling() { // VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary. cleanupRemovedVoxels(); - uint64_t sinceLastAudit = (start - _lastAudit) / 1000; + quint64 sinceLastAudit = (start - _lastAudit) / 1000; if (Menu::getInstance()->isOptionChecked(MenuOption::AutomaticallyAuditTree)) { if (sinceLastAudit >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)) { @@ -1590,7 +1591,7 @@ void VoxelSystem::falseColorizeBySource() { // create a bunch of colors we'll use during colorization foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { - if (node->getType() == NODE_TYPE_VOXEL_SERVER) { + if (node->getType() == NodeType::VoxelServer) { uint16_t nodeID = VoxelTreeElement::getSourceNodeUUIDKey(node->getUUID()); int groupColor = voxelServerCount % NUMBER_OF_COLOR_GROUPS; args.colors[nodeID] = groupColors[groupColor]; @@ -2662,7 +2663,7 @@ void VoxelSystem::falseColorizeOccludedV2() { } void VoxelSystem::nodeAdded(SharedNodePointer node) { - if (node->getType() == NODE_TYPE_VOXEL_SERVER) { + if (node->getType() == NodeType::VoxelServer) { qDebug("VoxelSystem... voxel server %s added...", node->getUUID().toString().toLocal8Bit().constData()); _voxelServerCount++; } @@ -2683,7 +2684,7 @@ bool VoxelSystem::killSourceVoxelsOperation(OctreeElement* element, void* extraD } void VoxelSystem::nodeKilled(SharedNodePointer node) { - if (node->getType() == NODE_TYPE_VOXEL_SERVER) { + if (node->getType() == NodeType::VoxelServer) { _voxelServerCount--; QUuid nodeUUID = node->getUUID(); qDebug("VoxelSystem... voxel server %s removed...", nodeUUID.toString().toLocal8Bit().constData()); diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 728106a922..ca90424b70 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -49,7 +49,7 @@ public: void setDataSourceUUID(const QUuid& dataSourceUUID) { _dataSourceUUID = dataSourceUUID; } const QUuid& getDataSourceUUID() const { return _dataSourceUUID; } - int parseData(unsigned char* sourceBuffer, int numBytes); + int parseData(const QByteArray& packet); virtual void init(); void simulate(float deltaTime) { } @@ -229,10 +229,10 @@ private: bool _readRenderFullVBO; int _setupNewVoxelsForDrawingLastElapsed; - uint64_t _setupNewVoxelsForDrawingLastFinished; - uint64_t _lastViewCulling; - uint64_t _lastViewIsChanging; - uint64_t _lastAudit; + quint64 _setupNewVoxelsForDrawingLastFinished; + quint64 _lastViewCulling; + quint64 _lastViewIsChanging; + quint64 _lastAudit; int _lastViewCullingElapsed; bool _hasRecentlyChanged; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 5e41619997..4b4c189729 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include @@ -73,7 +72,6 @@ Avatar::Avatar() : _worldUpDirection(DEFAULT_UP_DIRECTION), _mouseRayOrigin(0.0f, 0.0f, 0.0f), _mouseRayDirection(0.0f, 0.0f, 0.0f), - _isCollisionsOn(true), _moving(false), _owningAvatarMixer(), _initialized(false) @@ -108,7 +106,7 @@ glm::quat Avatar::getWorldAlignedOrientation () const { return computeRotationFromBodyToWorldUp() * getOrientation(); } -void Avatar::simulate(float deltaTime, Transmitter* transmitter) { +void Avatar::simulate(float deltaTime) { if (_scale != _targetScale) { setScale(_targetScale); } @@ -336,11 +334,11 @@ bool Avatar::findSphereCollision(const glm::vec3& sphereCenter, float sphereRadi return false; } -int Avatar::parseData(unsigned char* sourceBuffer, int numBytes) { +int Avatar::parseData(const QByteArray& packet) { // change in position implies movement glm::vec3 oldPosition = _position; - int bytesRead = AvatarData::parseData(sourceBuffer, numBytes); + int bytesRead = AvatarData::parseData(packet); const float MOVE_DISTANCE_THRESHOLD = 0.001f; _moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD; @@ -396,30 +394,6 @@ void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, glEnd(); } -void Avatar::goHome() { - qDebug("Going Home!"); - setPosition(START_LOCATION); -} - -void Avatar::increaseSize() { - if ((1.f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) { - _targetScale *= (1.f + SCALING_RATIO); - qDebug("Changed scale to %f", _targetScale); - } -} - -void Avatar::decreaseSize() { - if (MIN_AVATAR_SCALE < (1.f - SCALING_RATIO) * _targetScale) { - _targetScale *= (1.f - SCALING_RATIO); - qDebug("Changed scale to %f", _targetScale); - } -} - -void Avatar::resetSize() { - _targetScale = 1.0f; - qDebug("Reseted scale to %f", _targetScale); -} - void Avatar::setScale(float scale) { _scale = scale; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 10a3760f6e..61b36706d2 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -20,7 +20,6 @@ #include "InterfaceConfig.h" #include "SkeletonModel.h" #include "world.h" -#include "devices/Transmitter.h" static const float SCALING_RATIO = .05f; static const float SMOOTHING_RATIO = .05f; // 0 < ratio < 1 @@ -73,7 +72,7 @@ public: ~Avatar(); void init(); - void simulate(float deltaTime, Transmitter* transmitter); + void simulate(float deltaTime); void render(bool forceRenderHead); //setters @@ -114,20 +113,10 @@ public: virtual bool isMyAvatar() { return false; } - int parseData(unsigned char* sourceBuffer, int numBytes); + int parseData(const QByteArray& packet); static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2); -public slots: - void setWantCollisionsOn(bool wantCollisionsOn) { _isCollisionsOn = wantCollisionsOn; } - void goHome(); - void increaseSize(); - void decreaseSize(); - void resetSize(); - - friend class MyAvatar; - - protected: Head _head; Hand _hand; @@ -142,7 +131,6 @@ protected: glm::vec3 _worldUpDirection; glm::vec3 _mouseRayOrigin; glm::vec3 _mouseRayDirection; - bool _isCollisionsOn; float _stringLength; bool _moving; ///< set when position is changing QWeakPointer _owningAvatarMixer; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index dd1686a0d2..d98d30cf6a 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -16,65 +16,49 @@ #include "AvatarManager.h" +// We add _myAvatar into the hash with all the other AvatarData, and we use the default NULL QUid as the key. +const QUuid MY_AVATAR_KEY; // NULL key + AvatarManager::AvatarManager(QObject* parent) : - _lookAtTargetAvatar(), - _lookAtOtherPosition(), - _lookAtIndicatorScale(1.0f), - _avatarHash(), - _avatarFades() -{ + _avatarFades() { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); + _myAvatar = QSharedPointer(new MyAvatar()); } -void AvatarManager::updateLookAtTargetAvatar(glm::vec3 &eyePosition) { - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()"); - - Application* applicationInstance = Application::getInstance(); - - if (!applicationInstance->isMousePressed()) { - foreach (const AvatarSharedPointer& avatar, _avatarHash) { - float distance; - - if (avatar->findRayIntersection(applicationInstance->getMouseRayOrigin(), - applicationInstance->getMouseRayDirection(), distance)) { - // rescale to compensate for head embiggening - eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) * - (avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot(); - - _lookAtIndicatorScale = avatar->getHead().getScale(); - _lookAtOtherPosition = avatar->getHead().getPosition(); - - _lookAtTargetAvatar = avatar; - - // found the look at target avatar, return - return; - } - } - - _lookAtTargetAvatar.clear(); - } +void AvatarManager::init() { + _myAvatar->init(); + _myAvatar->setPosition(START_LOCATION); + _myAvatar->setDisplayingLookatVectors(false); + _avatarHash.insert(MY_AVATAR_KEY, _myAvatar); } -void AvatarManager::updateAvatars(float deltaTime) { +void AvatarManager::updateOtherAvatars(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); - + + Application* applicationInstance = Application::getInstance(); + glm::vec3 mouseOrigin = applicationInstance->getMouseRayOrigin(); + glm::vec3 mouseDirection = applicationInstance->getMouseRayDirection(); + // simulate avatars - AvatarHash::iterator avatar = _avatarHash.begin(); - if (avatar != _avatarHash.end()) { - if (avatar->data()->getOwningAvatarMixer()) { + AvatarHash::iterator avatarIterator = _avatarHash.begin(); + while (avatarIterator != _avatarHash.end()) { + Avatar* avatar = static_cast(avatarIterator.value().data()); + if (avatar == static_cast(_myAvatar.data())) { + // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. + //updateMyAvatar(deltaTime); + ++avatarIterator; + continue; + } + if (avatar->getOwningAvatarMixer()) { // this avatar's mixer is still around, go ahead and simulate it - avatar->data()->simulate(deltaTime, NULL); - - Application* applicationInstance = Application::getInstance(); - - avatar->data()->setMouseRay(applicationInstance->getMouseRayOrigin(), - applicationInstance->getMouseRayDirection()); + avatar->simulate(deltaTime); + avatar->setMouseRay(mouseOrigin, mouseDirection); + ++avatarIterator; } else { // the mixer that owned this avatar is gone, give it to the vector of fades and kill it - avatar = removeAvatarAtHashIterator(avatar); + avatarIterator = erase(avatarIterator); } } @@ -88,41 +72,44 @@ void AvatarManager::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) { } PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::renderAvatars()"); + bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors); if (!selfAvatarOnly) { - // Render avatars of other nodes - foreach (const AvatarSharedPointer& avatar, _avatarHash) { + foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) { + Avatar* avatar = static_cast(avatarPointer.data()); if (!avatar->isInitialized()) { avatar->init(); } - avatar->render(false); - avatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors)); + if (avatar == static_cast(_myAvatar.data())) { + avatar->render(forceRenderHead); + } else { + avatar->render(false); + } + avatar->setDisplayingLookatVectors(renderLookAtVectors); } - renderAvatarFades(); + } else { + // just render myAvatar + _myAvatar->render(forceRenderHead); + _myAvatar->setDisplayingLookatVectors(renderLookAtVectors); } - - // Render my own Avatar - Avatar* myAvatar = Application::getInstance()->getAvatar(); - myAvatar->render(forceRenderHead); - myAvatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors)); } void AvatarManager::simulateAvatarFades(float deltaTime) { - QVector::iterator fadingAvatar = _avatarFades.begin(); + QVector::iterator fadingIterator = _avatarFades.begin(); - while (fadingAvatar != _avatarFades.end()) { - const float SHRINK_RATE = 0.9f; - - fadingAvatar->data()->setTargetScale(fadingAvatar->data()->getScale() * SHRINK_RATE); - - const float MIN_FADE_SCALE = 0.001f; - - if (fadingAvatar->data()->getTargetScale() < MIN_FADE_SCALE) { - fadingAvatar = _avatarFades.erase(fadingAvatar); + const float SHRINK_RATE = 0.9f; + const float MIN_FADE_SCALE = 0.001f; + + while (fadingIterator != _avatarFades.end()) { + Avatar* avatar = static_cast(fadingIterator->data()); + avatar->setTargetScale(avatar->getScale() * SHRINK_RATE); + if (avatar->getTargetScale() < MIN_FADE_SCALE) { + fadingIterator = _avatarFades.erase(fadingIterator); } else { - fadingAvatar->data()->simulate(deltaTime, NULL); + avatar->simulate(deltaTime); + ++fadingIterator; } } } @@ -132,29 +119,36 @@ void AvatarManager::renderAvatarFades() { Glower glower; foreach(const AvatarSharedPointer& fadingAvatar, _avatarFades) { - fadingAvatar->render(false); + Avatar* avatar = static_cast(fadingAvatar.data()); + avatar->render(false); } } void AvatarManager::processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList &valueList) { + QUuid avatarKey = QUuid(userString); + if (avatarKey == MY_AVATAR_KEY) { + // ignore updates to our own mesh + return; + } for (int i = 0; i < keyList.size(); i++) { if (valueList[i] != " ") { if (keyList[i] == DataServerKey::FaceMeshURL || keyList[i] == DataServerKey::SkeletonURL) { // mesh URL for a UUID, find avatar in our list - AvatarSharedPointer matchingAvatar = _avatarHash.value(QUuid(userString)); + AvatarSharedPointer matchingAvatar = _avatarHash.value(avatarKey); if (matchingAvatar) { + Avatar* avatar = static_cast(matchingAvatar.data()); if (keyList[i] == DataServerKey::FaceMeshURL) { qDebug() << "Changing mesh to" << valueList[i] << "for avatar with UUID" - << uuidStringWithoutCurlyBraces(QUuid(userString)); + << uuidStringWithoutCurlyBraces(avatarKey); - QMetaObject::invokeMethod(&matchingAvatar->getHead().getFaceModel(), + QMetaObject::invokeMethod(&(avatar->getHead().getFaceModel()), "setURL", Q_ARG(QUrl, QUrl(valueList[i]))); } else if (keyList[i] == DataServerKey::SkeletonURL) { qDebug() << "Changing skeleton to" << valueList[i] << "for avatar with UUID" - << uuidStringWithoutCurlyBraces(QString(userString)); + << uuidStringWithoutCurlyBraces(avatarKey.toString()); - QMetaObject::invokeMethod(&matchingAvatar->getSkeletonModel(), + QMetaObject::invokeMethod(&(avatar->getSkeletonModel()), "setURL", Q_ARG(QUrl, QUrl(valueList[i]))); } } @@ -164,15 +158,12 @@ void AvatarManager::processDataServerResponse(const QString& userString, const Q } void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer& mixerWeakPointer) { - unsigned char packetData[MAX_PACKET_SIZE]; - memcpy(packetData, datagram.data(), datagram.size()); - int numBytesPacketHeader = numBytesForPacketHeader(packetData); + int bytesRead = numBytesForPacketHeader(datagram); - int bytesRead = numBytesPacketHeader; - - unsigned char avatarData[MAX_PACKET_SIZE]; - int numBytesDummyPacketHeader = populateTypeAndVersion(avatarData, PACKET_TYPE_HEAD_DATA); + QByteArray dummyAvatarByteArray = byteArrayWithPopluatedHeader(PacketTypeAvatarData); + int numDummyHeaderBytes = dummyAvatarByteArray.size(); + int numDummyHeaderBytesWithoutUUID = numDummyHeaderBytes - NUM_BYTES_RFC4122_UUID; // enumerate over all of the avatars in this packet // only add them if mixerWeakPointer points to something (meaning that mixer is still around) @@ -183,10 +174,11 @@ void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const if (!matchingAvatar) { // construct a new Avatar for this node - matchingAvatar = AvatarSharedPointer(new Avatar()); - matchingAvatar->setOwningAvatarMixer(mixerWeakPointer); + Avatar* avatar = new Avatar(); + avatar->setOwningAvatarMixer(mixerWeakPointer); // insert the new avatar into our hash + matchingAvatar = AvatarSharedPointer(avatar); _avatarHash.insert(nodeUUID, matchingAvatar); // new UUID requires mesh and skeleton request to data-server @@ -197,37 +189,44 @@ void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const } // copy the rest of the packet to the avatarData holder so we can read the next Avatar from there - memcpy(avatarData + numBytesDummyPacketHeader, packetData + bytesRead, datagram.size() - bytesRead); + dummyAvatarByteArray.resize(numDummyHeaderBytesWithoutUUID); + + // make this Avatar's UUID the UUID in the packet and tack the remaining data onto the end + dummyAvatarByteArray.append(datagram.mid(bytesRead)); // have the matching (or new) avatar parse the data from the packet - bytesRead += matchingAvatar->parseData(avatarData, datagram.size() - bytesRead); + bytesRead += matchingAvatar->parseData(dummyAvatarByteArray) - numDummyHeaderBytesWithoutUUID; } } void AvatarManager::processKillAvatar(const QByteArray& datagram) { // read the node id - QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(numBytesForPacketHeader(reinterpret_cast - (datagram.data())), - NUM_BYTES_RFC4122_UUID)); + QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(numBytesForPacketHeader(datagram), NUM_BYTES_RFC4122_UUID)); // remove the avatar with that UUID from our hash, if it exists AvatarHash::iterator matchedAvatar = _avatarHash.find(nodeUUID); if (matchedAvatar != _avatarHash.end()) { - removeAvatarAtHashIterator(matchedAvatar); + erase(matchedAvatar); } } -AvatarHash::iterator AvatarManager::removeAvatarAtHashIterator(const AvatarHash::iterator& iterator) { - qDebug() << "Removing Avatar with UUID" << iterator.key() << "from AvatarManager hash."; - _avatarFades.push_back(iterator.value()); - return _avatarHash.erase(iterator); +AvatarHash::iterator AvatarManager::erase(const AvatarHash::iterator& iterator) { + if (iterator.key() != MY_AVATAR_KEY) { + qDebug() << "Removing Avatar with UUID" << iterator.key() << "from AvatarManager hash."; + _avatarFades.push_back(iterator.value()); + return AvatarHashMap::erase(iterator); + } else { + // never remove _myAvatar from the list + AvatarHash::iterator returnIterator = iterator; + return ++returnIterator; + } } -void AvatarManager::clearHash() { - // clear the AvatarManager hash - typically happens on the removal of the avatar-mixer +void AvatarManager::clearOtherAvatars() { + // clear any avatars that came from an avatar-mixer AvatarHash::iterator removeAvatar = _avatarHash.begin(); - while (removeAvatar != _avatarHash.end()) { - removeAvatar = removeAvatarAtHashIterator(removeAvatar); + removeAvatar = erase(removeAvatar); } -} \ No newline at end of file + _myAvatar->clearLookAtTargetAvatar(); +} diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 7d0f11646d..6605f5c234 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -13,30 +13,27 @@ #include #include +#include #include #include "Avatar.h" -typedef QSharedPointer AvatarSharedPointer; -typedef QHash AvatarHash; +class MyAvatar; -class AvatarManager : public QObject, public DataServerCallbackObject { +class AvatarManager : public QObject, public DataServerCallbackObject, public AvatarHashMap { Q_OBJECT public: AvatarManager(QObject* parent = 0); + + void init(); + + MyAvatar* getMyAvatar() { return _myAvatar.data(); } - const AvatarHash& getAvatarHash() { return _avatarHash; } - int size() const { return _avatarHash.size(); } - - Avatar* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); } - - void updateLookAtTargetAvatar(glm::vec3& eyePosition); - - void updateAvatars(float deltaTime); + void updateOtherAvatars(float deltaTime); void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false); - void clearHash(); - + void clearOtherAvatars(); + public slots: void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList); @@ -44,17 +41,16 @@ public slots: void processKillAvatar(const QByteArray& datagram); private: + AvatarManager(const AvatarManager& other); + void simulateAvatarFades(float deltaTime); void renderAvatarFades(); - AvatarHash::iterator removeAvatarAtHashIterator(const AvatarHash::iterator& iterator); + // virtual override + AvatarHash::iterator erase(const AvatarHash::iterator& iterator); - QWeakPointer _lookAtTargetAvatar; - glm::vec3 _lookAtOtherPosition; - float _lookAtIndicatorScale; - - AvatarHash _avatarHash; QVector _avatarFades; + QSharedPointer _myAvatar; }; #endif /* defined(__hifi__AvatarManager__) */ diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 18d6031f98..b441000cc1 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -183,7 +183,12 @@ void Hand::updateCollisions() { glm::vec3 totalPenetration; // check other avatars - foreach (const AvatarSharedPointer& avatar, Application::getInstance()->getAvatarManager().getAvatarHash()) { + foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) { + Avatar* avatar = static_cast(avatarPointer.data()); + if (avatar == _owningAvatar) { + // don't collid with our own hands + continue; + } if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) { // Check for palm collisions glm::vec3 myPalmPosition = palm.getPosition(); @@ -205,9 +210,9 @@ void Hand::updateCollisions() { const float PALM_COLLIDE_DURATION_MAX = 0.75f; const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f; Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME, - PALM_COLLIDE_FREQUENCY, - PALM_COLLIDE_DURATION_MAX, - PALM_COLLIDE_DECAY_PER_SAMPLE); + PALM_COLLIDE_FREQUENCY, + PALM_COLLIDE_DURATION_MAX, + PALM_COLLIDE_DECAY_PER_SAMPLE); // If the other person's palm is in motion, move mine downward to show I was hit const float MIN_VELOCITY_FOR_SLAP = 0.05f; if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) { diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 78e93996c8..2269d1d4a6 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -37,8 +37,6 @@ Head::Head(Avatar* owningAvatar) : _leftEyeBlinkVelocity(0.0f), _rightEyeBlinkVelocity(0.0f), _timeWithoutTalking(0.0f), - _cameraPitch(_pitch), - _mousePitch(0.f), _cameraYaw(_yaw), _isCameraMoving(false), _faceModel(this) @@ -52,7 +50,6 @@ void Head::init() { void Head::reset() { _yaw = _pitch = _roll = 0.0f; - _mousePitch = 0.0f; _leanForward = _leanSideways = 0.0f; _faceModel.reset(); } @@ -186,13 +183,6 @@ void Head::setScale (float scale) { _scale = scale; } -void Head::setMousePitch(float mousePitch) { - const float MAX_PITCH = 90.0f; - _mousePitch = glm::clamp(mousePitch, -MAX_PITCH, MAX_PITCH); -} - - - glm::quat Head::getOrientation() const { return glm::quat(glm::radians(_bodyRotation)) * glm::quat(glm::radians(glm::vec3(_pitch, _yaw, _roll))); } @@ -200,7 +190,7 @@ glm::quat Head::getOrientation() const { glm::quat Head::getCameraOrientation () const { Avatar* owningAvatar = static_cast(_owningAvatar); return owningAvatar->getWorldAlignedOrientation() - * glm::quat(glm::radians(glm::vec3(_cameraPitch + _mousePitch, _cameraYaw, 0.0f))); + * glm::quat(glm::radians(glm::vec3(_pitch, _cameraYaw, 0.0f))); } glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const { diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 5d9f9d9bbd..94b8bd3dc7 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -46,9 +46,6 @@ public: void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; } void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; } - float getMousePitch() const { return _mousePitch; } - void setMousePitch(float mousePitch); - glm::quat getOrientation() const; glm::quat getCameraOrientation () const; const glm::vec3& getAngularVelocity() const { return _angularVelocity; } @@ -99,8 +96,6 @@ private: float _leftEyeBlinkVelocity; float _rightEyeBlinkVelocity; float _timeWithoutTalking; - float _cameraPitch; // Used to position the camera differently from the head - float _mousePitch; float _cameraYaw; bool _isCameraMoving; FaceModel _faceModel; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index ca7a3b863b..0bdc5e9748 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -12,15 +12,18 @@ #include #include -#include #include #include #include "Application.h" +#include "Audio.h" #include "DataServerClient.h" +#include "Environment.h" #include "Menu.h" #include "MyAvatar.h" #include "Physics.h" +#include "VoxelSystem.h" +#include "devices/Faceshift.h" #include "devices/OculusManager.h" #include "ui/TextRenderer.h" @@ -50,19 +53,32 @@ MyAvatar::MyAvatar() : _elapsedTimeSinceCollision(0.0f), _lastCollisionPosition(0, 0, 0), _speedBrakes(false), + _isCollisionsOn(true), _isThrustOn(false), _thrustMultiplier(1.0f), _moveTarget(0,0,0), - _moveTargetStepCounter(0) + _moveTargetStepCounter(0), + _lookAtTargetAvatar() { for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } } +MyAvatar::~MyAvatar() { + _lookAtTargetAvatar.clear(); +} + void MyAvatar::reset() { + // TODO? resurrect headMouse stuff? + //_headMouseX = _glWidget->width() / 2; + //_headMouseY = _glWidget->height() / 2; _head.reset(); _hand.reset(); + + setVelocity(glm::vec3(0,0,0)); + setThrust(glm::vec3(0,0,0)); + _transmitter.resetLevels(); } void MyAvatar::setMoveTarget(const glm::vec3 moveTarget) { @@ -70,7 +86,89 @@ void MyAvatar::setMoveTarget(const glm::vec3 moveTarget) { _moveTargetStepCounter = 0; } -void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) { +void MyAvatar::updateTransmitter(float deltaTime) { + // no transmitter drive implies transmitter pick + if (!Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _transmitter.isConnected()) { + _transmitterPickStart = getChestPosition(); + glm::vec3 direction = getOrientation() * glm::quat(glm::radians(_transmitter.getEstimatedRotation())) * IDENTITY_FRONT; + + // check against voxels, avatars + const float MAX_PICK_DISTANCE = 100.0f; + float minDistance = MAX_PICK_DISTANCE; + VoxelDetail detail; + float distance; + BoxFace face; + VoxelSystem* voxels = Application::getInstance()->getVoxels(); + if (voxels->findRayIntersection(_transmitterPickStart, direction, detail, distance, face)) { + minDistance = min(minDistance, distance); + } + _transmitterPickEnd = _transmitterPickStart + direction * minDistance; + + } else { + _transmitterPickStart = _transmitterPickEnd = glm::vec3(); + } +} + +void MyAvatar::update(float deltaTime) { + updateTransmitter(deltaTime); + + // TODO: resurrect touch interactions between avatars + //// rotate body yaw for yaw received from multitouch + //setOrientation(getOrientation() * glm::quat(glm::vec3(0, _yawFromTouch, 0))); + //_yawFromTouch = 0.f; + // + //// apply pitch from touch + //_head.setPitch(_head.getPitch() + _pitchFromTouch); + //_pitchFromTouch = 0.0f; + // + //float TOUCH_YAW_SCALE = -0.25f; + //float TOUCH_PITCH_SCALE = -12.5f; + //float FIXED_TOUCH_TIMESTEP = 0.016f; + //_yawFromTouch += ((_touchAvgX - _lastTouchAvgX) * TOUCH_YAW_SCALE * FIXED_TOUCH_TIMESTEP); + //_pitchFromTouch += ((_touchAvgY - _lastTouchAvgY) * TOUCH_PITCH_SCALE * FIXED_TOUCH_TIMESTEP); + + // Update my avatar's state from gyros + updateFromGyros(Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)); + + // Update head mouse from faceshift if active + Faceshift* faceshift = Application::getInstance()->getFaceshift(); + if (faceshift->isActive()) { + glm::vec3 headVelocity = faceshift->getHeadAngularVelocity(); + + // TODO? resurrect headMouse stuff? + //// sets how quickly head angular rotation moves the head mouse + //const float HEADMOUSE_FACESHIFT_YAW_SCALE = 40.f; + //const float HEADMOUSE_FACESHIFT_PITCH_SCALE = 30.f; + //_headMouseX -= headVelocity.y * HEADMOUSE_FACESHIFT_YAW_SCALE; + //_headMouseY -= headVelocity.x * HEADMOUSE_FACESHIFT_PITCH_SCALE; + // + //// Constrain head-driven mouse to edges of screen + //_headMouseX = glm::clamp(_headMouseX, 0, _glWidget->width()); + //_headMouseY = glm::clamp(_headMouseY, 0, _glWidget->height()); + } + + if (OculusManager::isConnected()) { + float yaw, pitch, roll; + OculusManager::getEulerAngles(yaw, pitch, roll); + + _head.setYaw(yaw); + _head.setPitch(pitch); + _head.setRoll(roll); + } + + // Get audio loudness data from audio input device + _head.setAudioLoudness(Application::getInstance()->getAudio()->getLastInputLoudness()); + + if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) { + setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition())); + } else { + setGravity(glm::vec3(0.0f, 0.0f, 0.0f)); + } + + simulate(deltaTime); +} + +void MyAvatar::simulate(float deltaTime) { glm::quat orientation = getOrientation(); @@ -92,7 +190,7 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) { } // Collect thrust forces from keyboard and devices - updateThrust(deltaTime, transmitter); + updateThrust(deltaTime); // copy velocity so we can use it later for acceleration glm::vec3 oldVelocity = getVelocity(); @@ -238,7 +336,7 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) { // Adjust body yaw by yaw from controller setOrientation(glm::angleAxis(-euler.y, glm::vec3(0, 1, 0)) * getOrientation()); // Adjust head pitch from controller - getHead().setMousePitch(getHead().getMousePitch() - euler.x); + getHead().setPitch(getHead().getPitch() - euler.x); _position += _velocity * deltaTime; @@ -284,8 +382,6 @@ void MyAvatar::updateFromGyros(bool turnWithHead) { } } } else { - _head.setPitch(_head.getMousePitch()); - // restore rotation, lean to neutral positions const float RESTORE_RATE = 0.05f; _head.setYaw(glm::mix(_head.getYaw(), 0.0f, RESTORE_RATE)); @@ -427,6 +523,74 @@ void MyAvatar::render(bool forceRenderHead) { } } +void MyAvatar::renderHeadMouse() const { + // TODO? resurrect headMouse stuff? + /* + // Display small target box at center or head mouse target that can also be used to measure LOD + glColor3f(1.0, 1.0, 1.0); + glDisable(GL_LINE_SMOOTH); + const int PIXEL_BOX = 16; + glBegin(GL_LINES); + glVertex2f(_headMouseX - PIXEL_BOX/2, _headMouseY); + glVertex2f(_headMouseX + PIXEL_BOX/2, _headMouseY); + glVertex2f(_headMouseX, _headMouseY - PIXEL_BOX/2); + glVertex2f(_headMouseX, _headMouseY + PIXEL_BOX/2); + glEnd(); + glEnable(GL_LINE_SMOOTH); + glColor3f(1.f, 0.f, 0.f); + glPointSize(3.0f); + glDisable(GL_POINT_SMOOTH); + glBegin(GL_POINTS); + glVertex2f(_headMouseX - 1, _headMouseY + 1); + glEnd(); + // If Faceshift is active, show eye pitch and yaw as separate pointer + if (_faceshift.isActive()) { + const float EYE_TARGET_PIXELS_PER_DEGREE = 40.0; + int eyeTargetX = (_glWidget->width() / 2) - _faceshift.getEstimatedEyeYaw() * EYE_TARGET_PIXELS_PER_DEGREE; + int eyeTargetY = (_glWidget->height() / 2) - _faceshift.getEstimatedEyePitch() * EYE_TARGET_PIXELS_PER_DEGREE; + + glColor3f(0.0, 1.0, 1.0); + glDisable(GL_LINE_SMOOTH); + glBegin(GL_LINES); + glVertex2f(eyeTargetX - PIXEL_BOX/2, eyeTargetY); + glVertex2f(eyeTargetX + PIXEL_BOX/2, eyeTargetY); + glVertex2f(eyeTargetX, eyeTargetY - PIXEL_BOX/2); + glVertex2f(eyeTargetX, eyeTargetY + PIXEL_BOX/2); + glEnd(); + + } + */ +} + +void MyAvatar::renderTransmitterPickRay() const { + if (_transmitterPickStart != _transmitterPickEnd) { + Glower glower; + const float TRANSMITTER_PICK_COLOR[] = { 1.0f, 1.0f, 0.0f }; + glColor3fv(TRANSMITTER_PICK_COLOR); + glLineWidth(3.0f); + glBegin(GL_LINES); + glVertex3f(_transmitterPickStart.x, _transmitterPickStart.y, _transmitterPickStart.z); + glVertex3f(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z); + glEnd(); + glLineWidth(1.0f); + + glPushMatrix(); + glTranslatef(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z); + + const float PICK_END_RADIUS = 0.025f; + glutSolidSphere(PICK_END_RADIUS, 8, 8); + + glPopMatrix(); + } +} + +void MyAvatar::renderTransmitterLevels(int width, int height) const { + // Show hand transmitter data if detected + if (_transmitter.isConnected()) { + _transmitter.renderLevels(width, height); + } +} + void MyAvatar::saveData(QSettings* settings) { settings->beginGroup("Avatar"); @@ -434,7 +598,7 @@ void MyAvatar::saveData(QSettings* settings) { settings->setValue("bodyPitch", _bodyPitch); settings->setValue("bodyRoll", _bodyRoll); - settings->setValue("mousePitch", _head.getMousePitch()); + settings->setValue("headPitch", _head.getPitch()); settings->setValue("position_x", _position.x); settings->setValue("position_y", _position.y); @@ -456,7 +620,7 @@ void MyAvatar::loadData(QSettings* settings) { _bodyPitch = loadSetting(settings, "bodyPitch", 0.0f); _bodyRoll = loadSetting(settings, "bodyRoll", 0.0f); - _head.setMousePitch(loadSetting(settings, "mousePitch", 0.0f)); + _head.setPitch(loadSetting(settings, "headPitch", 0.0f)); _position.x = loadSetting(settings, "position_x", 0.0f); _position.y = loadSetting(settings, "position_y", 0.0f); @@ -473,18 +637,8 @@ void MyAvatar::loadData(QSettings* settings) { } void MyAvatar::sendKillAvatar() { - unsigned char packet[MAX_PACKET_SIZE]; - unsigned char* packetPosition = packet; - - packetPosition += populateTypeAndVersion(packetPosition, PACKET_TYPE_KILL_AVATAR); - - NodeList* nodeList = NodeList::getInstance(); - - QByteArray rfcUUID = nodeList->getOwnerUUID().toRfc4122(); - memcpy(packetPosition, rfcUUID.constData(), rfcUUID.size()); - packetPosition += rfcUUID.size(); - - nodeList->broadcastToNodes(packet, packetPosition - packet, QSet() << NODE_TYPE_AVATAR_MIXER); + QByteArray killPacket = byteArrayWithPopluatedHeader(PacketTypeKillAvatar); + NodeList::getInstance()->broadcastToNodes(killPacket, NodeSet() << NodeType::AvatarMixer); } void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) { @@ -497,12 +651,42 @@ void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) { setOrientation(orientation); // then vertically - float oldMousePitch = _head.getMousePitch(); - _head.setMousePitch(oldMousePitch + deltaY * -ANGULAR_SCALE); - rotation = glm::angleAxis(_head.getMousePitch() - oldMousePitch, orientation * IDENTITY_RIGHT); + float oldPitch = _head.getPitch(); + _head.setPitch(oldPitch + deltaY * -ANGULAR_SCALE); + rotation = glm::angleAxis(_head.getPitch() - oldPitch, orientation * IDENTITY_RIGHT); + setPosition(position + rotation * (getPosition() - position)); } +void MyAvatar::updateLookAtTargetAvatar(glm::vec3 &eyePosition) { + Application* applicationInstance = Application::getInstance(); + + if (!applicationInstance->isMousePressed()) { + glm::vec3 mouseOrigin = applicationInstance->getMouseRayOrigin(); + glm::vec3 mouseDirection = applicationInstance->getMouseRayDirection(); + + foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) { + Avatar* avatar = static_cast(avatarPointer.data()); + if (avatar == static_cast(this)) { + continue; + } + float distance; + if (avatar->findRayIntersection(mouseOrigin, mouseDirection, distance)) { + // rescale to compensate for head embiggening + eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) * + (avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot(); + _lookAtTargetAvatar = avatarPointer; + return; + } + } + _lookAtTargetAvatar.clear(); + } +} + +void MyAvatar::clearLookAtTargetAvatar() { + _lookAtTargetAvatar.clear(); +} + float MyAvatar::getAbsoluteHeadYaw() const { return glm::yaw(_head.getOrientation()); } @@ -524,7 +708,7 @@ void MyAvatar::renderBody(bool forceRenderHead) { _hand.render(true); } -void MyAvatar::updateThrust(float deltaTime, Transmitter * transmitter) { +void MyAvatar::updateThrust(float deltaTime) { // // Gather thrust information from keyboard and sensors to apply to avatar motion // @@ -549,7 +733,7 @@ void MyAvatar::updateThrust(float deltaTime, Transmitter * transmitter) { _thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up; _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_MAG * deltaTime; _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_MAG * deltaTime; - _head.setMousePitch(_head.getMousePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_MAG * deltaTime); + _head.setPitch(_head.getPitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_MAG * deltaTime); // If thrust keys are being held down, slowly increase thrust to allow reaching great speeds if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) { @@ -572,9 +756,9 @@ void MyAvatar::updateThrust(float deltaTime, Transmitter * transmitter) { } // Add thrusts from Transmitter - if (transmitter) { - transmitter->checkForLostTransmitter(); - glm::vec3 rotation = transmitter->getEstimatedRotation(); + if (Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _transmitter.isConnected()) { + _transmitter.checkForLostTransmitter(); + glm::vec3 rotation = _transmitter.getEstimatedRotation(); const float TRANSMITTER_MIN_RATE = 1.f; const float TRANSMITTER_MIN_YAW_RATE = 4.f; const float TRANSMITTER_LATERAL_FORCE_SCALE = 5.f; @@ -592,9 +776,9 @@ void MyAvatar::updateThrust(float deltaTime, Transmitter * transmitter) { if (fabs(rotation.y) > TRANSMITTER_MIN_YAW_RATE) { _bodyYawDelta += rotation.y * TRANSMITTER_YAW_SCALE * deltaTime; } - if (transmitter->getTouchState()->state == 'D') { + if (_transmitter.getTouchState()->state == 'D') { _thrust += TRANSMITTER_UP_FORCE_SCALE * - (float)(transmitter->getTouchState()->y - TOUCH_POSITION_RANGE_HALF) / TOUCH_POSITION_RANGE_HALF * + (float)(_transmitter.getTouchState()->y - TOUCH_POSITION_RANGE_HALF) / TOUCH_POSITION_RANGE_HALF * TRANSMITTER_LIFT_SCALE * deltaTime * up; @@ -791,14 +975,15 @@ void MyAvatar::updateChatCircle(float deltaTime) { // find all circle-enabled members and sort by distance QVector sortedAvatars; - foreach (const AvatarSharedPointer& avatar, Application::getInstance()->getAvatarManager().getAvatarHash()) { - SortedAvatar sortedAvatar; - sortedAvatar.avatar = avatar.data(); - - if (!sortedAvatar.avatar->isChatCirclingEnabled()) { + foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) { + Avatar* avatar = static_cast(avatarPointer.data()); + if ( ! avatar->isChatCirclingEnabled() || + avatar == static_cast(this)) { continue; } - + + SortedAvatar sortedAvatar; + sortedAvatar.avatar = avatar; sortedAvatar.distance = glm::distance(_position, sortedAvatar.avatar->getPosition()); sortedAvatars.append(sortedAvatar); } @@ -898,3 +1083,28 @@ void MyAvatar::setOrientation(const glm::quat& orientation) { _bodyYaw = eulerAngles.y; _bodyRoll = eulerAngles.z; } + +void MyAvatar::goHome() { + qDebug("Going Home!"); + setPosition(START_LOCATION); +} + +void MyAvatar::increaseSize() { + if ((1.f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) { + _targetScale *= (1.f + SCALING_RATIO); + qDebug("Changed scale to %f", _targetScale); + } +} + +void MyAvatar::decreaseSize() { + if (MIN_AVATAR_SCALE < (1.f - SCALING_RATIO) * _targetScale) { + _targetScale *= (1.f - SCALING_RATIO); + qDebug("Changed scale to %f", _targetScale); + } +} + +void MyAvatar::resetSize() { + _targetScale = 1.0f; + qDebug("Reseted scale to %f", _targetScale); +} + diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 92afe530eb..c3ef1e4bfb 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -11,6 +11,8 @@ #include +#include + #include "Avatar.h" enum AvatarHandState @@ -23,14 +25,23 @@ enum AvatarHandState }; class MyAvatar : public Avatar { + Q_OBJECT + public: MyAvatar(); + ~MyAvatar(); void reset(); - void simulate(float deltaTime, Transmitter* transmitter); + void update(float deltaTime); + void simulate(float deltaTime); void updateFromGyros(bool turnWithHead); + void updateTransmitter(float deltaTime); + void render(bool forceRenderHead); void renderDebugBodyPoints(); + void renderHeadMouse() const; + void renderTransmitterPickRay() const; + void renderTransmitterLevels(int width, int height) const; // setters void setMousePressed(bool mousePressed) { _mousePressed = mousePressed; } @@ -39,7 +50,6 @@ public: void setLeanScale(float scale) { _leanScale = scale; } void setGravity(glm::vec3 gravity); void setOrientation(const glm::quat& orientation); - void setWantCollisionsOn(bool wantCollisionsOn) { _isCollisionsOn = wantCollisionsOn; } void setMoveTarget(const glm::vec3 moveTarget); // getters @@ -51,6 +61,7 @@ public: float getAbsoluteHeadYaw() const; const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } + Transmitter& getTransmitter() { return _transmitter; } glm::vec3 getGravity() const { return _gravity; } glm::vec3 getUprightHeadPosition() const; @@ -73,6 +84,17 @@ public: void orbit(const glm::vec3& position, int deltaX, int deltaY); + AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); } + void updateLookAtTargetAvatar(glm::vec3& eyePosition); + void clearLookAtTargetAvatar(); + +public slots: + void goHome(); + void setWantCollisionsOn(bool wantCollisionsOn) { _isCollisionsOn = wantCollisionsOn; } + void increaseSize(); + void decreaseSize(); + void resetSize(); + private: bool _mousePressed; float _bodyPitchDelta; @@ -86,15 +108,21 @@ private: float _elapsedTimeSinceCollision; glm::vec3 _lastCollisionPosition; bool _speedBrakes; + bool _isCollisionsOn; bool _isThrustOn; float _thrustMultiplier; float _collisionRadius; glm::vec3 _moveTarget; int _moveTargetStepCounter; + QWeakPointer _lookAtTargetAvatar; + + Transmitter _transmitter; // Gets UDP data from transmitter app used to animate the avatar + glm::vec3 _transmitterPickStart; + glm::vec3 _transmitterPickEnd; // private methods void renderBody(bool forceRenderHead); - void updateThrust(float deltaTime, Transmitter * transmitter); + void updateThrust(float deltaTime); void updateHandMovementAndTouching(float deltaTime); void updateAvatarCollisions(float deltaTime); void updateCollisionWithEnvironment(float deltaTime); diff --git a/interface/src/avatar/Profile.cpp b/interface/src/avatar/Profile.cpp index 83105f3f41..902a0ea12a 100644 --- a/interface/src/avatar/Profile.cpp +++ b/interface/src/avatar/Profile.cpp @@ -53,7 +53,10 @@ void Profile::setUUID(const QUuid& uuid) { // when the UUID is changed we need set it appropriately on the NodeList instance NodeList::getInstance()->setOwnerUUID(uuid); - } + + // ask for a window title update so the new UUID is presented + Application::getInstance()->updateWindowTitle(); + } } void Profile::setFaceModelURL(const QUrl& faceModelURL) { @@ -87,7 +90,7 @@ void Profile::updatePosition(const glm::vec3 position) { if (_lastPosition != position) { static timeval lastPositionSend = {}; - const uint64_t DATA_SERVER_POSITION_UPDATE_INTERVAL_USECS = 5 * 1000 * 1000; + const quint64 DATA_SERVER_POSITION_UPDATE_INTERVAL_USECS = 5 * 1000 * 1000; const float DATA_SERVER_POSITION_CHANGE_THRESHOLD_METERS = 1; if (usecTimestampNow() - usecTimestamp(&lastPositionSend) >= DATA_SERVER_POSITION_UPDATE_INTERVAL_USECS && @@ -115,10 +118,10 @@ void Profile::updateOrientation(const glm::quat& orientation) { if (_lastOrientation == eulerAngles) { return; } - const uint64_t DATA_SERVER_ORIENTATION_UPDATE_INTERVAL_USECS = 5 * 1000 * 1000; + const quint64 DATA_SERVER_ORIENTATION_UPDATE_INTERVAL_USECS = 5 * 1000 * 1000; const float DATA_SERVER_ORIENTATION_CHANGE_THRESHOLD_DEGREES = 5.0f; - uint64_t now = usecTimestampNow(); + quint64 now = usecTimestampNow(); if (now - _lastOrientationSend >= DATA_SERVER_ORIENTATION_UPDATE_INTERVAL_USECS && glm::distance(_lastOrientation, eulerAngles) >= DATA_SERVER_ORIENTATION_CHANGE_THRESHOLD_DEGREES) { DataServerClient::putValueForKeyAndUserString(DataServerKey::Orientation, QString(createByteArray(eulerAngles)), diff --git a/interface/src/avatar/Profile.h b/interface/src/avatar/Profile.h index 225022b99d..5cb2295f5b 100644 --- a/interface/src/avatar/Profile.h +++ b/interface/src/avatar/Profile.h @@ -57,7 +57,7 @@ private: QString _lastDomain; glm::vec3 _lastPosition; glm::vec3 _lastOrientation; - uint64_t _lastOrientationSend; + quint64 _lastOrientationSend; QUrl _faceModelURL; QUrl _skeletonModelURL; }; diff --git a/interface/src/devices/Faceshift.cpp b/interface/src/devices/Faceshift.cpp index 437a3268d2..60839c362d 100644 --- a/interface/src/devices/Faceshift.cpp +++ b/interface/src/devices/Faceshift.cpp @@ -56,7 +56,7 @@ Faceshift::Faceshift() : } bool Faceshift::isActive() const { - const uint64_t ACTIVE_TIMEOUT_USECS = 1000000; + const quint64 ACTIVE_TIMEOUT_USECS = 1000000; return (usecTimestampNow() - _lastTrackingStateReceived) < ACTIVE_TIMEOUT_USECS; } diff --git a/interface/src/devices/Faceshift.h b/interface/src/devices/Faceshift.h index 62eb494f42..d3981fc182 100644 --- a/interface/src/devices/Faceshift.h +++ b/interface/src/devices/Faceshift.h @@ -90,7 +90,7 @@ private: bool _tcpEnabled; int _tcpRetryCount; bool _tracking; - uint64_t _lastTrackingStateReceived; + quint64 _lastTrackingStateReceived; glm::quat _headRotation; glm::vec3 _headAngularVelocity; diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 863db7852f..1d8263ee30 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -25,7 +25,7 @@ public slots: private: - uint64_t _lastMovement; + quint64 _lastMovement; }; #endif /* defined(__interface__SixenseManager__) */ diff --git a/interface/src/devices/Transmitter.cpp b/interface/src/devices/Transmitter.cpp index 8ae6bdc405..5fca045cfd 100644 --- a/interface/src/devices/Transmitter.cpp +++ b/interface/src/devices/Transmitter.cpp @@ -60,7 +60,7 @@ void Transmitter::resetLevels() { void Transmitter::processIncomingData(unsigned char* packetData, int numBytes) { // Packet's first byte is 'T' - int numBytesPacketHeader = numBytesForPacketHeader(packetData); + int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast(packetData)); const int ROTATION_MARKER_SIZE = 1; // 'R' = Rotation (clockwise about x,y,z) const int ACCELERATION_MARKER_SIZE = 1; // 'A' = Acceleration (x,y,z) @@ -113,7 +113,7 @@ void Transmitter::processIncomingData(unsigned char* packetData, int numBytes) { } } -void Transmitter::renderLevels(int width, int height) { +void Transmitter::renderLevels(int width, int height) const { char val[50]; const int LEVEL_CORNER_X = 10; const int LEVEL_CORNER_Y = 400; @@ -163,7 +163,5 @@ void Transmitter::renderLevels(int width, int height) { glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y - 6); glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 30); glEnd(); - - } diff --git a/interface/src/devices/Transmitter.h b/interface/src/devices/Transmitter.h index b35d7200bd..1fa392a280 100644 --- a/interface/src/devices/Transmitter.h +++ b/interface/src/devices/Transmitter.h @@ -29,8 +29,8 @@ public: void render(); void checkForLostTransmitter(); void resetLevels(); - void renderLevels(int width, int height); - bool isConnected() { return _isConnected; }; + void renderLevels(int width, int height) const; + bool isConnected() const { return _isConnected; }; const glm::vec3 getLastRotationRate() const { return _lastRotationRate; }; const glm::vec3 getLastAcceleration() const { return _lastRotationRate; }; const glm::vec3 getEstimatedRotation() const { return _estimatedRotation; }; diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 8a7f839b70..e2c3bfafdd 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -170,6 +170,7 @@ public: const QByteArray& getDatum() const { return _datum; } void pushBackToken(int token) { _pushedBackToken = token; } + void ungetChar(char ch) { _device->ungetChar(ch); } private: @@ -221,7 +222,7 @@ int Tokenizer::nextToken() { _datum.append(ch); while (_device->getChar(&ch)) { if (QChar(ch).isSpace() || ch == ';' || ch == ':' || ch == '{' || ch == '}' || ch == ',' || ch == '\"') { - _device->ungetChar(ch); // read until we encounter a special character, then replace it + ungetChar(ch); // read until we encounter a special character, then replace it break; } _datum.append(ch); @@ -257,9 +258,17 @@ FBXNode parseTextFBXNode(Tokenizer& tokenizer) { expectingDatum = true; } else if (token == Tokenizer::DATUM_TOKEN && expectingDatum) { - node.properties.append(tokenizer.getDatum()); - expectingDatum = false; - + QByteArray datum = tokenizer.getDatum(); + if ((token = tokenizer.nextToken()) == ':') { + tokenizer.ungetChar(':'); + tokenizer.pushBackToken(Tokenizer::DATUM_TOKEN); + return node; + + } else { + tokenizer.pushBackToken(token); + node.properties.append(datum); + expectingDatum = false; + } } else { tokenizer.pushBackToken(token); return node; @@ -377,6 +386,9 @@ glm::mat4 createMat4(const QVector& doubleVector) { } QVector getIntVector(const QVariantList& properties, int index) { + if (index >= properties.size()) { + return QVector(); + } QVector vector = properties.at(index).value >(); if (!vector.isEmpty()) { return vector; @@ -388,6 +400,9 @@ QVector getIntVector(const QVariantList& properties, int index) { } QVector getDoubleVector(const QVariantList& properties, int index) { + if (index >= properties.size()) { + return QVector(); + } QVector vector = properties.at(index).value >(); if (!vector.isEmpty()) { return vector; @@ -723,6 +738,22 @@ ExtractedMesh extractMesh(const FBXNode& object) { return data.extracted; } +FBXBlendshape extractBlendshape(const FBXNode& object) { + FBXBlendshape blendshape; + foreach (const FBXNode& data, object.children) { + if (data.name == "Indexes") { + blendshape.indices = getIntVector(data.properties, 0); + + } else if (data.name == "Vertices") { + blendshape.vertices = createVec3Vector(getDoubleVector(data.properties, 0)); + + } else if (data.name == "Normals") { + blendshape.normals = createVec3Vector(getDoubleVector(data.properties, 0)); + } + } + return blendshape; +} + void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) { glm::vec3 normal = glm::normalize(mesh.normals.at(firstIndex)); glm::vec3 bitangent = glm::cross(normal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex)); @@ -745,6 +776,49 @@ QVector getIndices(const QVector ids, QVector modelIDs) { return indices; } +typedef QPair WeightedIndex; + +void addBlendshapes(const ExtractedBlendshape& extracted, const QList& indices, ExtractedMesh& extractedMesh) { + foreach (const WeightedIndex& index, indices) { + extractedMesh.mesh.blendshapes.resize(max(extractedMesh.mesh.blendshapes.size(), index.first + 1)); + extractedMesh.blendshapeIndexMaps.resize(extractedMesh.mesh.blendshapes.size()); + FBXBlendshape& blendshape = extractedMesh.mesh.blendshapes[index.first]; + QHash& blendshapeIndexMap = extractedMesh.blendshapeIndexMaps[index.first]; + for (int i = 0; i < extracted.blendshape.indices.size(); i++) { + int oldIndex = extracted.blendshape.indices.at(i); + for (QMultiHash::const_iterator it = extractedMesh.newIndices.constFind(oldIndex); + it != extractedMesh.newIndices.constEnd() && it.key() == oldIndex; it++) { + QHash::iterator blendshapeIndex = blendshapeIndexMap.find(it.value()); + if (blendshapeIndex == blendshapeIndexMap.end()) { + blendshapeIndexMap.insert(it.value(), blendshape.indices.size()); + blendshape.indices.append(it.value()); + blendshape.vertices.append(extracted.blendshape.vertices.at(i) * index.second); + blendshape.normals.append(extracted.blendshape.normals.at(i) * index.second); + } else { + blendshape.vertices[*blendshapeIndex] += extracted.blendshape.vertices.at(i) * index.second; + blendshape.normals[*blendshapeIndex] += extracted.blendshape.normals.at(i) * index.second; + } + } + } + } +} + +QString getTopModelID(const QMultiHash& parentMap, + const QHash& models, const QString& modelID) { + QString topID = modelID; + forever { + foreach (const QString& parentID, parentMap.values(topID)) { + if (models.contains(parentID)) { + topID = parentID; + goto outerContinue; + } + } + return topID; + + outerContinue: ; + } +} + FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) { QHash meshes; QVector blendshapes; @@ -784,7 +858,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QVector jointRightFingertipIDs(jointRightFingertipNames.size()); QVariantHash blendshapeMappings = mapping.value("bs").toHash(); - typedef QPair WeightedIndex; QMultiHash blendshapeIndices; for (int i = 0;; i++) { QByteArray blendshapeName = FACESHIFT_BLENDSHAPES[i]; @@ -812,22 +885,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) meshes.insert(getID(object.properties), extractMesh(object)); } else { // object.properties.at(2) == "Shape" - ExtractedBlendshape extracted = { getID(object.properties) }; - - foreach (const FBXNode& data, object.children) { - if (data.name == "Indexes") { - extracted.blendshape.indices = getIntVector(data.properties, 0); - - } else if (data.name == "Vertices") { - extracted.blendshape.vertices = createVec3Vector( - getDoubleVector(data.properties, 0)); - - } else if (data.name == "Normals") { - extracted.blendshape.normals = createVec3Vector( - getDoubleVector(data.properties, 0)); - } - } - + ExtractedBlendshape extracted = { getID(object.properties), extractBlendshape(object) }; blendshapes.append(extracted); } } else if (object.name == "Model") { @@ -885,6 +943,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) bool rotationMaxX = false, rotationMaxY = false, rotationMaxZ = false; glm::vec3 rotationMin, rotationMax; FBXModel model = { name, -1 }; + ExtractedMesh* mesh = NULL; + QVector blendshapes; foreach (const FBXNode& subobject, object.children) { bool properties = false; QByteArray propertyName; @@ -954,9 +1014,23 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } } else if (subobject.name == "Vertices") { // it's a mesh as well as a model - meshes.insert(getID(object.properties), extractMesh(object)); + mesh = &meshes[getID(object.properties)]; + *mesh = extractMesh(object); + + } else if (subobject.name == "Shape") { + ExtractedBlendshape blendshape = { subobject.properties.at(0).toString(), + extractBlendshape(subobject) }; + blendshapes.append(blendshape); } } + + // add the blendshapes included in the model, if any + if (mesh) { + foreach (const ExtractedBlendshape& extracted, blendshapes) { + addBlendshapes(extracted, blendshapeIndices.values(extracted.id.toLatin1()), *mesh); + } + } + // see FBX documentation, http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html model.translation = translation; model.preTransform = glm::translate(rotationOffset) * glm::translate(rotationPivot); @@ -1069,29 +1143,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QString blendshapeChannelID = parentMap.value(extracted.id); QString blendshapeID = parentMap.value(blendshapeChannelID); QString meshID = parentMap.value(blendshapeID); - ExtractedMesh& extractedMesh = meshes[meshID]; - foreach (const WeightedIndex& index, blendshapeChannelIndices.values(blendshapeChannelID)) { - extractedMesh.mesh.blendshapes.resize(max(extractedMesh.mesh.blendshapes.size(), index.first + 1)); - extractedMesh.blendshapeIndexMaps.resize(extractedMesh.mesh.blendshapes.size()); - FBXBlendshape& blendshape = extractedMesh.mesh.blendshapes[index.first]; - QHash& blendshapeIndexMap = extractedMesh.blendshapeIndexMaps[index.first]; - for (int i = 0; i < extracted.blendshape.indices.size(); i++) { - int oldIndex = extracted.blendshape.indices.at(i); - for (QMultiHash::const_iterator it = extractedMesh.newIndices.constFind(oldIndex); - it != extractedMesh.newIndices.constEnd() && it.key() == oldIndex; it++) { - QHash::iterator blendshapeIndex = blendshapeIndexMap.find(it.value()); - if (blendshapeIndex == blendshapeIndexMap.end()) { - blendshapeIndexMap.insert(it.value(), blendshape.indices.size()); - blendshape.indices.append(it.value()); - blendshape.vertices.append(extracted.blendshape.vertices.at(i) * index.second); - blendshape.normals.append(extracted.blendshape.normals.at(i) * index.second); - } else { - blendshape.vertices[*blendshapeIndex] += extracted.blendshape.vertices.at(i) * index.second; - blendshape.normals[*blendshapeIndex] += extracted.blendshape.normals.at(i) * index.second; - } - } - } - } + addBlendshapes(extracted, blendshapeChannelIndices.values(blendshapeChannelID), meshes[meshID]); } // get offset transform from mapping @@ -1106,23 +1158,30 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QVector modelIDs; QSet remainingModels; for (QHash::const_iterator model = models.constBegin(); model != models.constEnd(); model++) { + // models with clusters must be parented to the cluster top + foreach (const QString& deformerID, childMap.values(model.key())) { + foreach (const QString& clusterID, childMap.values(deformerID)) { + if (!clusters.contains(clusterID)) { + continue; + } + QString topID = getTopModelID(parentMap, models, childMap.value(clusterID)); + childMap.remove(parentMap.take(model.key()), model.key()); + parentMap.insert(model.key(), topID); + goto outerBreak; + } + } + outerBreak: + + // make sure the parent is in the child map + QString parent = parentMap.value(model.key()); + if (!childMap.contains(parent, model.key())) { + childMap.insert(parent, model.key()); + } remainingModels.insert(model.key()); } while (!remainingModels.isEmpty()) { - QString top = *remainingModels.constBegin(); - forever { - foreach (const QString& name, parentMap.values(top)) { - if (models.contains(name)) { - top = name; - goto outerContinue; - } - } - top = parentMap.value(top); - break; - - outerContinue: ; - } - appendModelIDs(top, childMap, models, remainingModels, modelIDs); + QString topID = getTopModelID(parentMap, models, *remainingModels.constBegin()); + appendModelIDs(parentMap.value(topID), childMap, models, remainingModels, modelIDs); } // convert the models to joints diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 73248b413f..63a0c51f0b 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -508,3 +508,17 @@ void NetworkGeometry::maybeReadModelWithMapping() { _meshes.append(networkMesh); } } + +bool NetworkMeshPart::isTranslucent() const { + return diffuseTexture && diffuseTexture->isTranslucent(); +} + +int NetworkMesh::getTranslucentPartCount() const { + int count = 0; + foreach (const NetworkMeshPart& part, parts) { + if (part.isTranslucent()) { + count++; + } + } + return count; +} diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index 312d8bcd91..e65aed31d4 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -93,6 +93,8 @@ public: QSharedPointer diffuseTexture; QSharedPointer normalTexture; + + bool isTranslucent() const; }; /// The state associated with a single mesh. @@ -103,6 +105,8 @@ public: GLuint vertexBufferID; QVector parts; + + int getTranslucentPartCount() const; }; #endif /* defined(__interface__GeometryCache__) */ diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index c6c820a0f9..6d61b2df68 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -240,7 +240,6 @@ bool Model::render(float alpha) { // set up blended buffer ids on first render after load/simulate const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const QVector& networkMeshes = _geometry->getMeshes(); if (_blendedVertexBufferIDs.isEmpty()) { foreach (const FBXMesh& mesh, geometry.meshes) { GLuint id = 0; @@ -264,191 +263,28 @@ bool Model::render(float alpha) { glDisable(GL_COLOR_MATERIAL); + // render opaque meshes with alpha testing + glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.5f); - for (int i = 0; i < networkMeshes.size(); i++) { - const NetworkMesh& networkMesh = networkMeshes.at(i); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, networkMesh.indexBufferID); - - const FBXMesh& mesh = geometry.meshes.at(i); - int vertexCount = mesh.vertices.size(); - - glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID); - - ProgramObject* program = &_program; - ProgramObject* skinProgram = &_skinProgram; - SkinLocations* skinLocations = &_skinLocations; - if (!mesh.tangents.isEmpty()) { - program = &_normalMapProgram; - skinProgram = &_skinNormalMapProgram; - skinLocations = &_skinNormalMapLocations; - } - - const MeshState& state = _meshStates.at(i); - ProgramObject* activeProgram = program; - int tangentLocation = _normalMapTangentLocation; - if (state.worldSpaceVertices.isEmpty()) { - glPushMatrix(); - Application::getInstance()->loadTranslatedViewMatrix(_translation); - - if (state.clusterMatrices.size() > 1) { - skinProgram->bind(); - glUniformMatrix4fvARB(skinLocations->clusterMatrices, state.clusterMatrices.size(), false, - (const float*)state.clusterMatrices.constData()); - int offset = (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3) + - mesh.texCoords.size() * sizeof(glm::vec2) + - (mesh.blendshapes.isEmpty() ? vertexCount * 2 * sizeof(glm::vec3) : 0); - skinProgram->setAttributeBuffer(skinLocations->clusterIndices, GL_FLOAT, offset, 4); - skinProgram->setAttributeBuffer(skinLocations->clusterWeights, GL_FLOAT, - offset + vertexCount * sizeof(glm::vec4), 4); - skinProgram->enableAttributeArray(skinLocations->clusterIndices); - skinProgram->enableAttributeArray(skinLocations->clusterWeights); - activeProgram = skinProgram; - tangentLocation = skinLocations->tangent; - - } else { - glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]); - program->bind(); - } - } else { - program->bind(); - } - - if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { - if (!mesh.tangents.isEmpty()) { - activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3); - activeProgram->enableAttributeArray(tangentLocation); - } - glColorPointer(3, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3) + - mesh.tangents.size() * sizeof(glm::vec3))); - glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3) + - (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); - - } else { - if (!mesh.tangents.isEmpty()) { - activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, 0, 3); - activeProgram->enableAttributeArray(tangentLocation); - } - glColorPointer(3, GL_FLOAT, 0, (void*)(mesh.tangents.size() * sizeof(glm::vec3))); - glTexCoordPointer(2, GL_FLOAT, 0, (void*)((mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); - glBindBuffer(GL_ARRAY_BUFFER, _blendedVertexBufferIDs.at(i)); - - if (!state.worldSpaceVertices.isEmpty()) { - glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), state.worldSpaceVertices.constData()); - glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3), - vertexCount * sizeof(glm::vec3), state.worldSpaceNormals.constData()); - - } else { - _blendedVertices.resize(max(_blendedVertices.size(), vertexCount)); - _blendedNormals.resize(_blendedVertices.size()); - memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3)); - memcpy(_blendedNormals.data(), mesh.normals.constData(), vertexCount * sizeof(glm::vec3)); - - // blend in each coefficient - for (unsigned int j = 0; j < _blendshapeCoefficients.size(); j++) { - float coefficient = _blendshapeCoefficients[j]; - if (coefficient == 0.0f || j >= (unsigned int)mesh.blendshapes.size() || mesh.blendshapes[j].vertices.isEmpty()) { - continue; - } - const float NORMAL_COEFFICIENT_SCALE = 0.01f; - float normalCoefficient = coefficient * NORMAL_COEFFICIENT_SCALE; - const glm::vec3* vertex = mesh.blendshapes[j].vertices.constData(); - const glm::vec3* normal = mesh.blendshapes[j].normals.constData(); - for (const int* index = mesh.blendshapes[j].indices.constData(), - *end = index + mesh.blendshapes[j].indices.size(); index != end; index++, vertex++, normal++) { - _blendedVertices[*index] += *vertex * coefficient; - _blendedNormals[*index] += *normal * normalCoefficient; - } - } - - glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), _blendedVertices.constData()); - glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3), - vertexCount * sizeof(glm::vec3), _blendedNormals.constData()); - } - } - glVertexPointer(3, GL_FLOAT, 0, 0); - glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3))); - - if (!mesh.colors.isEmpty()) { - glEnableClientState(GL_COLOR_ARRAY); - } else { - glColor3f(1.0f, 1.0f, 1.0f); - } - if (!mesh.texCoords.isEmpty()) { - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - } - - qint64 offset = 0; - for (int j = 0; j < networkMesh.parts.size(); j++) { - const NetworkMeshPart& networkPart = networkMesh.parts.at(j); - const FBXMeshPart& part = mesh.parts.at(j); - - // apply material properties - glm::vec4 diffuse = glm::vec4(part.diffuseColor, alpha); - glm::vec4 specular = glm::vec4(part.specularColor, alpha); - glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse); - glMaterialfv(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse); - glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); - glMaterialf(GL_FRONT, GL_SHININESS, part.shininess); - - Texture* diffuseMap = networkPart.diffuseTexture.data(); - if (mesh.isEye) { - if (diffuseMap != NULL) { - diffuseMap = (_dilatedTextures[i][j] = - static_cast(diffuseMap)->getDilatedTexture(_pupilDilation)).data(); - } - } - glBindTexture(GL_TEXTURE_2D, diffuseMap == NULL ? - Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID()); - - if (!mesh.tangents.isEmpty()) { - glActiveTexture(GL_TEXTURE1); - Texture* normalMap = networkPart.normalTexture.data(); - glBindTexture(GL_TEXTURE_2D, normalMap == NULL ? - Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID()); - glActiveTexture(GL_TEXTURE0); - } - - glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset); - offset += part.quadIndices.size() * sizeof(int); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, part.triangleIndices.size(), - GL_UNSIGNED_INT, (void*)offset); - offset += part.triangleIndices.size() * sizeof(int); - } - - if (!mesh.colors.isEmpty()) { - glDisableClientState(GL_COLOR_ARRAY); - } - if (!mesh.texCoords.isEmpty()) { - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - } - - if (!mesh.tangents.isEmpty()) { - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - - activeProgram->disableAttributeArray(tangentLocation); - } - - if (state.worldSpaceVertices.isEmpty()) { - if (state.clusterMatrices.size() > 1) { - skinProgram->disableAttributeArray(skinLocations->clusterIndices); - skinProgram->disableAttributeArray(skinLocations->clusterWeights); - } - glPopMatrix(); - } - activeProgram->release(); - } + renderMeshes(alpha, false); + + glDisable(GL_ALPHA_TEST); + + // render translucent meshes afterwards, with back face culling + + glEnable(GL_CULL_FACE); + + renderMeshes(alpha, true); + + glDisable(GL_CULL_FACE); // deactivate vertex arrays after drawing glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisable(GL_ALPHA_TEST); - // bind with 0 to switch back to normal operation glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); @@ -882,3 +718,191 @@ void Model::deleteGeometry() { _jointStates.clear(); _meshStates.clear(); } + +void Model::renderMeshes(float alpha, bool translucent) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const QVector& networkMeshes = _geometry->getMeshes(); + + for (int i = 0; i < networkMeshes.size(); i++) { + // exit early if the translucency doesn't match what we're drawing + const NetworkMesh& networkMesh = networkMeshes.at(i); + if (translucent ? (networkMesh.getTranslucentPartCount() == 0) : + (networkMesh.getTranslucentPartCount() == networkMesh.parts.size())) { + continue; + } + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, networkMesh.indexBufferID); + + const FBXMesh& mesh = geometry.meshes.at(i); + int vertexCount = mesh.vertices.size(); + + glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID); + + ProgramObject* program = &_program; + ProgramObject* skinProgram = &_skinProgram; + SkinLocations* skinLocations = &_skinLocations; + if (!mesh.tangents.isEmpty()) { + program = &_normalMapProgram; + skinProgram = &_skinNormalMapProgram; + skinLocations = &_skinNormalMapLocations; + } + + const MeshState& state = _meshStates.at(i); + ProgramObject* activeProgram = program; + int tangentLocation = _normalMapTangentLocation; + if (state.worldSpaceVertices.isEmpty()) { + glPushMatrix(); + Application::getInstance()->loadTranslatedViewMatrix(_translation); + + if (state.clusterMatrices.size() > 1) { + skinProgram->bind(); + glUniformMatrix4fvARB(skinLocations->clusterMatrices, state.clusterMatrices.size(), false, + (const float*)state.clusterMatrices.constData()); + int offset = (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3) + + mesh.texCoords.size() * sizeof(glm::vec2) + + (mesh.blendshapes.isEmpty() ? vertexCount * 2 * sizeof(glm::vec3) : 0); + skinProgram->setAttributeBuffer(skinLocations->clusterIndices, GL_FLOAT, offset, 4); + skinProgram->setAttributeBuffer(skinLocations->clusterWeights, GL_FLOAT, + offset + vertexCount * sizeof(glm::vec4), 4); + skinProgram->enableAttributeArray(skinLocations->clusterIndices); + skinProgram->enableAttributeArray(skinLocations->clusterWeights); + activeProgram = skinProgram; + tangentLocation = skinLocations->tangent; + + } else { + glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]); + program->bind(); + } + } else { + program->bind(); + } + + if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { + if (!mesh.tangents.isEmpty()) { + activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3); + activeProgram->enableAttributeArray(tangentLocation); + } + glColorPointer(3, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3) + + mesh.tangents.size() * sizeof(glm::vec3))); + glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3) + + (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); + + } else { + if (!mesh.tangents.isEmpty()) { + activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, 0, 3); + activeProgram->enableAttributeArray(tangentLocation); + } + glColorPointer(3, GL_FLOAT, 0, (void*)(mesh.tangents.size() * sizeof(glm::vec3))); + glTexCoordPointer(2, GL_FLOAT, 0, (void*)((mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); + glBindBuffer(GL_ARRAY_BUFFER, _blendedVertexBufferIDs.at(i)); + + if (!state.worldSpaceVertices.isEmpty()) { + glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), state.worldSpaceVertices.constData()); + glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3), + vertexCount * sizeof(glm::vec3), state.worldSpaceNormals.constData()); + + } else { + _blendedVertices.resize(max(_blendedVertices.size(), vertexCount)); + _blendedNormals.resize(_blendedVertices.size()); + memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3)); + memcpy(_blendedNormals.data(), mesh.normals.constData(), vertexCount * sizeof(glm::vec3)); + + // blend in each coefficient + for (unsigned int j = 0; j < _blendshapeCoefficients.size(); j++) { + float coefficient = _blendshapeCoefficients[j]; + if (coefficient == 0.0f || j >= (unsigned int)mesh.blendshapes.size() || mesh.blendshapes[j].vertices.isEmpty()) { + continue; + } + const float NORMAL_COEFFICIENT_SCALE = 0.01f; + float normalCoefficient = coefficient * NORMAL_COEFFICIENT_SCALE; + const glm::vec3* vertex = mesh.blendshapes[j].vertices.constData(); + const glm::vec3* normal = mesh.blendshapes[j].normals.constData(); + for (const int* index = mesh.blendshapes[j].indices.constData(), + *end = index + mesh.blendshapes[j].indices.size(); index != end; index++, vertex++, normal++) { + _blendedVertices[*index] += *vertex * coefficient; + _blendedNormals[*index] += *normal * normalCoefficient; + } + } + + glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), _blendedVertices.constData()); + glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3), + vertexCount * sizeof(glm::vec3), _blendedNormals.constData()); + } + } + glVertexPointer(3, GL_FLOAT, 0, 0); + glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3))); + + if (!mesh.colors.isEmpty()) { + glEnableClientState(GL_COLOR_ARRAY); + } else { + glColor3f(1.0f, 1.0f, 1.0f); + } + if (!mesh.texCoords.isEmpty()) { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } + + qint64 offset = 0; + for (int j = 0; j < networkMesh.parts.size(); j++) { + const NetworkMeshPart& networkPart = networkMesh.parts.at(j); + if (networkPart.isTranslucent() != translucent) { + continue; + } + const FBXMeshPart& part = mesh.parts.at(j); + + // apply material properties + glm::vec4 diffuse = glm::vec4(part.diffuseColor, alpha); + glm::vec4 specular = glm::vec4(part.specularColor, alpha); + glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse); + glMaterialfv(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse); + glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); + glMaterialf(GL_FRONT, GL_SHININESS, part.shininess); + + Texture* diffuseMap = networkPart.diffuseTexture.data(); + if (mesh.isEye) { + if (diffuseMap != NULL) { + diffuseMap = (_dilatedTextures[i][j] = + static_cast(diffuseMap)->getDilatedTexture(_pupilDilation)).data(); + } + } + glBindTexture(GL_TEXTURE_2D, diffuseMap == NULL ? + Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID()); + + if (!mesh.tangents.isEmpty()) { + glActiveTexture(GL_TEXTURE1); + Texture* normalMap = networkPart.normalTexture.data(); + glBindTexture(GL_TEXTURE_2D, normalMap == NULL ? + Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID()); + glActiveTexture(GL_TEXTURE0); + } + + glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset); + offset += part.quadIndices.size() * sizeof(int); + glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, part.triangleIndices.size(), + GL_UNSIGNED_INT, (void*)offset); + offset += part.triangleIndices.size() * sizeof(int); + } + + if (!mesh.colors.isEmpty()) { + glDisableClientState(GL_COLOR_ARRAY); + } + if (!mesh.texCoords.isEmpty()) { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + + if (!mesh.tangents.isEmpty()) { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + + activeProgram->disableAttributeArray(tangentLocation); + } + + if (state.worldSpaceVertices.isEmpty()) { + if (state.clusterMatrices.size() > 1) { + skinProgram->disableAttributeArray(skinLocations->clusterIndices); + skinProgram->disableAttributeArray(skinLocations->clusterWeights); + } + glPopMatrix(); + } + activeProgram->release(); + } +} diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 3e8cd2be9c..389020d1b1 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -214,6 +214,7 @@ protected: private: void deleteGeometry(); + void renderMeshes(float alpha, bool translucent); float _pupilDilation; std::vector _blendshapeCoefficients; diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index 96fe84b305..dc6883a5d0 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -257,7 +257,8 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) : _request(url), _reply(NULL), _attempts(0), - _averageColor(1.0f, 1.0f, 1.0f, 1.0f) { + _averageColor(1.0f, 1.0f, 1.0f, 1.0f), + _translucent(false) { if (!url.isValid()) { return; @@ -300,19 +301,27 @@ void NetworkTexture::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTo QImage image = QImage::fromData(entirety).convertToFormat(QImage::Format_ARGB32); - // sum up the colors for the average + // sum up the colors for the average and check for translucency glm::vec4 accumulated; + int translucentPixels = 0; + const int EIGHT_BIT_MAXIMUM = 255; for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x++) { QRgb pixel = image.pixel(x, y); accumulated.r += qRed(pixel); accumulated.g += qGreen(pixel); accumulated.b += qBlue(pixel); - accumulated.a += qAlpha(pixel); + + int alpha = qAlpha(pixel); + if (alpha != 0 && alpha != EIGHT_BIT_MAXIMUM) { + translucentPixels++; + } + accumulated.a += alpha; } } - const float EIGHT_BIT_MAXIMUM = 255.0f; - _averageColor = accumulated / (image.width() * image.height() * EIGHT_BIT_MAXIMUM); + int imageArea = image.width() * image.height(); + _averageColor = accumulated / (imageArea * EIGHT_BIT_MAXIMUM); + _translucent = (translucentPixels >= imageArea / 2); imageLoaded(image); glBindTexture(GL_TEXTURE_2D, getID()); diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index d3f138254f..e560acf6f7 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -121,6 +121,10 @@ public: /// Returns the average color over the entire texture. const glm::vec4& getAverageColor() const { return _averageColor; } + /// Checks whether it "looks like" this texture is translucent + /// (majority of pixels neither fully opaque or fully transparent). + bool isTranslucent() const { return _translucent; } + protected: virtual void imageLoaded(const QImage& image); @@ -137,6 +141,7 @@ private: QNetworkReply* _reply; int _attempts; glm::vec4 _averageColor; + bool _translucent; }; /// Caches derived, dilated textures. diff --git a/interface/src/starfield/Config.h b/interface/src/starfield/Config.h index 564f6f3a1a..04a5331cf0 100755 --- a/interface/src/starfield/Config.h +++ b/interface/src/starfield/Config.h @@ -59,7 +59,7 @@ namespace starfield { using namespace std; typedef uint32_t nuint; - typedef uint64_t wuint; + typedef quint64 wuint; } diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index df932a9933..d35b44ffef 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -58,18 +58,21 @@ MetavoxelEditor::MetavoxelEditor() : _gridPlane->addItem("X/Z"); _gridPlane->addItem("Y/Z"); _gridPlane->setCurrentIndex(GRID_PLANE_XZ); + connect(_gridPlane, SIGNAL(currentIndexChanged(int)), SLOT(centerGridPosition())); formLayout->addRow("Grid Spacing:", _gridSpacing = new QDoubleSpinBox()); - _gridSpacing->setValue(0.1); + _gridSpacing->setMinimum(-FLT_MAX); _gridSpacing->setMaximum(FLT_MAX); - _gridSpacing->setSingleStep(0.01); - connect(_gridSpacing, SIGNAL(valueChanged(double)), SLOT(updateGridPosition())); + _gridSpacing->setPrefix("2^"); + _gridSpacing->setValue(-3.0); + connect(_gridSpacing, SIGNAL(valueChanged(double)), SLOT(alignGridPosition())); formLayout->addRow("Grid Position:", _gridPosition = new QDoubleSpinBox()); - _gridPosition->setSingleStep(0.1); _gridPosition->setMinimum(-FLT_MAX); _gridPosition->setMaximum(FLT_MAX); - + alignGridPosition(); + centerGridPosition(); + _value = new QGroupBox(); _value->setTitle("Value"); topLayout->addWidget(_value); @@ -119,7 +122,7 @@ bool MetavoxelEditor::eventFilter(QObject* watched, QEvent* event) { float top = base + _height; glm::quat rotation = getGridRotation(); glm::vec3 start = rotation * glm::vec3(glm::min(_startPosition, _endPosition), glm::min(base, top)); - float spacing = _gridSpacing->value(); + float spacing = getGridSpacing(); glm::vec3 end = rotation * glm::vec3(glm::max(_startPosition, _endPosition) + glm::vec2(spacing, spacing), glm::max(base, top)); @@ -181,13 +184,19 @@ void MetavoxelEditor::createNewAttribute() { updateAttributes(nameText); } -void MetavoxelEditor::updateGridPosition() { +void MetavoxelEditor::centerGridPosition() { + const float CENTER_OFFSET = 0.625f; + float eyePosition = (glm::inverse(getGridRotation()) * Application::getInstance()->getCamera()->getPosition()).z - + Application::getInstance()->getAvatar()->getScale() * CENTER_OFFSET; + double step = getGridSpacing(); + _gridPosition->setValue(step * floor(eyePosition / step)); +} + +void MetavoxelEditor::alignGridPosition() { // make sure our grid position matches our grid spacing - double step = _gridSpacing->value(); - if (step > 0.0) { - _gridPosition->setSingleStep(step); - _gridPosition->setValue(step * floor(_gridPosition->value() / step)); - } + double step = getGridSpacing(); + _gridPosition->setSingleStep(step); + _gridPosition->setValue(step * floor(_gridPosition->value() / step)); } void MetavoxelEditor::render() { @@ -209,7 +218,7 @@ void MetavoxelEditor::render() { glm::quat inverseRotation = glm::inverse(rotation); glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin(); glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection(); - float spacing = _gridSpacing->value(); + float spacing = getGridSpacing(); float position = _gridPosition->value(); if (_state == RAISING_STATE) { // find the plane at the mouse position, orthogonal to the plane, facing the eye position @@ -318,6 +327,10 @@ QString MetavoxelEditor::getSelectedAttribute() const { return selectedItems.isEmpty() ? QString() : selectedItems.first()->text(); } +double MetavoxelEditor::getGridSpacing() const { + return pow(2.0, _gridSpacing->value()); +} + glm::quat MetavoxelEditor::getGridRotation() const { // for simplicity, we handle the other two planes by rotating them onto X/Y and performing computation there switch (_gridPlane->currentIndex()) { @@ -339,60 +352,14 @@ void MetavoxelEditor::resetState() { _height = 0.0f; } -class Applier : public MetavoxelVisitor { -public: - - Applier(const glm::vec3& minimum, const glm::vec3& maximum, float granularity, const AttributeValue& value); - - virtual bool visit(MetavoxelInfo& info); - -protected: - - glm::vec3 _minimum; - glm::vec3 _maximum; - float _granularity; - AttributeValue _value; -}; - -Applier::Applier(const glm::vec3& minimum, const glm::vec3& maximum, float granularity, const AttributeValue& value) : - MetavoxelVisitor(QVector(), QVector() << value.getAttribute()), - _minimum(minimum), - _maximum(maximum), - _granularity(granularity), - _value(value) { -} - -bool Applier::visit(MetavoxelInfo& info) { - // find the intersection between volume and voxel - glm::vec3 minimum = glm::max(info.minimum, _minimum); - glm::vec3 maximum = glm::min(info.minimum + glm::vec3(info.size, info.size, info.size), _maximum); - glm::vec3 size = maximum - minimum; - if (size.x <= 0.0f || size.y <= 0.0f || size.z <= 0.0f) { - return false; // disjoint - } - float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size); - if (volume >= 1.0f) { - info.outputValues[0] = _value; - return false; // entirely contained - } - if (info.size <= _granularity) { - if (volume > 0.5f) { - info.outputValues[0] = _value; - } - return false; // reached granularity limit; take best guess - } - return true; // subdivide -} - void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) { AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(getSelectedAttribute()); if (!attribute) { return; } OwnedAttributeValue value(attribute, attribute->createFromVariant(getValue())); - - Applier applier(minimum, maximum, _gridSpacing->value(), value); - Application::getInstance()->getMetavoxels()->getData().guide(applier); + MetavoxelEditMessage edit = { { minimum, maximum }, getGridSpacing(), value }; + Application::getInstance()->getMetavoxels()->applyEdit(edit); } QVariant MetavoxelEditor::getValue() const { diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 21c8478d95..6fe5b26398 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -32,7 +32,8 @@ private slots: void updateValueEditor(); void createNewAttribute(); - void updateGridPosition(); + void centerGridPosition(); + void alignGridPosition(); void render(); @@ -40,6 +41,7 @@ private: void updateAttributes(const QString& select = QString()); QString getSelectedAttribute() const; + double getGridSpacing() const; glm::quat getGridRotation() const; void resetState(); void applyValue(const glm::vec3& minimum, const glm::vec3& maximum); diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp new file mode 100644 index 0000000000..f0fef33cee --- /dev/null +++ b/interface/src/ui/Snapshot.cpp @@ -0,0 +1,46 @@ +// +// Snapshot.cpp +// hifi +// +// Created by Stojce Slavkovski on 1/26/14. +// +// + +#include "Snapshot.h" + +#include + +#include +#include +#include + +// filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg +// %1 <= username, %2 <= date and time, %3 <= current location +const QString FILENAME_PATH_FORMAT = "hifi-snap-by-%1-on-%2@%3.jpg"; + +const QString DATETIME_FORMAT = "yyyy-MM-dd_hh-mm-ss"; +const QString SNAPSHOTS_DIRECTORY = "Snapshots"; + +void Snapshot::saveSnapshot(QGLWidget* widget, QString username, glm::vec3 location) { + QImage shot = widget->grabFrameBuffer(); + + // add metadata + shot.setText("location-x", QString::number(location.x)); + shot.setText("location-y", QString::number(location.y)); + shot.setText("location-z", QString::number(location.z)); + + QString formattedLocation = QString("%1_%2_%3").arg(location.x).arg(location.y).arg(location.z); + // replace decimal . with '-' + formattedLocation.replace('.', '-'); + + // normalize username, replace all non alphanumeric with '-' + username.replace(QRegExp("[^A-Za-z0-9_]"), "-"); + + QDateTime now = QDateTime::currentDateTime(); + + QString fileName = FileUtils::standardPath(SNAPSHOTS_DIRECTORY); + fileName.append(QString(FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT), formattedLocation))); + shot.save(fileName, 0, 100); +} + + diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h new file mode 100644 index 0000000000..26315678f9 --- /dev/null +++ b/interface/src/ui/Snapshot.h @@ -0,0 +1,27 @@ +// +// Snapshot.h +// hifi +// +// Created by Stojce Slavkovski on 1/26/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__Snapshot__ +#define __hifi__Snapshot__ + +#include +#include +#include + +#include + +class Snapshot { + +public: + static void saveSnapshot(QGLWidget* widget, QString username, glm::vec3 location); + +private: + QString _username; +}; + +#endif /* defined(__hifi__Snapshot__) */ diff --git a/interface/src/ui/VoxelStatsDialog.cpp b/interface/src/ui/VoxelStatsDialog.cpp index 2c2702a21d..27b7f788ec 100644 --- a/interface/src/ui/VoxelStatsDialog.cpp +++ b/interface/src/ui/VoxelStatsDialog.cpp @@ -224,9 +224,9 @@ void VoxelStatsDialog::paintEvent(QPaintEvent* event) { void VoxelStatsDialog::showAllOctreeServers() { int serverCount = 0; - showOctreeServersOfType(serverCount, NODE_TYPE_VOXEL_SERVER, "Voxel", + showOctreeServersOfType(serverCount, NodeType::VoxelServer, "Voxel", Application::getInstance()->getVoxelServerJurisdictions()); - showOctreeServersOfType(serverCount, NODE_TYPE_PARTICLE_SERVER, "Particle", + showOctreeServersOfType(serverCount, NodeType::ParticleServer, "Particle", Application::getInstance()->getParticleServerJurisdictions()); if (_voxelServerLabelsCount > serverCount) { @@ -239,7 +239,7 @@ void VoxelStatsDialog::showAllOctreeServers() { } } -void VoxelStatsDialog::showOctreeServersOfType(int& serverCount, NODE_TYPE serverType, const char* serverTypeName, +void VoxelStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t serverType, const char* serverTypeName, NodeToJurisdictionMap& serverJurisdictions) { QLocale locale(QLocale::English); @@ -247,7 +247,7 @@ void VoxelStatsDialog::showOctreeServersOfType(int& serverCount, NODE_TYPE serve NodeList* nodeList = NodeList::getInstance(); foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { - // only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER + // only send to the NodeTypes that are NodeType_t_VOXEL_SERVER if (node->getType() == serverType) { serverCount++; diff --git a/interface/src/ui/VoxelStatsDialog.h b/interface/src/ui/VoxelStatsDialog.h index c1dcbdd734..f3a54241b5 100644 --- a/interface/src/ui/VoxelStatsDialog.h +++ b/interface/src/ui/VoxelStatsDialog.h @@ -44,7 +44,7 @@ protected: void RemoveStatItem(int item); void showAllOctreeServers(); - void showOctreeServersOfType(int& serverNumber, NODE_TYPE serverType, + void showOctreeServersOfType(int& serverNumber, NodeType_t serverType, const char* serverTypeName, NodeToJurisdictionMap& serverJurisdictions); private: diff --git a/interface/src/world.h b/interface/src/world.h index a226dc228e..8d3bd7322e 100644 --- a/interface/src/world.h +++ b/interface/src/world.h @@ -9,8 +9,9 @@ #ifndef __interface__world__ #define __interface__world__ - +#ifndef PIf #define PIf 3.14159265f +#endif const float GRAVITY_EARTH = 9.80665f; const float EDGE_SIZE_GROUND_PLANE = 20.f; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 60ff777452..325cd06639 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -6,6 +6,8 @@ // Copyright (c) 2014 HighFidelity, Inc. All rights reserved. // +#include + #include #include #include @@ -45,45 +47,28 @@ void AudioInjector::injectAudio() { NodeList* nodeList = NodeList::getInstance(); // setup the packet for injected audio - unsigned char injectedAudioPacket[MAX_PACKET_SIZE]; - unsigned char* currentPacketPosition = injectedAudioPacket; + QByteArray injectAudioPacket = byteArrayWithPopluatedHeader(PacketTypeInjectAudio); + QDataStream packetStream(&injectAudioPacket, QIODevice::Append); - int numBytesPacketHeader = populateTypeAndVersion(injectedAudioPacket, PACKET_TYPE_INJECT_AUDIO); - currentPacketPosition += numBytesPacketHeader; - - // pack the session UUID for this Node - QByteArray rfcSessionUUID = NodeList::getInstance()->getOwnerUUID().toRfc4122(); - memcpy(currentPacketPosition, rfcSessionUUID.constData(), rfcSessionUUID.size()); - currentPacketPosition += rfcSessionUUID.size(); - - // pick a random UUID to use for this stream - QUuid randomStreamUUID = QUuid::createUuid(); - QByteArray rfcStreamUUID = randomStreamUUID.toRfc4122(); - memcpy(currentPacketPosition, rfcStreamUUID, rfcStreamUUID.size()); - currentPacketPosition += rfcStreamUUID.size(); + packetStream << QUuid::createUuid(); // pack the flag for loopback - bool loopbackFlag = (_options.getLoopbackAudioInterface() == NULL); - memcpy(currentPacketPosition, &loopbackFlag, sizeof(loopbackFlag)); - currentPacketPosition += sizeof(loopbackFlag); + uchar loopbackFlag = (uchar) (_options.getLoopbackAudioInterface() == NULL); + packetStream << loopbackFlag; // pack the position for injected audio - memcpy(currentPacketPosition, &_options.getPosition(), sizeof(_options.getPosition())); - currentPacketPosition += sizeof(_options.getPosition()); + packetStream.writeRawData(reinterpret_cast(&_options.getPosition()), sizeof(_options.getPosition())); // pack our orientation for injected audio - memcpy(currentPacketPosition, &_options.getOrientation(), sizeof(_options.getOrientation())); - currentPacketPosition += sizeof(_options.getOrientation()); + packetStream.writeRawData(reinterpret_cast(&_options.getOrientation()), sizeof(_options.getOrientation())); // pack zero for radius float radius = 0; - memcpy(currentPacketPosition, &radius, sizeof(radius)); - currentPacketPosition += sizeof(radius); + packetStream << radius; // pack 255 for attenuation byte - uchar volume = MAX_INJECTOR_VOLUME * _options.getVolume(); - memcpy(currentPacketPosition, &volume, sizeof(volume)); - currentPacketPosition += sizeof(volume); + quint8 volume = MAX_INJECTOR_VOLUME * _options.getVolume(); + packetStream << volume; timeval startTime = {}; gettimeofday(&startTime, NULL); @@ -91,24 +76,26 @@ void AudioInjector::injectAudio() { int currentSendPosition = 0; + int numPreAudioDataBytes = injectAudioPacket.size(); + // loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks while (currentSendPosition < soundByteArray.size()) { int bytesToCopy = std::min(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, soundByteArray.size() - currentSendPosition); - // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet - memcpy(currentPacketPosition, soundByteArray.data() + currentSendPosition, - bytesToCopy); + // resize the QByteArray to the right size + injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy); + // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet + memcpy(injectAudioPacket.data() + numPreAudioDataBytes, soundByteArray.data() + currentSendPosition, bytesToCopy); // grab our audio mixer from the NodeList, if it exists - SharedNodePointer audioMixer = nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); + SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); if (audioMixer && nodeList->getNodeActiveSocketOrPing(audioMixer.data())) { // send off this audio packet - nodeList->getNodeSocket().writeDatagram((char*) injectedAudioPacket, - (currentPacketPosition - injectedAudioPacket) + bytesToCopy, + nodeList->getNodeSocket().writeDatagram(injectAudioPacket, audioMixer->getActiveSocket()->getAddress(), audioMixer->getActiveSocket()->getPort()); } @@ -130,4 +117,4 @@ void AudioInjector::injectAudio() { } emit finished(); -} \ No newline at end of file +} diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index e65d4632f6..8a444e50fd 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -50,9 +50,9 @@ void AudioRingBuffer::resizeForFrameSize(qint64 numFrameSamples) { _endOfLastWrite = _buffer; } -int AudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) { - int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer); - return writeData((char*) sourceBuffer + numBytesPacketHeader, numBytes - numBytesPacketHeader); +int AudioRingBuffer::parseData(const QByteArray& packet) { + int numBytesPacketHeader = numBytesForPacketHeader(packet); + return writeData(packet.data() + numBytesPacketHeader, packet.size() - numBytesPacketHeader); } qint64 AudioRingBuffer::readSamples(int16_t* destination, qint64 maxSamples) { diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 0bcd127a2e..b6e75a2e86 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -44,7 +44,7 @@ public: int getSampleCapacity() const { return _sampleCapacity; } - int parseData(unsigned char* sourceBuffer, int numBytes); + int parseData(const QByteArray& packet); qint64 readSamples(int16_t* destination, qint64 maxSamples); qint64 writeSamples(const int16_t* source, qint64 maxSamples); diff --git a/libraries/audio/src/InjectedAudioRingBuffer.cpp b/libraries/audio/src/InjectedAudioRingBuffer.cpp index ae8574f3c9..f8205a846f 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.cpp +++ b/libraries/audio/src/InjectedAudioRingBuffer.cpp @@ -8,6 +8,7 @@ #include +#include #include #include @@ -26,29 +27,31 @@ InjectedAudioRingBuffer::InjectedAudioRingBuffer(const QUuid& streamIdentifier) const uchar MAX_INJECTOR_VOLUME = 255; -int InjectedAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) { - unsigned char* currentBuffer = sourceBuffer + numBytesForPacketHeader(sourceBuffer); +int InjectedAudioRingBuffer::parseData(const QByteArray& packet) { + // setup a data stream to read from this packet + QDataStream packetStream(packet); + packetStream.skipRawData(numBytesForPacketHeader(packet)); - // push past the UUID for this node and the stream identifier - currentBuffer += (NUM_BYTES_RFC4122_UUID * 2); + // push past the stream identifier + packetStream.skipRawData(NUM_BYTES_RFC4122_UUID); // pull the loopback flag and set our boolean uchar shouldLoopback; - memcpy(&shouldLoopback, currentBuffer, sizeof(shouldLoopback)); - currentBuffer += sizeof(shouldLoopback); + packetStream >> shouldLoopback; _shouldLoopbackForNode = (shouldLoopback == 1); // use parsePositionalData in parent PostionalAudioRingBuffer class to pull common positional data - currentBuffer += parsePositionalData(currentBuffer, numBytes - (currentBuffer - sourceBuffer)); + packetStream.skipRawData(parsePositionalData(packet.mid(packetStream.device()->pos()))); // pull out the radius for this injected source - if it's zero this is a point source - memcpy(&_radius, currentBuffer, sizeof(_radius)); - currentBuffer += sizeof(_radius); + packetStream >> _radius; - unsigned int attenuationByte = *(currentBuffer++); + quint8 attenuationByte = 0; + packetStream >> attenuationByte; _attenuationRatio = attenuationByte / (float) MAX_INJECTOR_VOLUME; - currentBuffer += writeData((char*) currentBuffer, numBytes - (currentBuffer - sourceBuffer)); + packetStream.skipRawData(writeData(packet.data() + packetStream.device()->pos(), + packet.size() - packetStream.device()->pos())); - return currentBuffer - sourceBuffer; + return packetStream.device()->pos(); } diff --git a/libraries/audio/src/InjectedAudioRingBuffer.h b/libraries/audio/src/InjectedAudioRingBuffer.h index b5845366fb..d4dfb5e360 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.h +++ b/libraries/audio/src/InjectedAudioRingBuffer.h @@ -17,7 +17,7 @@ class InjectedAudioRingBuffer : public PositionalAudioRingBuffer { public: InjectedAudioRingBuffer(const QUuid& streamIdentifier = QUuid()); - int parseData(unsigned char* sourceBuffer, int numBytes); + int parseData(const QByteArray& packet); const QUuid& getStreamIdentifier() const { return _streamIdentifier; } float getRadius() const { return _radius; } diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 97793a98fb..27b62a8a56 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -8,6 +8,8 @@ #include +#include + #include #include #include @@ -35,23 +37,24 @@ PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer:: PositionalAudioRingBuffer::~PositionalAudioRingBuffer() { } -int PositionalAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) { - unsigned char* currentBuffer = sourceBuffer + numBytesForPacketHeader(sourceBuffer); - currentBuffer += NUM_BYTES_RFC4122_UUID; // the source UUID - currentBuffer += parsePositionalData(currentBuffer, numBytes - (currentBuffer - sourceBuffer)); - currentBuffer += writeData((char*) currentBuffer, numBytes - (currentBuffer - sourceBuffer)); +int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { + QDataStream packetStream(packet); + + // skip the packet header (includes the source UUID) + packetStream.skipRawData(numBytesForPacketHeader(packet)); + + packetStream.skipRawData(parsePositionalData(packet.mid(packetStream.device()->pos()))); + packetStream.skipRawData(writeData(packet.data() + packetStream.device()->pos(), + packet.size() - packetStream.device()->pos())); - return currentBuffer - sourceBuffer; + return packetStream.device()->pos(); } -int PositionalAudioRingBuffer::parsePositionalData(unsigned char* sourceBuffer, int numBytes) { - unsigned char* currentBuffer = sourceBuffer; - - memcpy(&_position, currentBuffer, sizeof(_position)); - currentBuffer += sizeof(_position); - - memcpy(&_orientation, currentBuffer, sizeof(_orientation)); - currentBuffer += sizeof(_orientation); +int PositionalAudioRingBuffer::parsePositionalData(const QByteArray& positionalByteArray) { + QDataStream packetStream(positionalByteArray); + + packetStream.readRawData(reinterpret_cast(&_position), sizeof(_position)); + packetStream.readRawData(reinterpret_cast(&_orientation), sizeof(_orientation)); // if this node sent us a NaN for first float in orientation then don't consider this good audio and bail if (isnan(_orientation.x)) { @@ -59,7 +62,7 @@ int PositionalAudioRingBuffer::parsePositionalData(unsigned char* sourceBuffer, return 0; } - return currentBuffer - sourceBuffer; + return packetStream.device()->pos(); } bool PositionalAudioRingBuffer::shouldBeAddedToMix(int numJitterBufferSamples) { diff --git a/libraries/audio/src/PositionalAudioRingBuffer.h b/libraries/audio/src/PositionalAudioRingBuffer.h index 55ed627c4d..e3fffa1689 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.h +++ b/libraries/audio/src/PositionalAudioRingBuffer.h @@ -24,9 +24,9 @@ public: PositionalAudioRingBuffer(PositionalAudioRingBuffer::Type type); ~PositionalAudioRingBuffer(); - int parseData(unsigned char* sourceBuffer, int numBytes); - int parsePositionalData(unsigned char* sourceBuffer, int numBytes); - int parseListenModeData(unsigned char* sourceBuffer, int numBytes); + int parseData(const QByteArray& packet); + int parsePositionalData(const QByteArray& positionalByteArray); + int parseListenModeData(const QByteArray& listenModeByteArray); bool shouldBeAddedToMix(int numJitterBufferSamples); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 8f548869ec..98afa76107 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include #include #include @@ -52,9 +54,7 @@ void AvatarData::setHandPosition(const glm::vec3& handPosition) { _handPosition = glm::inverse(getOrientation()) * (handPosition - _position); } -int AvatarData::getBroadcastData(unsigned char* destinationBuffer) { - unsigned char* bufferStart = destinationBuffer; - +QByteArray AvatarData::toByteArray() { // TODO: DRY this up to a shared method // that can pack any type given the number of bytes // and return the number of bytes to push the pointer @@ -68,9 +68,14 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) { _handData = new HandData(this); } - // Body world position - memcpy(destinationBuffer, &_position, sizeof(float) * 3); - destinationBuffer += sizeof(float) * 3; + QByteArray avatarDataByteArray; + avatarDataByteArray.resize(MAX_PACKET_SIZE); + + unsigned char* destinationBuffer = reinterpret_cast(avatarDataByteArray.data()); + unsigned char* startPosition = destinationBuffer; + + memcpy(destinationBuffer, &_position, sizeof(_position)); + destinationBuffer += sizeof(_position); // Body rotation (NOTE: This needs to become a quaternion to save two bytes) destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _bodyYaw); @@ -85,6 +90,7 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) { destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->_pitch); destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->_roll); + // Head lean X,Z (head lateral and fwd/back motion relative to torso) memcpy(destinationBuffer, &_headData->_leanSideways, sizeof(_headData->_leanSideways)); destinationBuffer += sizeof(_headData->_leanSideways); @@ -150,11 +156,11 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) { // leap hand data destinationBuffer += _handData->encodeRemoteData(destinationBuffer); - return destinationBuffer - bufferStart; + return avatarDataByteArray.left(destinationBuffer - startPosition); } // called on the other nodes - assigns it to my views of the others -int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { +int AvatarData::parseData(const QByteArray& packet) { // lazily allocate memory for HeadData in case we're not an Avatar instance if (!_headData) { @@ -167,14 +173,9 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { } // increment to push past the packet header - int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer); - sourceBuffer += numBytesPacketHeader; + const unsigned char* startPosition = reinterpret_cast(packet.data()); + const unsigned char* sourceBuffer = startPosition + numBytesForPacketHeader(packet); - unsigned char* startPosition = sourceBuffer; - - // push past the node session UUID - sourceBuffer += NUM_BYTES_RFC4122_UUID; - // Body world position memcpy(&_position, sourceBuffer, sizeof(float) * 3); sourceBuffer += sizeof(float) * 3; @@ -183,10 +184,10 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyYaw); sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyPitch); sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyRoll); - + // Body scale sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, _targetScale); - + // Head rotation (NOTE: This needs to become a quaternion to save two bytes) float headYaw, headPitch, headRoll; sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headYaw); @@ -196,64 +197,64 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { _headData->setYaw(headYaw); _headData->setPitch(headPitch); _headData->setRoll(headRoll); - + // Head position relative to pelvis memcpy(&_headData->_leanSideways, sourceBuffer, sizeof(_headData->_leanSideways)); sourceBuffer += sizeof(float); memcpy(&_headData->_leanForward, sourceBuffer, sizeof(_headData->_leanForward)); sourceBuffer += sizeof(_headData->_leanForward); - + // Hand Position - is relative to body position glm::vec3 handPositionRelative; memcpy(&handPositionRelative, sourceBuffer, sizeof(float) * 3); _handPosition = _position + handPositionRelative; sourceBuffer += sizeof(float) * 3; - + // Lookat Position memcpy(&_headData->_lookAtPosition, sourceBuffer, sizeof(_headData->_lookAtPosition)); sourceBuffer += sizeof(_headData->_lookAtPosition); - + // Instantaneous audio loudness (used to drive facial animation) //sourceBuffer += unpackFloatFromByte(sourceBuffer, _audioLoudness, MAX_AUDIO_LOUDNESS); memcpy(&_headData->_audioLoudness, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); - + // the rest is a chat message int chatMessageSize = *sourceBuffer++; _chatMessage = string((char*)sourceBuffer, chatMessageSize); sourceBuffer += chatMessageSize * sizeof(char); - + // voxel sending features... unsigned char bitItems = 0; bitItems = (unsigned char)*sourceBuffer++; - + // key state, stored as a semi-nibble in the bitItems _keyState = (KeyState)getSemiNibbleAt(bitItems,KEY_STATE_START_BIT); - + // hand state, stored as a semi-nibble in the bitItems _handState = getSemiNibbleAt(bitItems,HAND_STATE_START_BIT); - + _headData->_isFaceshiftConnected = oneAtBit(bitItems, IS_FACESHIFT_CONNECTED); - + _isChatCirclingEnabled = oneAtBit(bitItems, IS_CHAT_CIRCLING_ENABLED); - + // If it is connected, pack up the data if (_headData->_isFaceshiftConnected) { memcpy(&_headData->_leftEyeBlink, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); - + memcpy(&_headData->_rightEyeBlink, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); - + memcpy(&_headData->_averageLoudness, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); - + memcpy(&_headData->_browAudioLift, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); _headData->_blendshapeCoefficients.resize(*sourceBuffer++); memcpy(_headData->_blendshapeCoefficients.data(), sourceBuffer, - _headData->_blendshapeCoefficients.size() * sizeof(float)); + _headData->_blendshapeCoefficients.size() * sizeof(float)); sourceBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float); } @@ -261,11 +262,11 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f); // leap hand data - if (sourceBuffer - startPosition < numBytes) { + if (sourceBuffer - startPosition < packet.size()) { // check passed, bytes match - sourceBuffer += _handData->decodeRemoteData(sourceBuffer); + sourceBuffer += _handData->decodeRemoteData(packet.mid(sourceBuffer - startPosition)); } - + return sourceBuffer - startPosition; } @@ -276,3 +277,10 @@ void AvatarData::setClampedTargetScale(float targetScale) { _targetScale = targetScale; qDebug() << "Changed scale to " << _targetScale; } + +void AvatarData::setOrientation(const glm::quat& orientation) { + glm::vec3 eulerAngles = safeEulerAngles(orientation); + _bodyPitch = eulerAngles.x; + _bodyYaw = eulerAngles.y; + _bodyRoll = eulerAngles.z; +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index fc9bad7f02..0869445090 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -19,7 +19,7 @@ typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef signed long long int64_t; -typedef unsigned long long uint64_t; +typedef unsigned long long quint64; #define PRId64 "I64d" #else #include @@ -69,6 +69,10 @@ class AvatarData : public NodeData { Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch) Q_PROPERTY(float bodyRoll READ getBodyRoll WRITE setBodyRoll) Q_PROPERTY(QString chatMessage READ getQStringChatMessage WRITE setChatMessage) + + Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) + Q_PROPERTY(float headPitch READ getHeadPitch WRITE setHeadPitch) + public: AvatarData(); ~AvatarData(); @@ -79,8 +83,8 @@ public: glm::vec3 getHandPosition() const; void setHandPosition(const glm::vec3& handPosition); - int getBroadcastData(unsigned char* destinationBuffer); - int parseData(unsigned char* sourceBuffer, int numBytes); + QByteArray toByteArray(); + int parseData(const QByteArray& packet); // Body Rotation float getBodyYaw() const { return _bodyYaw; } @@ -91,6 +95,11 @@ public: void setBodyRoll(float bodyRoll) { _bodyRoll = bodyRoll; } glm::quat getOrientation() const { return glm::quat(glm::radians(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll))); } + void setOrientation(const glm::quat& orientation); + + // access to Head().set/getMousePitch + float getHeadPitch() const { return _headData->getPitch(); } + void setHeadPitch(float value) { _headData->setPitch(value); }; // Scale float getTargetScale() const { return _targetScale; } diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp new file mode 100644 index 0000000000..72ada7d421 --- /dev/null +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -0,0 +1,23 @@ +// +// AvatarHashMap.cpp +// hifi +// +// Created by Stephen AndrewMeadows on 1/28/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include "AvatarHashMap.h" + +AvatarHashMap::AvatarHashMap() : + _avatarHash() +{ +} + +void AvatarHashMap::insert(const QUuid& id, AvatarSharedPointer avatar) { + _avatarHash.insert(id, avatar); +} + +AvatarHash::iterator AvatarHashMap::erase(const AvatarHash::iterator& iterator) { + return _avatarHash.erase(iterator); +} + diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h new file mode 100644 index 0000000000..19b6623402 --- /dev/null +++ b/libraries/avatars/src/AvatarHashMap.h @@ -0,0 +1,36 @@ +// +// AvatarHashMap.h +// hifi +// +// Created by Stephen AndrewMeadows on 1/28/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__AvatarHashMap__ +#define __hifi__AvatarHashMap__ + +#include +#include +#include + +#include "AvatarData.h" + +typedef QSharedPointer AvatarSharedPointer; +typedef QHash AvatarHash; + +class AvatarHashMap { +public: + AvatarHashMap(); + + const AvatarHash& getAvatarHash() { return _avatarHash; } + int size() const { return _avatarHash.size(); } + + virtual void insert(const QUuid& id, AvatarSharedPointer avatar); + +protected: + virtual AvatarHash::iterator erase(const AvatarHash::iterator& iterator); + + AvatarHash _avatarHash; +}; + +#endif /* defined(__hifi__AvatarHashMap__) */ diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 81d4469486..5a923eea93 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -6,6 +6,8 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +#include + #include "HandData.h" #include "AvatarData.h" #include @@ -157,22 +159,22 @@ int HandData::encodeRemoteData(unsigned char* destinationBuffer) { return destinationBuffer - startPosition; } -int HandData::decodeRemoteData(unsigned char* sourceBuffer) { - const unsigned char* startPosition = sourceBuffer; - +int HandData::decodeRemoteData(const QByteArray& dataByteArray) { + const unsigned char* startPosition; + const unsigned char* sourceBuffer = startPosition = reinterpret_cast(dataByteArray.data()); unsigned int numHands = *sourceBuffer++; for (unsigned int handIndex = 0; handIndex < numHands; ++handIndex) { if (handIndex >= getNumPalms()) addNewPalm(); PalmData& palm = getPalms()[handIndex]; - + glm::vec3 handPosition; glm::vec3 handNormal; sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, handPosition, fingerVectorRadix); sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, handNormal, fingerVectorRadix); unsigned int numFingers = *sourceBuffer++; - + palm.setRawPosition(handPosition); palm.setRawNormal(handNormal); palm.setActive(true); @@ -183,12 +185,12 @@ int HandData::decodeRemoteData(unsigned char* sourceBuffer) { for (unsigned int fingerIndex = 0; fingerIndex < numFingers; ++fingerIndex) { if (fingerIndex < palm.getNumFingers()) { FingerData& finger = palm.getFingers()[fingerIndex]; - + glm::vec3 tipPosition; glm::vec3 rootPosition; sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, tipPosition, fingerVectorRadix); sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, rootPosition, fingerVectorRadix); - + finger.setRawTipPosition(tipPosition); finger.setRawRootPosition(rootPosition); finger.setActive(true); @@ -210,7 +212,7 @@ int HandData::decodeRemoteData(unsigned char* sourceBuffer) { unsigned char requiredLength = (unsigned char)(sourceBuffer - startPosition); unsigned char checkLength = *sourceBuffer++; assert(checkLength == requiredLength); - + return sourceBuffer - startPosition; } diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 0f1e393db9..550c62e829 100755 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -73,7 +73,7 @@ public: // Use these for sending and receiving hand data int encodeRemoteData(unsigned char* destinationBuffer); - int decodeRemoteData(unsigned char* sourceBuffer); + int decodeRemoteData(const QByteArray& dataByteArray); /// Checks for penetration between the described sphere and the hand. /// \param penetratorCenter the center of the penetration test sphere @@ -228,7 +228,7 @@ private: bool _isCollidingWithVoxel; /// Whether the finger of this palm is inside a leaf voxel bool _isCollidingWithPalm; - uint64_t _collisionlessPaddleExpiry; /// Timestamp after which paddle starts colliding + quint64 _collisionlessPaddleExpiry; /// Timestamp after which paddle starts colliding }; #endif /* defined(__hifi__HandData__) */ diff --git a/libraries/embedded-webserver/src/HTTPConnection.cpp b/libraries/embedded-webserver/src/HTTPConnection.cpp index 3ffc96c9f4..50ce72e0cd 100755 --- a/libraries/embedded-webserver/src/HTTPConnection.cpp +++ b/libraries/embedded-webserver/src/HTTPConnection.cpp @@ -15,8 +15,10 @@ #include "HTTPManager.h" const char* HTTPConnection::StatusCode200 = "200 OK"; +const char* HTTPConnection::StatusCode301 = "301 Moved Permanently"; const char* HTTPConnection::StatusCode400 = "400 Bad Request"; const char* HTTPConnection::StatusCode404 = "404 Not Found"; +const char* HTTPConnection::DefaultContentType = "text/plain; charset=ISO-8859-1"; HTTPConnection::HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager) : QObject(parentManager), diff --git a/libraries/embedded-webserver/src/HTTPConnection.h b/libraries/embedded-webserver/src/HTTPConnection.h index 9342c32849..dbf1e31f47 100644 --- a/libraries/embedded-webserver/src/HTTPConnection.h +++ b/libraries/embedded-webserver/src/HTTPConnection.h @@ -40,8 +40,10 @@ class HTTPConnection : public QObject { public: static const char* StatusCode200; + static const char* StatusCode301; static const char* StatusCode400; static const char* StatusCode404; + static const char* DefaultContentType; /// WebSocket close status codes. enum ReasonCode { NoReason = 0, NormalClosure = 1000, GoingAway = 1001 }; @@ -72,7 +74,7 @@ public: /// Sends a response and closes the connection. void respond (const char* code, const QByteArray& content = QByteArray(), - const char* contentType = "text/plain; charset=ISO-8859-1", + const char* contentType = DefaultContentType, const Headers& headers = Headers()); protected slots: diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 735300de7c..a217555a78 100755 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -23,7 +23,6 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p } // check to see if there is a file to serve from the document root for this path - QString subPath = path; // remove any slash at the beginning of the path @@ -33,6 +32,19 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p QString filePath; + if (QFileInfo(_documentRoot + subPath).isFile()) { + filePath = _documentRoot + subPath; + } else if (subPath.size() > 0 && !subPath.endsWith('/')) { + // this could be a directory with a trailing slash + // send a redirect to the path with a slash so we can + QString redirectLocation = '/' + subPath + '/'; + + QHash redirectHeader; + redirectHeader.insert(QByteArray("Location"), redirectLocation.toUtf8()); + + connection->respond(HTTPConnection::StatusCode301, "", HTTPConnection::DefaultContentType, redirectHeader); + } + // if the last thing is a trailing slash then we want to look for index file if (subPath.endsWith('/') || subPath.size() == 0) { QStringList possibleIndexFiles = QStringList() << "index.html" << "index.shtml"; @@ -43,19 +55,15 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p break; } } - } else if (QFileInfo(_documentRoot + subPath).isFile()) { - filePath = _documentRoot + subPath; } - if (!filePath.isEmpty()) { // file exists, serve it - static QMimeDatabase mimeDatabase; QFile localFile(filePath); localFile.open(QIODevice::ReadOnly); - QString localFileString(localFile.readAll()); + QByteArray localFileData = localFile.readAll(); QFileInfo localFileInfo(filePath); @@ -69,6 +77,7 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p int matchPosition = 0; + QString localFileString(localFileData); while ((matchPosition = includeRegExp.indexIn(localFileString, matchPosition)) != -1) { // check if this is a file or vitual include @@ -97,10 +106,14 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p // push the match position forward so we can check the next match matchPosition += includeRegExp.matchedLength(); } + + localFileData = localFileString.toLocal8Bit(); } - connection->respond(HTTPConnection::StatusCode200, localFileString.toLocal8Bit(), qPrintable(mimeDatabase.mimeTypeForFile(filePath).name())); + connection->respond(HTTPConnection::StatusCode200, localFileData, + qPrintable(mimeDatabase.mimeTypeForFile(filePath).name())); } else { + // respond with a 404 connection->respond(HTTPConnection::StatusCode404, "Resource not found."); } diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index e1ab79c974..4f23c79d78 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -237,6 +237,29 @@ Bitstream& Bitstream::operator>>(QVariant& value) { return *this; } +Bitstream& Bitstream::operator<<(const AttributeValue& attributeValue) { + _attributeStreamer << attributeValue.getAttribute(); + if (attributeValue.getAttribute()) { + attributeValue.getAttribute()->write(*this, attributeValue.getValue(), true); + } + return *this; +} + +Bitstream& Bitstream::operator>>(OwnedAttributeValue& attributeValue) { + AttributePointer attribute; + _attributeStreamer >> attribute; + if (attribute) { + void* value = attribute->create(); + attribute->read(*this, value, true); + attributeValue = AttributeValue(attribute, value); + attribute->destroy(value); + + } else { + attributeValue = AttributeValue(); + } + return *this; +} + Bitstream& Bitstream::operator<<(const QObject* object) { if (!object) { _metaObjectStreamer << NULL; diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 09c14d63e6..5764cf2cdf 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -23,7 +23,9 @@ struct QMetaObject; class QObject; class Attribute; +class AttributeValue; class Bitstream; +class OwnedAttributeValue; class TypeStreamer; typedef QSharedPointer AttributePointer; @@ -89,9 +91,11 @@ template inline QHash RepeatedValueStreamer::getAndResetTran template inline void RepeatedValueStreamer::persistTransientOffsets(const QHash& transientOffsets) { int oldLastPersistentID = _lastPersistentID; for (typename QHash::const_iterator it = transientOffsets.constBegin(); it != transientOffsets.constEnd(); it++) { - int id = oldLastPersistentID + it.value(); - _lastPersistentID = qMax(_lastPersistentID, id); - _persistentIDs.insert(it.key(), id); + int& id = _persistentIDs[it.key()]; + if (id == 0) { + id = oldLastPersistentID + it.value(); + _lastPersistentID = qMax(_lastPersistentID, id); + } } _idStreamer.setBitsFromValue(_lastPersistentID); } @@ -106,9 +110,12 @@ template inline QHash RepeatedValueStreamer::getAndResetTran template inline void RepeatedValueStreamer::persistTransientValues(const QHash& transientValues) { int oldLastPersistentID = _lastPersistentID; for (typename QHash::const_iterator it = transientValues.constBegin(); it != transientValues.constEnd(); it++) { - int id = oldLastPersistentID + it.key(); - _lastPersistentID = qMax(_lastPersistentID, id); - _persistentValues.insert(id, it.value()); + int& id = _persistentIDs[it.value()]; + if (id == 0) { + id = oldLastPersistentID + it.key(); + _lastPersistentID = qMax(_lastPersistentID, id); + _persistentValues.insert(id, it.value()); + } } _idStreamer.setBitsFromValue(_lastPersistentID); } @@ -231,6 +238,9 @@ public: Bitstream& operator<<(const QVariant& value); Bitstream& operator>>(QVariant& value); + Bitstream& operator<<(const AttributeValue& attributeValue); + Bitstream& operator>>(OwnedAttributeValue& attributeValue); + template Bitstream& operator<<(const QList& list); template Bitstream& operator>>(QList& list); diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 028a01e2e8..83ef641b39 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -25,7 +25,8 @@ DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader) : _outgoingDatagramStream(&_outgoingDatagramBuffer), _incomingPacketNumber(0), _incomingPacketStream(&_incomingPacketData, QIODevice::ReadOnly), - _inputStream(_incomingPacketStream) { + _inputStream(_incomingPacketStream), + _receivedHighPriorityMessages(0) { _outgoingPacketStream.setByteOrder(QDataStream::LittleEndian); _incomingDatagramStream.setByteOrder(QDataStream::LittleEndian); @@ -35,6 +36,34 @@ DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader) : memcpy(_outgoingDatagram.data(), datagramHeader.constData(), _datagramHeaderSize); } +void DatagramSequencer::sendHighPriorityMessage(const QVariant& data) { + HighPriorityMessage message = { data, _outgoingPacketNumber + 1 }; + _highPriorityMessages.append(message); +} + +Bitstream& DatagramSequencer::startPacket() { + // start with the list of acknowledgements + _outgoingPacketStream << (quint32)_receiveRecords.size(); + foreach (const ReceiveRecord& record, _receiveRecords) { + _outgoingPacketStream << (quint32)record.packetNumber; + } + + // write the high-priority messages + _outgoingPacketStream << (quint32)_highPriorityMessages.size(); + foreach (const HighPriorityMessage& message, _highPriorityMessages) { + _outputStream << message.data; + } + + // return the stream, allowing the caller to write the rest + return _outputStream; +} + +void DatagramSequencer::endPacket() { + _outputStream.flush(); + sendPacket(QByteArray::fromRawData(_outgoingPacketData.constData(), _outgoingPacketStream.device()->pos())); + _outgoingPacketStream.device()->seek(0); +} + /// Simple RAII-style object to keep a device open when in scope. class QIODeviceOpener { public: @@ -47,21 +76,6 @@ private: QIODevice* _device; }; -Bitstream& DatagramSequencer::startPacket() { - // start with the list of acknowledgements - _outgoingPacketStream << (quint32)_receiveRecords.size(); - foreach (const ReceiveRecord& record, _receiveRecords) { - _outgoingPacketStream << (quint32)record.packetNumber; - } - return _outputStream; -} - -void DatagramSequencer::endPacket() { - _outputStream.flush(); - sendPacket(QByteArray::fromRawData(_outgoingPacketData.constData(), _outgoingPacketStream.device()->pos())); - _outgoingPacketStream.device()->seek(0); -} - void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { _incomingDatagramBuffer.setData(datagram.constData() + _datagramHeaderSize, datagram.size() - _datagramHeaderSize); QIODeviceOpener opener(&_incomingDatagramBuffer, QIODevice::ReadOnly); @@ -123,25 +137,47 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { _sendRecords.erase(_sendRecords.begin(), it + 1); } + // read and dispatch the high-priority messages + quint32 highPriorityMessageCount; + _incomingPacketStream >> highPriorityMessageCount; + int newHighPriorityMessages = highPriorityMessageCount - _receivedHighPriorityMessages; + for (int i = 0; i < highPriorityMessageCount; i++) { + QVariant data; + _inputStream >> data; + if (i >= _receivedHighPriorityMessages) { + emit receivedHighPriorityMessage(data); + } + } + _receivedHighPriorityMessages = highPriorityMessageCount; + + // alert external parties so that they can read the rest emit readyToRead(_inputStream); _incomingPacketStream.device()->seek(0); _inputStream.reset(); // record the receipt - ReceiveRecord record = { _incomingPacketNumber, _inputStream.getAndResetReadMappings() }; + ReceiveRecord record = { _incomingPacketNumber, _inputStream.getAndResetReadMappings(), newHighPriorityMessages }; _receiveRecords.append(record); } void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { - // stop acknowledging the recorded packets (TODO: replace with interpolation search?) - ReceiveRecord compare = { record.lastReceivedPacketNumber }; - QList::iterator it = qBinaryFind(_receiveRecords.begin(), _receiveRecords.end(), compare); - if (it != _receiveRecords.end()) { - _inputStream.persistReadMappings(it->mappings); - emit receiveAcknowledged(it - _receiveRecords.begin()); - _receiveRecords.erase(_receiveRecords.begin(), it + 1); + // stop acknowledging the recorded packets + while (!_receiveRecords.isEmpty() && _receiveRecords.first().packetNumber <= record.lastReceivedPacketNumber) { + const ReceiveRecord& received = _receiveRecords.first(); + _inputStream.persistReadMappings(received.mappings); + _receivedHighPriorityMessages -= received.newHighPriorityMessages; + emit receiveAcknowledged(0); + _receiveRecords.removeFirst(); } _outputStream.persistWriteMappings(record.mappings); + + // remove the received high priority messages + for (int i = _highPriorityMessages.size() - 1; i >= 0; i--) { + if (_highPriorityMessages.at(i).firstPacketNumber <= record.packetNumber) { + _highPriorityMessages.erase(_highPriorityMessages.begin(), _highPriorityMessages.begin() + i + 1); + break; + } + } } void DatagramSequencer::sendPacket(const QByteArray& packet) { diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index e5e4b8c4be..4ef827abeb 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -23,6 +23,12 @@ class DatagramSequencer : public QObject { public: + class HighPriorityMessage { + public: + QVariant data; + int firstPacketNumber; + }; + DatagramSequencer(const QByteArray& datagramHeader = QByteArray()); /// Returns the packet number of the last packet sent. @@ -31,6 +37,15 @@ public: /// Returns the packet number of the last packet received (or the packet currently being assembled). int getIncomingPacketNumber() const { return _incomingPacketNumber; } + /// Returns the packet number of the sent packet at the specified index. + int getSentPacketNumber(int index) const { return _sendRecords.at(index).packetNumber; } + + /// Adds a message to the high priority queue. Will be sent with every outgoing packet until received. + void sendHighPriorityMessage(const QVariant& data); + + /// Returns a reference to the list of high priority messages not yet acknowledged. + const QList& getHighPriorityMessages() const { return _highPriorityMessages; } + /// Starts a new packet for transmission. /// \return a reference to the Bitstream to use for writing to the packet Bitstream& startPacket(); @@ -50,6 +65,9 @@ signals: /// Emitted when a packet is available to read. void readyToRead(Bitstream& input); + /// Emitted when we've received a high-priority message + void receivedHighPriorityMessage(const QVariant& data); + /// Emitted when a sent packet has been acknowledged by the remote side. /// \param index the index of the packet in our list of send records void sendAcknowledged(int index); @@ -71,6 +89,7 @@ private: public: int packetNumber; Bitstream::ReadMappings mappings; + int newHighPriorityMessages; bool operator<(const ReceiveRecord& other) const { return packetNumber < other.packetNumber; } }; @@ -104,6 +123,9 @@ private: Bitstream _inputStream; QSet _offsetsReceived; int _remainingBytes; + + QList _highPriorityMessages; + int _receivedHighPriorityMessages; }; #endif /* defined(__interface__DatagramSequencer__) */ diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index ffbe5b4ff8..2f99684a42 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -10,11 +10,15 @@ #include #include "MetavoxelData.h" +#include "MetavoxelUtil.h" -MetavoxelData::MetavoxelData() { +MetavoxelData::MetavoxelData() : _size(1.0f) { } -MetavoxelData::MetavoxelData(const MetavoxelData& other) : _roots(other._roots) { +MetavoxelData::MetavoxelData(const MetavoxelData& other) : + _size(other._size), + _roots(other._roots) { + incrementRootReferenceCounts(); } @@ -24,18 +28,24 @@ MetavoxelData::~MetavoxelData() { MetavoxelData& MetavoxelData::operator=(const MetavoxelData& other) { decrementRootReferenceCounts(); + _size = other._size; _roots = other._roots; incrementRootReferenceCounts(); return *this; } +Box MetavoxelData::getBounds() const { + float halfSize = _size * 0.5f; + Box bounds = { glm::vec3(-halfSize, -halfSize, -halfSize), glm::vec3(halfSize, halfSize, halfSize) }; + return bounds; +} + void MetavoxelData::guide(MetavoxelVisitor& visitor) { // start with the root values/defaults (plus the guide attribute) - const float TOP_LEVEL_SIZE = 1.0f; const QVector& inputs = visitor.getInputs(); const QVector& outputs = visitor.getOutputs(); - MetavoxelVisitation firstVisitation = { this, NULL, visitor, QVector(inputs.size() + 1), - QVector(outputs.size()), { glm::vec3(), TOP_LEVEL_SIZE, + MetavoxelVisitation firstVisitation = { NULL, visitor, QVector(inputs.size() + 1), + QVector(outputs.size()), { glm::vec3(_size, _size, _size) * -0.5f, _size, QVector(inputs.size() + 1), QVector(outputs.size()) } }; for (int i = 0; i < inputs.size(); i++) { MetavoxelNode* node = _roots.value(inputs.at(i)); @@ -54,42 +64,80 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) { PolymorphicDataPointer>().data())->guide(firstVisitation); for (int i = 0; i < outputs.size(); i++) { AttributeValue& value = firstVisitation.info.outputValues[i]; - if (value.getAttribute()) { - MetavoxelNode* node = firstVisitation.outputNodes.at(i); - if (node->isLeaf() && value.isDefault()) { - node->decrementReferenceCount(value.getAttribute()); - _roots.remove(value.getAttribute()); - } + if (!value.getAttribute()) { + continue; + } + // replace the old node with the new + MetavoxelNode*& node = _roots[value.getAttribute()]; + if (node) { + node->decrementReferenceCount(value.getAttribute()); + } + node = firstVisitation.outputNodes.at(i); + if (node->isLeaf() && value.isDefault()) { + // immediately remove the new node if redundant + node->decrementReferenceCount(value.getAttribute()); + _roots.remove(value.getAttribute()); } } } +const int X_MAXIMUM_FLAG = 1; +const int Y_MAXIMUM_FLAG = 2; +const int Z_MAXIMUM_FLAG = 4; +const int MAXIMUM_FLAG_MASK = X_MAXIMUM_FLAG | Y_MAXIMUM_FLAG | Z_MAXIMUM_FLAG; + +static int getOppositeIndex(int index) { + return index ^ MAXIMUM_FLAG_MASK; +} + +void MetavoxelData::expand() { + for (QHash::iterator it = _roots.begin(); it != _roots.end(); it++) { + MetavoxelNode* newParent = new MetavoxelNode(it.key()); + for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { + MetavoxelNode* newChild = new MetavoxelNode(it.key()); + newParent->setChild(i, newChild); + int index = getOppositeIndex(i); + if (it.value()->isLeaf()) { + newChild->setChild(index, new MetavoxelNode(it.value()->getAttributeValue(it.key()))); + } else { + MetavoxelNode* grandchild = it.value()->getChild(i); + grandchild->incrementReferenceCount(); + newChild->setChild(index, grandchild); + } + for (int j = 1; j < MetavoxelNode::CHILD_COUNT; j++) { + MetavoxelNode* newGrandchild = new MetavoxelNode(it.key()); + newChild->setChild((index + j) % MetavoxelNode::CHILD_COUNT, newGrandchild); + } + newChild->mergeChildren(it.key()); + } + newParent->mergeChildren(it.key()); + it.value()->decrementReferenceCount(it.key()); + it.value() = newParent; + } + _size *= 2.0f; +} + void MetavoxelData::read(Bitstream& in) { - // save the old roots and clear - QHash oldRoots = _roots; + // clear out any existing roots + decrementRootReferenceCounts(); _roots.clear(); + + in >> _size; - // read in the new roots, reusing old ones where appropriate + // read in the new roots int rootCount; in >> rootCount; for (int i = 0; i < rootCount; i++) { AttributePointer attribute; in.getAttributeStreamer() >> attribute; - MetavoxelNode* root = oldRoots.take(attribute); - if (!root) { - root = new MetavoxelNode(attribute); - } - _roots.insert(attribute, root); + MetavoxelNode*& root = _roots[attribute]; + root = new MetavoxelNode(attribute); root->read(attribute, in); } - - // clear out the remaining old roots - for (QHash::const_iterator it = oldRoots.constBegin(); it != oldRoots.constEnd(); it++) { - it.value()->decrementReferenceCount(it.key()); - } } void MetavoxelData::write(Bitstream& out) const { + out << _size; out << _roots.size(); for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { out.getAttributeStreamer() << it.key(); @@ -98,22 +146,41 @@ void MetavoxelData::write(Bitstream& out) const { } void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { + // shallow copy the reference + *this = reference; + + bool changed; + in >> changed; + if (!changed) { + return; + } + + bool sizeChanged; + in >> sizeChanged; + if (sizeChanged) { + float size; + in >> size; + while (_size < size) { + expand(); + } + } + int changedCount; in >> changedCount; for (int i = 0; i < changedCount; i++) { AttributePointer attribute; in.getAttributeStreamer() >> attribute; MetavoxelNode*& root = _roots[attribute]; - if (!root) { + if (root) { + MetavoxelNode* oldRoot = root; root = new MetavoxelNode(attribute); - } - MetavoxelNode* referenceRoot = reference._roots.value(attribute); - if (referenceRoot) { - root->readDelta(attribute, *referenceRoot, in); - + root->readDelta(attribute, *oldRoot, in); + oldRoot->decrementReferenceCount(attribute); + } else { + root = new MetavoxelNode(attribute); root->read(attribute, in); - } + } } int removedCount; @@ -121,22 +188,44 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { for (int i = 0; i < removedCount; i++) { AttributePointer attribute; in.getAttributeStreamer() >> attribute; - + _roots.take(attribute)->decrementReferenceCount(attribute); } } void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) const { + // first things first: there might be no change whatsoever + if (_size == reference._size && _roots == reference._roots) { + out << false; + return; + } + out << true; + + // compare the size; if changed (rare), we must compare to the expanded reference + const MetavoxelData* expandedReference = &reference; + if (_size == reference._size) { + out << false; + } else { + out << true; + out << _size; + + MetavoxelData* expanded = new MetavoxelData(reference); + while (expanded->_size < _size) { + expanded->expand(); + } + expandedReference = expanded; + } + // count the number of roots added/changed, then write int changedCount = 0; for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { - MetavoxelNode* referenceRoot = reference._roots.value(it.key()); + MetavoxelNode* referenceRoot = expandedReference->_roots.value(it.key()); if (it.value() != referenceRoot) { changedCount++; } } out << changedCount; for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { - MetavoxelNode* referenceRoot = reference._roots.value(it.key()); + MetavoxelNode* referenceRoot = expandedReference->_roots.value(it.key()); if (it.value() != referenceRoot) { out.getAttributeStreamer() << it.key(); if (referenceRoot) { @@ -149,19 +238,24 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) c // same with nodes removed int removedCount = 0; - for (QHash::const_iterator it = reference._roots.constBegin(); - it != reference._roots.constEnd(); it++) { + for (QHash::const_iterator it = expandedReference->_roots.constBegin(); + it != expandedReference->_roots.constEnd(); it++) { if (!_roots.contains(it.key())) { removedCount++; } } out << removedCount; - for (QHash::const_iterator it = reference._roots.constBegin(); - it != reference._roots.constEnd(); it++) { + for (QHash::const_iterator it = expandedReference->_roots.constBegin(); + it != expandedReference->_roots.constEnd(); it++) { if (!_roots.contains(it.key())) { out.getAttributeStreamer() << it.key(); } } + + // delete the expanded reference if we had to expand + if (expandedReference != &reference) { + delete expandedReference; + } } void MetavoxelData::incrementRootReferenceCounts() { @@ -176,28 +270,6 @@ void MetavoxelData::decrementRootReferenceCounts() { } } -void writeDelta(const MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& out) { - if (data == reference) { - out << false; - - } else { - out << true; - data->writeDelta(*reference, out); - } -} - -void readDelta(MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& in) { - bool changed; - in >> changed; - if (changed) { - data.detach(); - data->readDelta(*reference, in); - - } else { - data = reference; - } -} - MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) : _referenceCount(1) { _attributeValue = attributeValue.copy(); for (int i = 0; i < CHILD_COUNT; i++) { @@ -205,6 +277,15 @@ MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) : _referenceC } } +MetavoxelNode::MetavoxelNode(const AttributePointer& attribute, const MetavoxelNode* copy) : _referenceCount(1) { + _attributeValue = attribute->create(copy->_attributeValue); + for (int i = 0; i < CHILD_COUNT; i++) { + if ((_children[i] = copy->_children[i])) { + _children[i]->incrementReferenceCount(); + } + } +} + void MetavoxelNode::setAttributeValue(const AttributeValue& attributeValue) { attributeValue.getAttribute()->destroy(_attributeValue); _attributeValue = attributeValue.copy(); @@ -237,22 +318,16 @@ bool MetavoxelNode::isLeaf() const { } void MetavoxelNode::read(const AttributePointer& attribute, Bitstream& in) { + clearChildren(attribute); + bool leaf; in >> leaf; attribute->read(in, _attributeValue, leaf); - if (leaf) { - clearChildren(attribute); - - } else { - void* childValues[CHILD_COUNT]; + if (!leaf) { for (int i = 0; i < CHILD_COUNT; i++) { - if (!_children[i]) { - _children[i] = new MetavoxelNode(attribute); - } + _children[i] = new MetavoxelNode(attribute); _children[i]->read(attribute, in); - childValues[i] = _children[i]->_attributeValue; } - attribute->merge(_attributeValue, childValues); } } @@ -268,20 +343,28 @@ void MetavoxelNode::write(const AttributePointer& attribute, Bitstream& out) con } void MetavoxelNode::readDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& in) { + clearChildren(attribute); + bool leaf; in >> leaf; attribute->readDelta(in, _attributeValue, reference._attributeValue, leaf); - if (leaf) { - clearChildren(attribute); - - } else { + if (!leaf) { if (reference.isLeaf()) { for (int i = 0; i < CHILD_COUNT; i++) { + _children[i] = new MetavoxelNode(attribute); _children[i]->read(attribute, in); } } else { for (int i = 0; i < CHILD_COUNT; i++) { - _children[i]->readDelta(attribute, *reference._children[i], in); + bool changed; + in >> changed; + if (changed) { + _children[i] = new MetavoxelNode(attribute); + _children[i]->readDelta(attribute, *reference._children[i], in); + } else { + _children[i] = reference._children[i]; + _children[i]->incrementReferenceCount(); + } } } } @@ -298,7 +381,12 @@ void MetavoxelNode::writeDelta(const AttributePointer& attribute, const Metavoxe } } else { for (int i = 0; i < CHILD_COUNT; i++) { - _children[i]->writeDelta(attribute, *reference._children[i], out); + if (_children[i] == reference._children[i]) { + out << false; + } else { + out << true; + _children[i]->writeDelta(attribute, *reference._children[i], out); + } } } } @@ -334,31 +422,33 @@ MetavoxelVisitor::MetavoxelVisitor(const QVector& inputs, cons _outputs(outputs) { } +MetavoxelVisitor::~MetavoxelVisitor() { +} + PolymorphicData* DefaultMetavoxelGuide::clone() const { return new DefaultMetavoxelGuide(); } -const int X_MAXIMUM_FLAG = 1; -const int Y_MAXIMUM_FLAG = 2; -const int Z_MAXIMUM_FLAG = 4; - void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { visitation.info.isLeaf = visitation.allInputNodesLeaves(); bool keepGoing = visitation.visitor.visit(visitation.info); for (int i = 0; i < visitation.outputNodes.size(); i++) { AttributeValue& value = visitation.info.outputValues[i]; - if (value.getAttribute()) { - MetavoxelNode*& node = visitation.outputNodes[i]; - if (!node) { - node = visitation.createOutputNode(i); - } - node->setAttributeValue(value); + if (!value.getAttribute()) { + continue; + } + MetavoxelNode*& node = visitation.outputNodes[i]; + if (node && node->isLeaf() && value.getAttribute()->equal(value.getValue(), node->getAttributeValue())) { + // "set" to same value; disregard + value = AttributeValue(); + } else { + node = new MetavoxelNode(value); } } if (!keepGoing) { return; } - MetavoxelVisitation nextVisitation = { visitation.data, &visitation, visitation.visitor, + MetavoxelVisitation nextVisitation = { &visitation, visitation.visitor, QVector(visitation.inputNodes.size()), QVector(visitation.outputNodes.size()), { glm::vec3(), visitation.info.size * 0.5f, QVector(visitation.inputNodes.size()), QVector(visitation.outputNodes.size()) } }; @@ -379,15 +469,39 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { (i & X_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f, (i & Y_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f, (i & Z_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f); - nextVisitation.childIndex = i; static_cast(nextVisitation.info.inputValues.last().getInlineValue< PolymorphicDataPointer>().data())->guide(nextVisitation); for (int j = 0; j < nextVisitation.outputNodes.size(); j++) { AttributeValue& value = nextVisitation.info.outputValues[j]; - if (value.getAttribute()) { - visitation.info.outputValues[j] = value; - value = AttributeValue(); + if (!value.getAttribute()) { + continue; } + // replace the child + AttributeValue& parentValue = visitation.info.outputValues[j]; + if (!parentValue.getAttribute()) { + // shallow-copy the parent node on first change + parentValue = value; + MetavoxelNode*& node = visitation.outputNodes[j]; + if (node) { + node = new MetavoxelNode(value.getAttribute(), node); + } else { + // create leaf with inherited value + node = new MetavoxelNode(visitation.getInheritedOutputValue(j)); + } + } + MetavoxelNode* node = visitation.outputNodes.at(j); + MetavoxelNode* child = node->getChild(i); + if (child) { + child->decrementReferenceCount(value.getAttribute()); + } else { + // it's a leaf; we need to split it up + AttributeValue nodeValue = node->getAttributeValue(value.getAttribute()); + for (int k = 1; k < MetavoxelNode::CHILD_COUNT; k++) { + node->setChild((i + k) % MetavoxelNode::CHILD_COUNT, new MetavoxelNode(nodeValue)); + } + } + node->setChild(i, nextVisitation.outputNodes.at(j)); + value = AttributeValue(); } } for (int i = 0; i < visitation.outputNodes.size(); i++) { @@ -507,20 +621,13 @@ bool MetavoxelVisitation::allInputNodesLeaves() const { return true; } -MetavoxelNode* MetavoxelVisitation::createOutputNode(int index) { - const AttributePointer& attribute = visitor.getOutputs().at(index); - if (previous) { - MetavoxelNode*& parent = previous->outputNodes[index]; - if (!parent) { - parent = previous->createOutputNode(index); +AttributeValue MetavoxelVisitation::getInheritedOutputValue(int index) const { + for (const MetavoxelVisitation* visitation = previous; visitation != NULL; visitation = visitation->previous) { + MetavoxelNode* node = visitation->outputNodes.at(index); + if (node) { + return node->getAttributeValue(visitor.getOutputs().at(index)); } - AttributeValue value = parent->getAttributeValue(attribute); - for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { - parent->_children[i] = new MetavoxelNode(value); - } - return parent->_children[childIndex]; - - } else { - return data->_roots[attribute] = new MetavoxelNode(attribute); } + return AttributeValue(visitor.getOutputs().at(index)); } + diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index a25e0bc9a8..0114f1b4d6 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -10,7 +10,6 @@ #define __interface__MetavoxelData__ #include -#include #include #include #include @@ -23,12 +22,13 @@ class QScriptContext; +class Box; class MetavoxelNode; class MetavoxelVisitation; class MetavoxelVisitor; /// The base metavoxel representation shared between server and client. -class MetavoxelData : public QSharedData { +class MetavoxelData { public: MetavoxelData(); @@ -37,8 +37,15 @@ public: MetavoxelData& operator=(const MetavoxelData& other); + float getSize() const { return _size; } + + Box getBounds() const; + /// Applies the specified visitor to the contained voxels. void guide(MetavoxelVisitor& visitor); + + /// Expands the tree, increasing its capacity in all dimensions. + void expand(); void read(Bitstream& in); void write(Bitstream& out) const; @@ -53,15 +60,10 @@ private: void incrementRootReferenceCounts(); void decrementRootReferenceCounts(); + float _size; QHash _roots; }; -typedef QExplicitlySharedDataPointer MetavoxelDataPointer; - -void writeDelta(const MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& out); - -void readDelta(MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& in); - /// A single node within a metavoxel layer. class MetavoxelNode { public: @@ -69,10 +71,12 @@ public: static const int CHILD_COUNT = 8; MetavoxelNode(const AttributeValue& attributeValue); + MetavoxelNode(const AttributePointer& attribute, const MetavoxelNode* copy); void setAttributeValue(const AttributeValue& attributeValue); AttributeValue getAttributeValue(const AttributePointer& attribute) const; + void* getAttributeValue() const { return _attributeValue; } void mergeChildren(const AttributePointer& attribute); @@ -124,6 +128,7 @@ class MetavoxelVisitor { public: MetavoxelVisitor(const QVector& inputs, const QVector& outputs); + virtual ~MetavoxelVisitor(); /// Returns a reference to the list of input attributes desired. const QVector& getInputs() const { return _inputs; } @@ -142,6 +147,8 @@ protected: QVector _outputs; }; +typedef QSharedPointer MetavoxelVisitorPointer; + /// Interface for objects that guide metavoxel visitors. class MetavoxelGuide : public PolymorphicData { public: @@ -195,16 +202,14 @@ private: class MetavoxelVisitation { public: - MetavoxelData* data; MetavoxelVisitation* previous; MetavoxelVisitor& visitor; QVector inputNodes; QVector outputNodes; MetavoxelInfo info; - int childIndex; bool allInputNodesLeaves() const; - MetavoxelNode* createOutputNode(int index); + AttributeValue getInheritedOutputValue(int index) const; }; #endif /* defined(__interface__MetavoxelData__) */ diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp new file mode 100644 index 0000000000..380df4cac1 --- /dev/null +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -0,0 +1,59 @@ +// +// MetavoxelMessages.cpp +// metavoxels +// +// Created by Andrzej Kapolka on 1/24/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#include "MetavoxelData.h" +#include "MetavoxelMessages.h" + +class EditVisitor : public MetavoxelVisitor { +public: + + EditVisitor(const MetavoxelEditMessage& edit); + + virtual bool visit(MetavoxelInfo& info); + +private: + + const MetavoxelEditMessage& _edit; +}; + +EditVisitor::EditVisitor(const MetavoxelEditMessage& edit) : + MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute()), + _edit(edit) { +} + +bool EditVisitor::visit(MetavoxelInfo& info) { + // find the intersection between volume and voxel + glm::vec3 minimum = glm::max(info.minimum, _edit.region.minimum); + glm::vec3 maximum = glm::min(info.minimum + glm::vec3(info.size, info.size, info.size), _edit.region.maximum); + glm::vec3 size = maximum - minimum; + if (size.x <= 0.0f || size.y <= 0.0f || size.z <= 0.0f) { + return false; // disjoint + } + float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size); + if (volume >= 1.0f) { + info.outputValues[0] = _edit.value; + return false; // entirely contained + } + if (info.size <= _edit.granularity) { + if (volume >= 0.5f) { + info.outputValues[0] = _edit.value; + } + return false; // reached granularity limit; take best guess + } + return true; // subdivide +} + +void MetavoxelEditMessage::apply(MetavoxelData& data) const { + // expand to fit the entire edit + while (!data.getBounds().contains(region)) { + data.expand(); + } + + EditVisitor visitor(*this); + data.guide(visitor); +} diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 3951e16d22..25f4ffe422 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -9,7 +9,17 @@ #ifndef __interface__MetavoxelMessages__ #define __interface__MetavoxelMessages__ -#include "Bitstream.h" +#include "AttributeRegistry.h" +#include "MetavoxelUtil.h" + +class MetavoxelData; + +/// Requests to close the session. +class CloseSessionMessage { + STREAMABLE +}; + +DECLARE_STREAMABLE_METATYPE(CloseSessionMessage) /// A message containing the state of a client. class ClientStateMessage { @@ -29,4 +39,19 @@ class MetavoxelDeltaMessage { DECLARE_STREAMABLE_METATYPE(MetavoxelDeltaMessage) +/// A simple streamable edit. +class MetavoxelEditMessage { + STREAMABLE + +public: + + STREAM Box region; + STREAM float granularity; + STREAM OwnedAttributeValue value; + + void apply(MetavoxelData& data) const; +}; + +DECLARE_STREAMABLE_METATYPE(MetavoxelEditMessage) + #endif /* defined(__interface__MetavoxelMessages__) */ diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 29b8c57168..d337b0cc3f 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -16,7 +16,7 @@ QUuid readSessionID(const QByteArray& data, const HifiSockAddr& sender, int& headerPlusIDSize) { // get the header size - int headerSize = numBytesForPacketHeader(reinterpret_cast(data.constData())); + int headerSize = numBytesForPacketHeader(data); // read the session id const int UUID_BYTES = 16; @@ -27,3 +27,9 @@ QUuid readSessionID(const QByteArray& data, const HifiSockAddr& sender, int& hea } return QUuid::fromRfc4122(QByteArray::fromRawData(data.constData() + headerSize, UUID_BYTES)); } + +bool Box::contains(const Box& other) const { + return other.minimum.x >= minimum.x && other.maximum.x <= maximum.x && + other.minimum.y >= minimum.y && other.maximum.y <= maximum.y && + other.minimum.z >= minimum.z && other.maximum.z <= maximum.z; +} diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index f7e0edbadf..d97826fb93 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -11,6 +11,8 @@ #include +#include "Bitstream.h" + class QByteArray; class HifiSockAddr; @@ -20,4 +22,18 @@ class HifiSockAddr; /// \return the session ID, or a null ID if invalid (in which case a warning will be logged) QUuid readSessionID(const QByteArray& data, const HifiSockAddr& sender, int& headerPlusIDSize); +/// A streamable axis-aligned bounding box. +class Box { + STREAMABLE + +public: + + STREAM glm::vec3 minimum; + STREAM glm::vec3 maximum; + + bool contains(const Box& other) const; +}; + +DECLARE_STREAMABLE_METATYPE(Box) + #endif /* defined(__interface__MetavoxelUtil__) */ diff --git a/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp b/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp index e537838649..f9523851b4 100644 --- a/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp +++ b/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp @@ -39,63 +39,64 @@ void OctreeInboundPacketProcessor::resetStats() { } -void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, - unsigned char* packetData, ssize_t packetLength) { +void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, const QByteArray& packet) { bool debugProcessPacket = _myServer->wantsVerboseDebug(); if (debugProcessPacket) { - printf("OctreeInboundPacketProcessor::processPacket() packetData=%p packetLength=%ld\n", packetData, packetLength); + printf("OctreeInboundPacketProcessor::processPacket() packetData=%p packetLength=%d\n", &packet, packet.size()); } - int numBytesPacketHeader = numBytesForPacketHeader(packetData); + int numBytesPacketHeader = numBytesForPacketHeader(packet); // Ask our tree subclass if it can handle the incoming packet... - PACKET_TYPE packetType = packetData[0]; + PacketType packetType = packetTypeForPacket(packet); if (_myServer->getOctree()->handlesEditPacketType(packetType)) { PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE",debugProcessPacket); _receivedPacketCount++; SharedNodePointer senderNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr); + + const unsigned char* packetData = reinterpret_cast(packet.data()); unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader))); - uint64_t sentAt = (*((uint64_t*)(packetData + numBytesPacketHeader + sizeof(sequence)))); - uint64_t arrivedAt = usecTimestampNow(); - uint64_t transitTime = arrivedAt - sentAt; + quint64 sentAt = (*((quint64*)(packetData + numBytesPacketHeader + sizeof(sequence)))); + quint64 arrivedAt = usecTimestampNow(); + quint64 transitTime = arrivedAt - sentAt; int editsInPacket = 0; - uint64_t processTime = 0; - uint64_t lockWaitTime = 0; + quint64 processTime = 0; + quint64 lockWaitTime = 0; if (_myServer->wantsDebugReceiving()) { qDebug() << "PROCESSING THREAD: got '" << packetType << "' packet - " << _receivedPacketCount - << " command from client receivedBytes=" << packetLength + << " command from client receivedBytes=" << packet.size() << " sequence=" << sequence << " transitTime=" << transitTime << " usecs"; } int atByte = numBytesPacketHeader + sizeof(sequence) + sizeof(sentAt); unsigned char* editData = (unsigned char*)&packetData[atByte]; - while (atByte < packetLength) { - int maxSize = packetLength - atByte; + while (atByte < packet.size()) { + int maxSize = packet.size() - atByte; if (debugProcessPacket) { printf("OctreeInboundPacketProcessor::processPacket() %c " - "packetData=%p packetLength=%ld voxelData=%p atByte=%d maxSize=%d\n", - packetType, packetData, packetLength, editData, atByte, maxSize); + "packetData=%p packetLength=%d voxelData=%p atByte=%d maxSize=%d\n", + packetType, packetData, packet.size(), editData, atByte, maxSize); } - uint64_t startLock = usecTimestampNow(); + quint64 startLock = usecTimestampNow(); _myServer->getOctree()->lockForWrite(); - uint64_t startProcess = usecTimestampNow(); + quint64 startProcess = usecTimestampNow(); int editDataBytesRead = _myServer->getOctree()->processEditPacketData(packetType, - packetData, - packetLength, + reinterpret_cast(packet.data()), + packet.size(), editData, maxSize, senderNode.data()); _myServer->getOctree()->unlock(); - uint64_t endProcess = usecTimestampNow(); + quint64 endProcess = usecTimestampNow(); editsInPacket++; - uint64_t thisProcessTime = endProcess - startProcess; - uint64_t thisLockWaitTime = startProcess - startLock; + quint64 thisProcessTime = endProcess - startProcess; + quint64 thisLockWaitTime = startProcess - startLock; processTime += thisProcessTime; lockWaitTime += thisLockWaitTime; @@ -106,8 +107,8 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA if (debugProcessPacket) { printf("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %c " - "packetData=%p packetLength=%ld voxelData=%p atByte=%d\n", - packetType, packetData, packetLength, editData, atByte); + "packetData=%p packetLength=%d voxelData=%p atByte=%d\n", + packetType, packetData, packet.size(), editData, atByte); } // Make sure our Node and NodeList knows we've heard from this node. @@ -125,12 +126,12 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA } trackInboundPackets(nodeUUID, sequence, transitTime, editsInPacket, processTime, lockWaitTime); } else { - qDebug("unknown packet ignored... packetData[0]=%c", packetData[0]); + qDebug("unknown packet ignored... packetType=%d", packetType); } } -void OctreeInboundPacketProcessor::trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime, - int editsInPacket, uint64_t processTime, uint64_t lockWaitTime) { +void OctreeInboundPacketProcessor::trackInboundPackets(const QUuid& nodeUUID, int sequence, quint64 transitTime, + int editsInPacket, quint64 processTime, quint64 lockWaitTime) { _totalTransitTime += transitTime; _totalProcessTime += processTime; diff --git a/libraries/octree-server/src/OctreeInboundPacketProcessor.h b/libraries/octree-server/src/OctreeInboundPacketProcessor.h index 51214b2617..3d9b12f484 100644 --- a/libraries/octree-server/src/OctreeInboundPacketProcessor.h +++ b/libraries/octree-server/src/OctreeInboundPacketProcessor.h @@ -20,21 +20,21 @@ class SingleSenderStats { public: SingleSenderStats(); - uint64_t getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; } - uint64_t getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; } - uint64_t getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; } - uint64_t getTotalElementsProcessed() const { return _totalElementsInPacket; } - uint64_t getTotalPacketsProcessed() const { return _totalPackets; } - uint64_t getAverageProcessTimePerElement() const + quint64 getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; } + quint64 getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; } + quint64 getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; } + quint64 getTotalElementsProcessed() const { return _totalElementsInPacket; } + quint64 getTotalPacketsProcessed() const { return _totalPackets; } + quint64 getAverageProcessTimePerElement() const { return _totalElementsInPacket == 0 ? 0 : _totalProcessTime / _totalElementsInPacket; } - uint64_t getAverageLockWaitTimePerElement() const + quint64 getAverageLockWaitTimePerElement() const { return _totalElementsInPacket == 0 ? 0 : _totalLockWaitTime / _totalElementsInPacket; } - uint64_t _totalTransitTime; - uint64_t _totalProcessTime; - uint64_t _totalLockWaitTime; - uint64_t _totalElementsInPacket; - uint64_t _totalPackets; + quint64 _totalTransitTime; + quint64 _totalProcessTime; + quint64 _totalLockWaitTime; + quint64 _totalElementsInPacket; + quint64 _totalPackets; }; typedef std::map NodeToSenderStatsMap; @@ -48,14 +48,14 @@ class OctreeInboundPacketProcessor : public ReceivedPacketProcessor { public: OctreeInboundPacketProcessor(OctreeServer* myServer); - uint64_t getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; } - uint64_t getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; } - uint64_t getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; } - uint64_t getTotalElementsProcessed() const { return _totalElementsInPacket; } - uint64_t getTotalPacketsProcessed() const { return _totalPackets; } - uint64_t getAverageProcessTimePerElement() const + quint64 getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; } + quint64 getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; } + quint64 getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; } + quint64 getTotalElementsProcessed() const { return _totalElementsInPacket; } + quint64 getTotalPacketsProcessed() const { return _totalPackets; } + quint64 getAverageProcessTimePerElement() const { return _totalElementsInPacket == 0 ? 0 : _totalProcessTime / _totalElementsInPacket; } - uint64_t getAverageLockWaitTimePerElement() const + quint64 getAverageLockWaitTimePerElement() const { return _totalElementsInPacket == 0 ? 0 : _totalLockWaitTime / _totalElementsInPacket; } void resetStats(); @@ -63,20 +63,20 @@ public: NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; } protected: - virtual void processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength); + virtual void processPacket(const HifiSockAddr& senderSockAddr, const QByteArray& packet); private: - void trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime, - int voxelsInPacket, uint64_t processTime, uint64_t lockWaitTime); + void trackInboundPackets(const QUuid& nodeUUID, int sequence, quint64 transitTime, + int voxelsInPacket, quint64 processTime, quint64 lockWaitTime); OctreeServer* _myServer; int _receivedPacketCount; - uint64_t _totalTransitTime; - uint64_t _totalProcessTime; - uint64_t _totalLockWaitTime; - uint64_t _totalElementsInPacket; - uint64_t _totalPackets; + quint64 _totalTransitTime; + quint64 _totalProcessTime; + quint64 _totalLockWaitTime; + quint64 _totalElementsInPacket; + quint64 _totalPackets; NodeToSenderStatsMap _singleSenderStats; }; diff --git a/libraries/octree-server/src/OctreeQueryNode.cpp b/libraries/octree-server/src/OctreeQueryNode.cpp index f220180980..f9363d9d80 100644 --- a/libraries/octree-server/src/OctreeQueryNode.cpp +++ b/libraries/octree-server/src/OctreeQueryNode.cpp @@ -69,7 +69,7 @@ bool OctreeQueryNode::shouldSuppressDuplicatePacket() { // How long has it been since we've sent one, if we're still under our max time, then keep considering // this packet for suppression - uint64_t now = usecTimestampNow(); + quint64 now = usecTimestampNow(); long sinceFirstSuppressedPacket = now - _firstSuppressedPacket; const long MAX_TIME_BETWEEN_DUPLICATE_PACKETS = 1000 * 1000; // 1 second. @@ -110,7 +110,7 @@ void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) { } _octreePacketAvailableBytes = MAX_PACKET_SIZE; - int numBytesPacketHeader = populateTypeAndVersion(_octreePacket, getMyPacketType()); + int numBytesPacketHeader = populatePacketHeader(reinterpret_cast(_octreePacket), getMyPacketType()); _octreePacketAt = _octreePacket + numBytesPacketHeader; _octreePacketAvailableBytes -= numBytesPacketHeader; @@ -233,7 +233,7 @@ void OctreeQueryNode::updateLastKnownViewFrustum() { } // save that we know the view has been sent. - uint64_t now = usecTimestampNow(); + quint64 now = usecTimestampNow(); setLastTimeBagEmpty(now); // is this what we want? poor names } diff --git a/libraries/octree-server/src/OctreeQueryNode.h b/libraries/octree-server/src/OctreeQueryNode.h index 6fc3c1a07b..6d4dee300a 100644 --- a/libraries/octree-server/src/OctreeQueryNode.h +++ b/libraries/octree-server/src/OctreeQueryNode.h @@ -27,7 +27,7 @@ public: OctreeQueryNode(); virtual ~OctreeQueryNode(); - virtual PACKET_TYPE getMyPacketType() const = 0; + virtual PacketType getMyPacketType() const = 0; void resetOctreePacket(bool lastWasSurpressed = false); // resets octree packet to after "V" header @@ -67,8 +67,8 @@ public: bool moveShouldDump() const; - uint64_t getLastTimeBagEmpty() const { return _lastTimeBagEmpty; } - void setLastTimeBagEmpty(uint64_t lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; } + quint64 getLastTimeBagEmpty() const { return _lastTimeBagEmpty; } + void setLastTimeBagEmpty(quint64 lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; } bool getCurrentPacketIsColor() const { return _currentPacketIsColor; } bool getCurrentPacketIsCompressed() const { return _currentPacketIsCompressed; } @@ -98,13 +98,13 @@ private: unsigned char* _lastOctreePacket; int _lastOctreePacketLength; int _duplicatePacketCount; - uint64_t _firstSuppressedPacket; + quint64 _firstSuppressedPacket; int _maxSearchLevel; int _maxLevelReachedInLastSearch; ViewFrustum _currentViewFrustum; ViewFrustum _lastKnownViewFrustum; - uint64_t _lastTimeBagEmpty; + quint64 _lastTimeBagEmpty; bool _viewFrustumChanging; bool _viewFrustumJustStoppedChanging; bool _currentPacketIsColor; diff --git a/libraries/octree-server/src/OctreeSendThread.cpp b/libraries/octree-server/src/OctreeSendThread.cpp index a12a633e58..26df0ffb0b 100644 --- a/libraries/octree-server/src/OctreeSendThread.cpp +++ b/libraries/octree-server/src/OctreeSendThread.cpp @@ -14,8 +14,8 @@ #include "OctreeServer.h" #include "OctreeServerConsts.h" -uint64_t startSceneSleepTime = 0; -uint64_t endSceneSleepTime = 0; +quint64 startSceneSleepTime = 0; +quint64 endSceneSleepTime = 0; OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer) : _nodeUUID(nodeUUID), @@ -25,7 +25,7 @@ OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer } bool OctreeSendThread::process() { - uint64_t start = usecTimestampNow(); + quint64 start = usecTimestampNow(); bool gotLock = false; // don't do any send processing until the initial load of the octree is complete... @@ -79,16 +79,16 @@ bool OctreeSendThread::process() { return isStillRunning(); // keep running till they terminate us } -uint64_t OctreeSendThread::_usleepTime = 0; -uint64_t OctreeSendThread::_usleepCalls = 0; +quint64 OctreeSendThread::_usleepTime = 0; +quint64 OctreeSendThread::_usleepCalls = 0; -uint64_t OctreeSendThread::_totalBytes = 0; -uint64_t OctreeSendThread::_totalWastedBytes = 0; -uint64_t OctreeSendThread::_totalPackets = 0; +quint64 OctreeSendThread::_totalBytes = 0; +quint64 OctreeSendThread::_totalWastedBytes = 0; +quint64 OctreeSendThread::_totalPackets = 0; int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) { bool debug = _myServer->wantsDebugSending(); - uint64_t now = usecTimestampNow(); + quint64 now = usecTimestampNow(); bool packetSent = false; // did we send a packet? int packetsSent = 0; @@ -101,7 +101,7 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in } const unsigned char* messageData = nodeData->getPacket(); - int numBytesPacketHeader = numBytesForPacketHeader(messageData); + int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast(messageData)); const unsigned char* dataAt = messageData + numBytesPacketHeader; dataAt += sizeof(OCTREE_PACKET_FLAGS); OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt); @@ -283,7 +283,7 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b // If the current view frustum has changed OR we have nothing to send, then search against // the current view frustum for things to send. if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { - uint64_t now = usecTimestampNow(); + quint64 now = usecTimestampNow(); if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { qDebug("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...", debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); @@ -310,7 +310,7 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b if (!viewFrustumChanged && !nodeData->getWantDelta()) { // only set our last sent time if we weren't resetting due to frustum change - uint64_t now = usecTimestampNow(); + quint64 now = usecTimestampNow(); nodeData->setLastTimeBagEmpty(now); } @@ -374,9 +374,9 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b // If we have something in our nodeBag, then turn them into packets and send them out... if (!nodeData->nodeBag.isEmpty()) { int bytesWritten = 0; - uint64_t start = usecTimestampNow(); - uint64_t startCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000; - uint64_t startCompressCalls = OctreePacketData::getCompressContentCalls(); + quint64 start = usecTimestampNow(); + quint64 startCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000; + quint64 startCompressCalls = OctreePacketData::getCompressContentCalls(); int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND)); int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); @@ -533,13 +533,13 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b } - uint64_t end = usecTimestampNow(); + quint64 end = usecTimestampNow(); int elapsedmsec = (end - start)/1000; - uint64_t endCompressCalls = OctreePacketData::getCompressContentCalls(); + quint64 endCompressCalls = OctreePacketData::getCompressContentCalls(); int elapsedCompressCalls = endCompressCalls - startCompressCalls; - uint64_t endCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000; + quint64 endCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000; int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs; diff --git a/libraries/octree-server/src/OctreeSendThread.h b/libraries/octree-server/src/OctreeSendThread.h index c0936371f2..6f60f0eb41 100644 --- a/libraries/octree-server/src/OctreeSendThread.h +++ b/libraries/octree-server/src/OctreeSendThread.h @@ -21,12 +21,12 @@ class OctreeSendThread : public GenericThread { public: OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer); - static uint64_t _totalBytes; - static uint64_t _totalWastedBytes; - static uint64_t _totalPackets; + static quint64 _totalBytes; + static quint64 _totalWastedBytes; + static quint64 _totalPackets; - static uint64_t _usleepTime; - static uint64_t _usleepCalls; + static quint64 _usleepTime; + static quint64 _usleepCalls; protected: /// Implements generic processing behavior for this thread. diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp index 438f71dd16..6e58a0a987 100644 --- a/libraries/octree-server/src/OctreeServer.cpp +++ b/libraries/octree-server/src/OctreeServer.cpp @@ -28,8 +28,8 @@ void OctreeServer::attachQueryNodeToNode(Node* newNode) { } } -OctreeServer::OctreeServer(const unsigned char* dataBuffer, int numBytes) : - ThreadedAssignment(dataBuffer, numBytes), +OctreeServer::OctreeServer(const QByteArray& packet) : + ThreadedAssignment(packet), _argc(0), _argv(NULL), _parsedArgV(NULL), @@ -120,7 +120,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString& } if (showStats) { - uint64_t checkSum; + quint64 checkSum; // return a 200 QString statsString("\r\n
\r\n");
         statsString += QString("Your %1 Server is running... [RELOAD]\r\n").arg(getMyServerName());
@@ -140,9 +140,9 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
 
         statsString += "\r\n";
 
-        uint64_t now  = usecTimestampNow();
+        quint64 now  = usecTimestampNow();
         const int USECS_PER_MSEC = 1000;
-        uint64_t msecsElapsed = (now - _startedUSecs) / USECS_PER_MSEC;
+        quint64 msecsElapsed = (now - _startedUSecs) / USECS_PER_MSEC;
         const int MSECS_PER_SEC = 1000;
         const int SECS_PER_MIN = 60;
         const int MIN_PER_HOUR = 60;
@@ -175,7 +175,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
 
             statsString += "\r\n";
 
-            uint64_t msecsElapsed = getLoadElapsedTime() / USECS_PER_MSEC;;
+            quint64 msecsElapsed = getLoadElapsedTime() / USECS_PER_MSEC;;
             float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
             int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
             int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
@@ -226,12 +226,12 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
 
         // display outbound packet stats
         statsString += QString("%1 Outbound Packet Statistics...\r\n").arg(getMyServerName());
-        uint64_t totalOutboundPackets = OctreeSendThread::_totalPackets;
-        uint64_t totalOutboundBytes = OctreeSendThread::_totalBytes;
-        uint64_t totalWastedBytes = OctreeSendThread::_totalWastedBytes;
-        uint64_t totalBytesOfOctalCodes = OctreePacketData::getTotalBytesOfOctalCodes();
-        uint64_t totalBytesOfBitMasks = OctreePacketData::getTotalBytesOfBitMasks();
-        uint64_t totalBytesOfColor = OctreePacketData::getTotalBytesOfColor();
+        quint64 totalOutboundPackets = OctreeSendThread::_totalPackets;
+        quint64 totalOutboundBytes = OctreeSendThread::_totalBytes;
+        quint64 totalWastedBytes = OctreeSendThread::_totalWastedBytes;
+        quint64 totalBytesOfOctalCodes = OctreePacketData::getTotalBytesOfOctalCodes();
+        quint64 totalBytesOfBitMasks = OctreePacketData::getTotalBytesOfBitMasks();
+        quint64 totalBytesOfColor = OctreePacketData::getTotalBytesOfColor();
 
         const int COLUMN_WIDTH = 10;
         statsString += QString("           Total Outbound Packets: %1 packets\r\n")
@@ -256,13 +256,13 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
         // display inbound packet stats
         statsString += QString().sprintf("%s Edit Statistics... [RESET]\r\n",
                                          getMyServerName());
-        uint64_t averageTransitTimePerPacket = _octreeInboundPacketProcessor->getAverageTransitTimePerPacket();
-        uint64_t averageProcessTimePerPacket = _octreeInboundPacketProcessor->getAverageProcessTimePerPacket();
-        uint64_t averageLockWaitTimePerPacket = _octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket();
-        uint64_t averageProcessTimePerElement = _octreeInboundPacketProcessor->getAverageProcessTimePerElement();
-        uint64_t averageLockWaitTimePerElement = _octreeInboundPacketProcessor->getAverageLockWaitTimePerElement();
-        uint64_t totalElementsProcessed = _octreeInboundPacketProcessor->getTotalElementsProcessed();
-        uint64_t totalPacketsProcessed = _octreeInboundPacketProcessor->getTotalPacketsProcessed();
+        quint64 averageTransitTimePerPacket = _octreeInboundPacketProcessor->getAverageTransitTimePerPacket();
+        quint64 averageProcessTimePerPacket = _octreeInboundPacketProcessor->getAverageProcessTimePerPacket();
+        quint64 averageLockWaitTimePerPacket = _octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket();
+        quint64 averageProcessTimePerElement = _octreeInboundPacketProcessor->getAverageProcessTimePerElement();
+        quint64 averageLockWaitTimePerElement = _octreeInboundPacketProcessor->getAverageLockWaitTimePerElement();
+        quint64 totalElementsProcessed = _octreeInboundPacketProcessor->getTotalElementsProcessed();
+        quint64 totalPacketsProcessed = _octreeInboundPacketProcessor->getTotalPacketsProcessed();
 
         float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed;
 
@@ -432,8 +432,8 @@ void OctreeServer::setArguments(int argc, char** argv) {
 
 void OctreeServer::parsePayload() {
 
-    if (getNumPayloadBytes() > 0) {
-        QString config((const char*) _payload);
+    if (getPayload().size() > 0) {
+        QString config(_payload);
 
         // Now, parse the config
         QStringList configList = config.split(" ");
@@ -461,26 +461,23 @@ void OctreeServer::parsePayload() {
 void OctreeServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
     NodeList* nodeList = NodeList::getInstance();
 
-    PACKET_TYPE packetType = dataByteArray[0];
+    PacketType packetType = packetTypeForPacket(dataByteArray);
 
     if (packetType == getMyQueryMessageType()) {
         bool debug = false;
         if (debug) {
-            qDebug() << "Got PACKET_TYPE_VOXEL_QUERY at" << usecTimestampNow();
+            qDebug() << "Got PacketType_VOXEL_QUERY at" << usecTimestampNow();
         }
-
-        int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) dataByteArray.data());
-
-        // If we got a PACKET_TYPE_VOXEL_QUERY, then we're talking to an NODE_TYPE_AVATAR, and we
+       
+        // If we got a PacketType_VOXEL_QUERY, then we're talking to an NodeType_t_AVATAR, and we
         // need to make sure we have it in our nodeList.
-        QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesPacketHeader,
-                                                              NUM_BYTES_RFC4122_UUID));
+        QUuid nodeUUID;
+        deconstructPacketHeader(dataByteArray, nodeUUID);
 
         SharedNodePointer node = nodeList->nodeWithUUID(nodeUUID);
 
         if (node) {
-            nodeList->updateNodeWithData(node.data(), senderSockAddr, (unsigned char *) dataByteArray.data(),
-                                         dataByteArray.size());
+            nodeList->updateNodeWithData(node.data(), senderSockAddr, dataByteArray);
             if (!node->getActiveSocket()) {
                 // we don't have an active socket for this node, but they're talking to us
                 // this means they've heard from us and can reply, let's assume public is active
@@ -491,16 +488,13 @@ void OctreeServer::processDatagram(const QByteArray& dataByteArray, const HifiSo
                 nodeData->initializeOctreeSendThread(this, nodeUUID);
             }
         }
-    } else if (packetType == PACKET_TYPE_JURISDICTION_REQUEST) {
-        _jurisdictionSender->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(),
-                                                 dataByteArray.size());
+    } else if (packetType == PacketTypeJurisdictionRequest) {
+        _jurisdictionSender->queueReceivedPacket(senderSockAddr, dataByteArray);
     } else if (_octreeInboundPacketProcessor && getOctree()->handlesEditPacketType(packetType)) {
-       _octreeInboundPacketProcessor->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(),
-                                                                    dataByteArray.size());
+       _octreeInboundPacketProcessor->queueReceivedPacket(senderSockAddr, dataByteArray);
    } else {
        // let processNodeData handle it.
-       NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(),
-                                                dataByteArray.size());
+       NodeList::getInstance()->processNodeData(senderSockAddr, dataByteArray);
     }
 }
 
@@ -512,7 +506,7 @@ void OctreeServer::run() {
     Logging::setTargetName(getMyLoggingServerTargetName());
 
     // Now would be a good time to parse our arguments, if we got them as assignment
-    if (getNumPayloadBytes() > 0) {
+    if (getPayload().size() > 0) {
         parsePayload();
     }
 
@@ -558,7 +552,7 @@ void OctreeServer::run() {
     nodeList->setOwnerType(getMyNodeType());
 
     // we need to ask the DS about agents so we can ping/reply with them
-    nodeList->addNodeTypeToInterestSet(NODE_TYPE_AGENT);
+    nodeList->addNodeTypeToInterestSet(NodeType::Agent);
 
     setvbuf(stdout, NULL, _IOLBF, 0);
 
diff --git a/libraries/octree-server/src/OctreeServer.h b/libraries/octree-server/src/OctreeServer.h
index 86ad134df0..48a74a5e9e 100644
--- a/libraries/octree-server/src/OctreeServer.h
+++ b/libraries/octree-server/src/OctreeServer.h
@@ -28,7 +28,7 @@
 class OctreeServer : public ThreadedAssignment, public HTTPRequestHandler {
     Q_OBJECT
 public:
-    OctreeServer(const unsigned char* dataBuffer, int numBytes);
+    OctreeServer(const QByteArray& packet);
     ~OctreeServer();
 
     /// allows setting of run arguments
@@ -45,13 +45,13 @@ public:
 
     bool isInitialLoadComplete() const { return (_persistThread) ? _persistThread->isInitialLoadComplete() : true; }
     bool isPersistEnabled() const { return (_persistThread) ? true : false; }
-    uint64_t getLoadElapsedTime() const { return (_persistThread) ? _persistThread->getLoadElapsedTime() : 0; }
+    quint64 getLoadElapsedTime() const { return (_persistThread) ? _persistThread->getLoadElapsedTime() : 0; }
 
     // Subclasses must implement these methods
     virtual OctreeQueryNode* createOctreeQueryNode() = 0;
     virtual Octree* createTree() = 0;
     virtual unsigned char getMyNodeType() const = 0;
-    virtual PACKET_TYPE getMyQueryMessageType() const = 0;
+    virtual PacketType getMyQueryMessageType() const = 0;
     virtual const char* getMyServerName() const = 0;
     virtual const char* getMyLoggingServerTargetName() const = 0;
     virtual const char* getMyDefaultPersistFilename() const = 0;
@@ -94,7 +94,7 @@ protected:
     static OctreeServer* _instance;
 
     time_t _started;
-    uint64_t _startedUSecs;
+    quint64 _startedUSecs;
 };
 
 #endif // __octree_server__OctreeServer__
diff --git a/libraries/octree/src/AABox.cpp b/libraries/octree/src/AABox.cpp
index 670e2b8a6b..1313111765 100644
--- a/libraries/octree/src/AABox.cpp
+++ b/libraries/octree/src/AABox.cpp
@@ -117,6 +117,13 @@ bool AABox::contains(const AABox& otherBox) const {
     return true;
 }
 
+bool AABox::touches(const AABox& otherBox) const {
+    glm::vec3 relativeCenter = _corner - otherBox._corner + (glm::vec3(_scale - otherBox._scale) * 0.5f);
+    float totalHalfScale = 0.5f * (_scale + otherBox._scale);
+    return fabs(relativeCenter.x) <= totalHalfScale && 
+        fabs(relativeCenter.y) <= totalHalfScale && 
+        fabs(relativeCenter.z) <= totalHalfScale;
+}
 
 // determines whether a value is within the expanded extents
 static bool isWithinExpanded(float value, float corner, float size, float expansion) {
diff --git a/libraries/octree/src/AABox.h b/libraries/octree/src/AABox.h
index aec0fff450..731b82be4c 100644
--- a/libraries/octree/src/AABox.h
+++ b/libraries/octree/src/AABox.h
@@ -61,6 +61,7 @@ public:
 
     bool contains(const glm::vec3& point) const;
     bool contains(const AABox& otherBox) const;
+    bool touches(const AABox& otherBox) const;
     bool expandedContains(const glm::vec3& point, float expansion) const;
     bool expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const;
     bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp
index e761a15e76..d2c24021ac 100644
--- a/libraries/octree/src/JurisdictionListener.cpp
+++ b/libraries/octree/src/JurisdictionListener.cpp
@@ -15,14 +15,14 @@
 #include 
 #include "JurisdictionListener.h"
 
-JurisdictionListener::JurisdictionListener(NODE_TYPE type) :
+JurisdictionListener::JurisdictionListener(NodeType_t type) :
     _packetSender(JurisdictionListener::DEFAULT_PACKETS_PER_SECOND)
 {
     _nodeType = type;
     ReceivedPacketProcessor::_dontSleep = true; // we handle sleeping so this class doesn't need to
     
     connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &JurisdictionListener::nodeKilled);
-    //qDebug("JurisdictionListener::JurisdictionListener(NODE_TYPE type=%c)", type);
+    //qDebug("JurisdictionListener::JurisdictionListener(NodeType_t type=%c)", type);
     
     // tell our NodeList we want to hear about nodes with our node type
     NodeList::getInstance()->addNodeTypeToInterestSet(type);
@@ -39,16 +39,15 @@ bool JurisdictionListener::queueJurisdictionRequest() {
 
     static unsigned char buffer[MAX_PACKET_SIZE];
     unsigned char* bufferOut = &buffer[0];
-    ssize_t sizeOut = populateTypeAndVersion(bufferOut, PACKET_TYPE_JURISDICTION_REQUEST);
+    ssize_t sizeOut = populatePacketHeader(reinterpret_cast(bufferOut), PacketTypeJurisdictionRequest);
     int nodeCount = 0;
 
     NodeList* nodeList = NodeList::getInstance();
     
     foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
-        if (nodeList->getNodeActiveSocketOrPing(node.data()) &&
-            node->getType() == getNodeType()) {
+        if (nodeList->getNodeActiveSocketOrPing(node.data()) && node->getType() == getNodeType()) {
             const HifiSockAddr* nodeAddress = node->getActiveSocket();
-            _packetSender.queuePacketForSending(*nodeAddress, bufferOut, sizeOut);
+            _packetSender.queuePacketForSending(*nodeAddress, QByteArray(reinterpret_cast(bufferOut), sizeOut));
             nodeCount++;
         }
     }
@@ -63,14 +62,14 @@ bool JurisdictionListener::queueJurisdictionRequest() {
     return isStillRunning();
 }
 
-void JurisdictionListener::processPacket(const HifiSockAddr& senderAddress, unsigned char*  packetData, ssize_t packetLength) {
+void JurisdictionListener::processPacket(const HifiSockAddr& senderAddress, const QByteArray& packet) {
     //qDebug() << "JurisdictionListener::processPacket()";
-    if (packetData[0] == PACKET_TYPE_JURISDICTION) {
+    if (packetTypeForPacket(packet) == PacketTypeJurisdictionRequest) {
         SharedNodePointer node = NodeList::getInstance()->nodeWithAddress(senderAddress);
         if (node) {
             QUuid nodeUUID = node->getUUID();
             JurisdictionMap map;
-            map.unpackFromMessage(packetData, packetLength);
+            map.unpackFromMessage(reinterpret_cast(packet.data()), packet.size());
             _jurisdictions[nodeUUID] = map;
         }
     }
diff --git a/libraries/octree/src/JurisdictionListener.h b/libraries/octree/src/JurisdictionListener.h
index fea76f57eb..712749aae7 100644
--- a/libraries/octree/src/JurisdictionListener.h
+++ b/libraries/octree/src/JurisdictionListener.h
@@ -18,8 +18,8 @@
 
 #include "JurisdictionMap.h"
 
-/// Sends out PACKET_TYPE_JURISDICTION_REQUEST packets to all voxel servers and then listens for and processes
-/// the PACKET_TYPE_JURISDICTION packets it receives in order to maintain an accurate state of all jurisidictions
+/// Sends out PacketType_JURISDICTION_REQUEST packets to all voxel servers and then listens for and processes
+/// the PacketType_JURISDICTION packets it receives in order to maintain an accurate state of all jurisidictions
 /// within the domain. As with other ReceivedPacketProcessor classes the user is responsible for reading inbound packets
 /// and adding them to the processing queue by calling queueReceivedPacket()
 class JurisdictionListener : public ReceivedPacketProcessor {
@@ -28,32 +28,32 @@ public:
     static const int DEFAULT_PACKETS_PER_SECOND = 1;
     static const int NO_SERVER_CHECK_RATE = 60; // if no servers yet detected, keep checking at 60fps
 
-    JurisdictionListener(NODE_TYPE type = NODE_TYPE_VOXEL_SERVER);
+    JurisdictionListener(NodeType_t type = NodeType::VoxelServer);
     
     virtual bool process();
 
     NodeToJurisdictionMap* getJurisdictions() { return &_jurisdictions; };
 
 
-    NODE_TYPE getNodeType() const { return _nodeType; }
-    void setNodeType(NODE_TYPE type) { _nodeType = type; }
+    NodeType_t getNodeType() const { return _nodeType; }
+    void setNodeType(NodeType_t type) { _nodeType = type; }
 
 public slots:
     /// Called by NodeList to inform us that a node has been killed.
     void nodeKilled(SharedNodePointer node);
     
 protected:
-    /// Callback for processing of received packets. Will process any queued PACKET_TYPE_JURISDICTION and update the
+    /// Callback for processing of received packets. Will process any queued PacketType_JURISDICTION and update the
     /// jurisdiction map member variable
     /// \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(const HifiSockAddr& senderAddress, unsigned char*  packetData, ssize_t packetLength);
+    virtual void processPacket(const HifiSockAddr& senderAddress, const QByteArray& packet);
 
 private:
     NodeToJurisdictionMap _jurisdictions;
-    NODE_TYPE _nodeType;
+    NodeType_t _nodeType;
 
     bool queueJurisdictionRequest();
 
diff --git a/libraries/octree/src/JurisdictionMap.cpp b/libraries/octree/src/JurisdictionMap.cpp
index bf64e19959..6dc5a22e73 100644
--- a/libraries/octree/src/JurisdictionMap.cpp
+++ b/libraries/octree/src/JurisdictionMap.cpp
@@ -92,7 +92,7 @@ void JurisdictionMap::clear() {
     _endNodes.clear();
 }
 
-JurisdictionMap::JurisdictionMap(NODE_TYPE type) : _rootOctalCode(NULL) {
+JurisdictionMap::JurisdictionMap(NodeType_t type) : _rootOctalCode(NULL) {
     _nodeType = type;
     unsigned char* rootCode = new unsigned char[1];
     *rootCode = 0;
@@ -262,10 +262,10 @@ bool JurisdictionMap::writeToFile(const char* filename) {
     return true;
 }
 
-int JurisdictionMap::packEmptyJurisdictionIntoMessage(NODE_TYPE type, unsigned char* destinationBuffer, int availableBytes) {
+int JurisdictionMap::packEmptyJurisdictionIntoMessage(NodeType_t type, unsigned char* destinationBuffer, int availableBytes) {
     unsigned char* bufferStart = destinationBuffer;
     
-    int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_JURISDICTION);
+    int headerLength = populatePacketHeader(reinterpret_cast(destinationBuffer), PacketTypeJurisdiction);
     destinationBuffer += headerLength;
 
     // Pack the Node Type in first byte
@@ -283,11 +283,11 @@ int JurisdictionMap::packEmptyJurisdictionIntoMessage(NODE_TYPE type, unsigned c
 int JurisdictionMap::packIntoMessage(unsigned char* destinationBuffer, int availableBytes) {
     unsigned char* bufferStart = destinationBuffer;
     
-    int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_JURISDICTION);
+    int headerLength = populatePacketHeader(reinterpret_cast(destinationBuffer), PacketTypeJurisdiction);
     destinationBuffer += headerLength;
 
     // Pack the Node Type in first byte
-    NODE_TYPE type = getNodeType();
+    NodeType_t type = getNodeType();
     memcpy(destinationBuffer, &type, sizeof(type));
     destinationBuffer += sizeof(type);
 
@@ -324,12 +324,12 @@ int JurisdictionMap::packIntoMessage(unsigned char* destinationBuffer, int avail
     return destinationBuffer - bufferStart; // includes header!
 }
 
-int JurisdictionMap::unpackFromMessage(unsigned char* sourceBuffer, int availableBytes) {
+int JurisdictionMap::unpackFromMessage(const unsigned char* sourceBuffer, int availableBytes) {
     clear();
-    unsigned char* startPosition = sourceBuffer;
+    const unsigned char* startPosition = sourceBuffer;
 
     // increment to push past the packet header
-    int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer);
+    int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast(sourceBuffer));
     sourceBuffer += numBytesPacketHeader;
     int remainingBytes = availableBytes - numBytesPacketHeader;
     
diff --git a/libraries/octree/src/JurisdictionMap.h b/libraries/octree/src/JurisdictionMap.h
index 44c1cfa3cf..219429712e 100644
--- a/libraries/octree/src/JurisdictionMap.h
+++ b/libraries/octree/src/JurisdictionMap.h
@@ -16,7 +16,7 @@
 #include 
 #include 
 
-#include 
+#include 
 
 class JurisdictionMap {
 public:
@@ -27,7 +27,7 @@ public:
     };
     
     // standard constructors
-    JurisdictionMap(NODE_TYPE type = NODE_TYPE_VOXEL_SERVER); // default constructor
+    JurisdictionMap(NodeType_t type = NodeType::VoxelServer); // default constructor
     JurisdictionMap(const JurisdictionMap& other); // copy constructor
 
     // standard assignment
@@ -56,16 +56,16 @@ public:
 
     void copyContents(unsigned char* rootCodeIn, const std::vector& endNodesIn);
 
-    int unpackFromMessage(unsigned char* sourceBuffer, int availableBytes);
+    int unpackFromMessage(const unsigned char* sourceBuffer, int availableBytes);
     int packIntoMessage(unsigned char* destinationBuffer, int availableBytes);
     
     /// Available to pack an empty or unknown jurisdiction into a network packet, used when no JurisdictionMap is available
-    static int packEmptyJurisdictionIntoMessage(NODE_TYPE type, unsigned char* destinationBuffer, int availableBytes);
+    static int packEmptyJurisdictionIntoMessage(NodeType_t type, unsigned char* destinationBuffer, int availableBytes);
 
     void displayDebugDetails() const;
     
-    NODE_TYPE getNodeType() const { return _nodeType; }
-    void setNodeType(NODE_TYPE type) { _nodeType = type; }
+    NodeType_t getNodeType() const { return _nodeType; }
+    void setNodeType(NodeType_t type) { _nodeType = type; }
     
 private:
     void copyContents(const JurisdictionMap& other); // use assignment instead
@@ -74,7 +74,7 @@ private:
 
     unsigned char* _rootOctalCode;
     std::vector _endNodes;
-    NODE_TYPE _nodeType;
+    NodeType_t _nodeType;
 };
 
 /// Map between node IDs and their reported JurisdictionMap. Typically used by classes that need to know which nodes are 
diff --git a/libraries/octree/src/JurisdictionSender.cpp b/libraries/octree/src/JurisdictionSender.cpp
index e1bbf78506..854496e40f 100644
--- a/libraries/octree/src/JurisdictionSender.cpp
+++ b/libraries/octree/src/JurisdictionSender.cpp
@@ -16,7 +16,7 @@
 #include "JurisdictionSender.h"
 
 
-JurisdictionSender::JurisdictionSender(JurisdictionMap* map, NODE_TYPE type) :
+JurisdictionSender::JurisdictionSender(JurisdictionMap* map, NodeType_t type) :
     ReceivedPacketProcessor(),
     _jurisdictionMap(map),
     _packetSender(JurisdictionSender::DEFAULT_PACKETS_PER_SECOND)
@@ -28,11 +28,11 @@ JurisdictionSender::~JurisdictionSender() {
 }
 
 
-void JurisdictionSender::processPacket(const HifiSockAddr& senderAddress, unsigned char*  packetData, ssize_t packetLength) {
-    if (packetData[0] == PACKET_TYPE_JURISDICTION_REQUEST) {
-        SharedNodePointer node = NodeList::getInstance()->nodeWithAddress(senderAddress);
-        if (node) {
-            QUuid nodeUUID = node->getUUID();
+void JurisdictionSender::processPacket(const HifiSockAddr& senderAddress, const QByteArray& packet) {
+    if (packetTypeForPacket(packet) == PacketTypeJurisdictionRequest) {
+        QUuid nodeUUID;
+        deconstructPacketHeader(packet, nodeUUID);
+        if (!nodeUUID.isNull()) {
             lockRequestingNodes();
             _nodesRequestingJurisdictions.push(nodeUUID);
             unlockRequestingNodes();
@@ -64,9 +64,9 @@ bool JurisdictionSender::process() {
             _nodesRequestingJurisdictions.pop();
             SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(nodeUUID);
 
-            if (node->getActiveSocket() != NULL) {
+            if (node && node->getActiveSocket() != NULL) {
                 const HifiSockAddr* nodeAddress = node->getActiveSocket();
-                _packetSender.queuePacketForSending(*nodeAddress, bufferOut, sizeOut);
+                _packetSender.queuePacketForSending(*nodeAddress, QByteArray(reinterpret_cast(bufferOut), sizeOut));
                 nodeCount++;
             }
         }
diff --git a/libraries/octree/src/JurisdictionSender.h b/libraries/octree/src/JurisdictionSender.h
index e70070b21d..19ce727a57 100644
--- a/libraries/octree/src/JurisdictionSender.h
+++ b/libraries/octree/src/JurisdictionSender.h
@@ -18,7 +18,7 @@
 #include 
 #include "JurisdictionMap.h"
 
-/// Will process PACKET_TYPE_JURISDICTION_REQUEST packets and send out PACKET_TYPE_JURISDICTION packets
+/// Will process PacketType_JURISDICTION_REQUEST packets and send out PacketType_JURISDICTION packets
 /// to requesting parties. As with other ReceivedPacketProcessor classes the user is responsible for reading inbound packets
 /// and adding them to the processing queue by calling queueReceivedPacket()
 class JurisdictionSender : public ReceivedPacketProcessor {
@@ -26,18 +26,18 @@ class JurisdictionSender : public ReceivedPacketProcessor {
 public:
     static const int DEFAULT_PACKETS_PER_SECOND = 1;
 
-    JurisdictionSender(JurisdictionMap* map, NODE_TYPE type = NODE_TYPE_VOXEL_SERVER);
+    JurisdictionSender(JurisdictionMap* map, NodeType_t type = NodeType::VoxelServer);
     ~JurisdictionSender();
 
     void setJurisdiction(JurisdictionMap* map) { _jurisdictionMap = map; }
 
     virtual bool process();
 
-    NODE_TYPE getNodeType() const { return _nodeType; }
-    void setNodeType(NODE_TYPE type) { _nodeType = type; }
+    NodeType_t getNodeType() const { return _nodeType; }
+    void setNodeType(NodeType_t type) { _nodeType = type; }
 
 protected:
-    virtual void processPacket(const HifiSockAddr& senderAddress, unsigned char*  packetData, ssize_t packetLength);
+    virtual void processPacket(const HifiSockAddr& senderAddress, const QByteArray& packet);
 
     /// Locks all the resources of the thread.
     void lockRequestingNodes() { _requestingNodeMutex.lock(); }
@@ -50,7 +50,7 @@ private:
     QMutex _requestingNodeMutex;
     JurisdictionMap* _jurisdictionMap;
     std::queue _nodesRequestingJurisdictions;
-    NODE_TYPE _nodeType;
+    NodeType_t _nodeType;
     
     PacketSender _packetSender;
 };
diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp
index 5d25f0c164..93ce5c9174 100644
--- a/libraries/octree/src/Octree.cpp
+++ b/libraries/octree/src/Octree.cpp
@@ -43,6 +43,7 @@ Octree::Octree(bool shouldReaverage) :
     _shouldReaverage(shouldReaverage),
     _stopImport(false) {
     _rootNode = NULL;
+    _isViewing = false;
 }
 
 Octree::~Octree() {
@@ -443,9 +444,9 @@ void Octree::eraseAllOctreeElements() {
 void Octree::processRemoveOctreeElementsBitstream(const unsigned char* bitstream, int bufferSizeBytes) {
     //unsigned short int itemNumber = (*((unsigned short int*)&bitstream[sizeof(PACKET_HEADER)]));
 
-    int numBytesPacketHeader = numBytesForPacketHeader(bitstream);
+    int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast(bitstream));
     unsigned short int sequence = (*((unsigned short int*)(bitstream + numBytesPacketHeader)));
-    uint64_t sentAt = (*((uint64_t*)(bitstream + numBytesPacketHeader + sizeof(sequence))));
+    quint64 sentAt = (*((quint64*)(bitstream + numBytesPacketHeader + sizeof(sequence))));
 
     int atByte = numBytesPacketHeader + sizeof(sequence) + sizeof(sentAt);
 
@@ -1322,13 +1323,16 @@ bool Octree::readFromSVOFile(const char* fileName) {
         // before reading the file, check to see if this version of the Octree supports file versions
         if (getWantSVOfileVersions()) {
             // if so, read the first byte of the file and see if it matches the expected version code
-            PACKET_TYPE expectedType = expectedDataPacketType();
-            PACKET_TYPE gotType = *dataAt;
+            PacketType expectedType = expectedDataPacketType();
+            
+            PacketType gotType;
+            memcpy(&gotType, dataAt, sizeof(gotType));
+            
             if (gotType == expectedType) {
                 dataAt += sizeof(expectedType);
                 dataLength -= sizeof(expectedType);
-                PACKET_VERSION expectedVersion = versionForPacketType(expectedType);
-                PACKET_VERSION gotVersion = *dataAt;
+                PacketVersion expectedVersion = versionForPacketType(expectedType);
+                PacketVersion gotVersion = *dataAt;
                 if (gotVersion == expectedVersion) {
                     dataAt += sizeof(expectedVersion);
                     dataLength -= sizeof(expectedVersion);
@@ -1365,9 +1369,9 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* node) {
         // before reading the file, check to see if this version of the Octree supports file versions
         if (getWantSVOfileVersions()) {
             // if so, read the first byte of the file and see if it matches the expected version code
-            PACKET_TYPE expectedType = expectedDataPacketType();
-            PACKET_VERSION expectedVersion = versionForPacketType(expectedType);
-            file.write(&expectedType, sizeof(expectedType));
+            PacketType expectedType = expectedDataPacketType();
+            PacketVersion expectedVersion = versionForPacketType(expectedType);
+            file.write(reinterpret_cast(&expectedType), sizeof(expectedType));
             file.write(&expectedVersion, sizeof(expectedVersion));
         }
 
diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h
index 959c9272cf..9b8921208b 100644
--- a/libraries/octree/src/Octree.h
+++ b/libraries/octree/src/Octree.h
@@ -47,7 +47,7 @@ const bool WANT_OCCLUSION_CULLING = true;
 const int DONT_CHOP              = 0;
 const int NO_BOUNDARY_ADJUST     = 0;
 const int LOW_RES_MOVING_ADJUST  = 1;
-const uint64_t IGNORE_LAST_SENT  = 0;
+const quint64 IGNORE_LAST_SENT  = 0;
 
 #define IGNORE_SCENE_STATS       NULL
 #define IGNORE_VIEW_FRUSTUM      NULL
@@ -67,7 +67,7 @@ public:
     bool wantOcclusionCulling;
     int boundaryLevelAdjust;
     float octreeElementSizeScale;
-    uint64_t lastViewFrustumSent;
+    quint64 lastViewFrustumSent;
     bool forceSendScene;
     OctreeSceneStats* stats;
     CoverageMap* map;
@@ -100,7 +100,7 @@ public:
         CoverageMap* map = IGNORE_COVERAGE_MAP,
         int boundaryLevelAdjust = NO_BOUNDARY_ADJUST,
         float octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE,
-        uint64_t lastViewFrustumSent = IGNORE_LAST_SENT,
+        quint64 lastViewFrustumSent = IGNORE_LAST_SENT,
         bool forceSendScene = true,
         OctreeSceneStats* stats = IGNORE_SCENE_STATS,
         JurisdictionMap* jurisdictionMap = IGNORE_JURISDICTION_MAP) :
@@ -187,10 +187,10 @@ public:
     // These methods will allow the OctreeServer to send your tree inbound edit packets of your
     // own definition. Implement these to allow your octree based server to support editing
     virtual bool getWantSVOfileVersions() const { return false; }
-    virtual PACKET_TYPE expectedDataPacketType() const { return PACKET_TYPE_UNKNOWN; }
-    virtual bool handlesEditPacketType(PACKET_TYPE packetType) const { return false; }
-    virtual int processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength,
-                    unsigned char* editData, int maxLength, Node* senderNode) { return 0; }
+    virtual PacketType expectedDataPacketType() const { return PacketTypeUnknown; }
+    virtual bool handlesEditPacketType(PacketType packetType) const { return false; }
+    virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
+                    const unsigned char* editData, int maxLength, Node* senderNode) { return 0; }
 
 
     virtual void update() { }; // nothing to do by default
@@ -240,9 +240,9 @@ public:
 
     // Octree does not currently handle its own locking, caller must use these to lock/unlock
     void lockForRead() { lock.lockForRead(); }
-    void tryLockForRead() { lock.tryLockForRead(); }
+    bool tryLockForRead() { return lock.tryLockForRead(); }
     void lockForWrite() { lock.lockForWrite(); }
-    void tryLockForWrite() { lock.tryLockForWrite(); }
+    bool tryLockForWrite() { return lock.tryLockForWrite(); }
     void unlock() { lock.unlock(); }
 
     unsigned long getOctreeElementsCount();
@@ -258,6 +258,9 @@ public:
     void recurseNodeWithOperationDistanceSorted(OctreeElement* node, RecurseOctreeOperation operation,
                 const glm::vec3& point, void* extraData, int recursionCount = 0);
 
+    bool getIsViewing() const { return _isViewing; }
+    void setIsViewing(bool isViewing) { _isViewing = isViewing; }
+
 signals:
     void importSize(float x, float y, float z);
     void importProgress(int progress);
@@ -321,6 +324,9 @@ protected:
     void emptyDeleteQueue();
 
     QReadWriteLock lock;
+    
+    /// This tree is receiving inbound viewer datagrams.
+    bool _isViewing;
 };
 
 float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale);
diff --git a/libraries/octree/src/OctreeConstants.h b/libraries/octree/src/OctreeConstants.h
index 31820b6fbc..a78d778f70 100644
--- a/libraries/octree/src/OctreeConstants.h
+++ b/libraries/octree/src/OctreeConstants.h
@@ -24,7 +24,7 @@ const glm::vec3 IDENTITY_RIGHT = glm::vec3( 1.0f, 0.0f, 0.0f);
 const glm::vec3 IDENTITY_UP    = glm::vec3( 0.0f, 1.0f, 0.0f);
 const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f,-1.0f);
 
-const uint64_t CHANGE_FUDGE = 1000 * 200; // useconds of fudge in determining if we want to resend changed voxels
+const quint64 CHANGE_FUDGE = 1000 * 200; // useconds of fudge in determining if we want to resend changed voxels
 
 const int   TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe
 
diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp
index d4c1051747..5b84b819cc 100644
--- a/libraries/octree/src/OctreeEditPacketSender.cpp
+++ b/libraries/octree/src/OctreeEditPacketSender.cpp
@@ -17,7 +17,7 @@
 #include "OctreeEditPacketSender.h"
 
 
-EditPacketBuffer::EditPacketBuffer(PACKET_TYPE type, unsigned char* buffer, ssize_t length, QUuid nodeUUID) {
+EditPacketBuffer::EditPacketBuffer(PacketType type, unsigned char* buffer, ssize_t length, QUuid nodeUUID) {
     _nodeUUID = nodeUUID;
     _currentType = type;
     _currentSize = length;
@@ -93,16 +93,16 @@ void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned c
             ((node->getUUID() == nodeUUID) || (nodeUUID.isNull()))) {
             if (nodeList->getNodeActiveSocketOrPing(node.data())) {
                 const HifiSockAddr* nodeAddress = node->getActiveSocket();
-                queuePacketForSending(*nodeAddress, buffer, length);
+                queuePacketForSending(*nodeAddress, QByteArray(reinterpret_cast(buffer), length));
 
                 // debugging output...
                 bool wantDebugging = false;
                 if (wantDebugging) {
-                    int numBytesPacketHeader = numBytesForPacketHeader(buffer);
+                    int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast(buffer));
                     unsigned short int sequence = (*((unsigned short int*)(buffer + numBytesPacketHeader)));
-                    uint64_t createdAt = (*((uint64_t*)(buffer + numBytesPacketHeader + sizeof(sequence))));
-                    uint64_t queuedAt = usecTimestampNow();
-                    uint64_t transitTime = queuedAt - createdAt;
+                    quint64 createdAt = (*((quint64*)(buffer + numBytesPacketHeader + sizeof(sequence))));
+                    quint64 queuedAt = usecTimestampNow();
+                    quint64 transitTime = queuedAt - createdAt;
 
                     qDebug() << "OctreeEditPacketSender::queuePacketToNode() queued " << buffer[0] <<
                             " - command to node bytes=" << length <<
@@ -141,7 +141,7 @@ void OctreeEditPacketSender::processPreServerExistsPackets() {
     }
 }
 
-void OctreeEditPacketSender::queuePendingPacketToNodes(PACKET_TYPE type, unsigned char* buffer, ssize_t length) {
+void OctreeEditPacketSender::queuePendingPacketToNodes(PacketType type, unsigned char* buffer, ssize_t length) {
     // If we're asked to save messages while waiting for voxel servers to arrive, then do so...
     if (_maxPendingMessages > 0) {
         EditPacketBuffer* packet = new EditPacketBuffer(type, buffer, length);
@@ -163,7 +163,7 @@ void OctreeEditPacketSender::queuePacketToNodes(unsigned char* buffer, ssize_t l
 
     assert(serversExist()); // we must have jurisdictions to be here!!
 
-    int headerBytes = numBytesForPacketHeader(buffer) + sizeof(short) + sizeof(uint64_t);
+    int headerBytes = numBytesForPacketHeader(reinterpret_cast(buffer)) + sizeof(short) + sizeof(quint64);
     unsigned char* octCode = buffer + headerBytes; // skip the packet header to get to the octcode
 
     // We want to filter out edit messages for servers based on the server's Jurisdiction
@@ -189,7 +189,7 @@ void OctreeEditPacketSender::queuePacketToNodes(unsigned char* buffer, ssize_t l
 
 
 // NOTE: codeColorBuffer - is JUST the octcode/color and does not contain the packet header!
-void OctreeEditPacketSender::queueOctreeEditMessage(PACKET_TYPE type, unsigned char* codeColorBuffer, ssize_t length) {
+void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, unsigned char* codeColorBuffer, ssize_t length) {
 
     if (!_shouldSend) {
         return; // bail early
@@ -289,28 +289,28 @@ void OctreeEditPacketSender::releaseQueuedMessages() {
 }
 
 void OctreeEditPacketSender::releaseQueuedPacket(EditPacketBuffer& packetBuffer) {
-    if (packetBuffer._currentSize > 0 && packetBuffer._currentType != PACKET_TYPE_UNKNOWN) {
+    if (packetBuffer._currentSize > 0 && packetBuffer._currentType != PacketTypeUnknown) {
         //qDebug() << "OctreeEditPacketSender::releaseQueuedPacket() line:" << __LINE__;
         queuePacketToNode(packetBuffer._nodeUUID, &packetBuffer._currentBuffer[0], packetBuffer._currentSize);
     }
     packetBuffer._currentSize = 0;
-    packetBuffer._currentType = PACKET_TYPE_UNKNOWN;
+    packetBuffer._currentType = PacketTypeUnknown;
 }
 
-void OctreeEditPacketSender::initializePacket(EditPacketBuffer& packetBuffer, PACKET_TYPE type) {
-    packetBuffer._currentSize = populateTypeAndVersion(&packetBuffer._currentBuffer[0], type);
+void OctreeEditPacketSender::initializePacket(EditPacketBuffer& packetBuffer, PacketType type) {
+    packetBuffer._currentSize = populatePacketHeader(reinterpret_cast(&packetBuffer._currentBuffer[0]), type);
 
-    // pack in sequence number
+    // pack in sequence numbers
     unsigned short int* sequenceAt = (unsigned short int*)&packetBuffer._currentBuffer[packetBuffer._currentSize];
     *sequenceAt = _sequenceNumber;
     packetBuffer._currentSize += sizeof(unsigned short int); // nudge past sequence
     _sequenceNumber++;
 
     // pack in timestamp
-    uint64_t now = usecTimestampNow();
-    uint64_t* timeAt = (uint64_t*)&packetBuffer._currentBuffer[packetBuffer._currentSize];
+    quint64 now = usecTimestampNow();
+    quint64* timeAt = (quint64*)&packetBuffer._currentBuffer[packetBuffer._currentSize];
     *timeAt = now;
-    packetBuffer._currentSize += sizeof(uint64_t); // nudge past timestamp
+    packetBuffer._currentSize += sizeof(quint64); // nudge past timestamp
 
     packetBuffer._currentType = type;
 }
diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h
index f214796f9d..3f20ed45a3 100644
--- a/libraries/octree/src/OctreeEditPacketSender.h
+++ b/libraries/octree/src/OctreeEditPacketSender.h
@@ -18,10 +18,10 @@
 /// Used for construction of edit packets
 class EditPacketBuffer {
 public:
-    EditPacketBuffer() : _nodeUUID(), _currentType(PACKET_TYPE_UNKNOWN), _currentSize(0)  { }
-    EditPacketBuffer(PACKET_TYPE type, unsigned char* codeColorBuffer, ssize_t length, const QUuid nodeUUID = QUuid());
+    EditPacketBuffer() : _nodeUUID(), _currentType(PacketTypeUnknown), _currentSize(0)  { }
+    EditPacketBuffer(PacketType type, unsigned char* codeColorBuffer, ssize_t length, const QUuid nodeUUID = QUuid());
     QUuid _nodeUUID;
-    PACKET_TYPE _currentType;
+    PacketType _currentType;
     unsigned char _currentBuffer[MAX_PACKET_SIZE];
     ssize_t _currentSize;
 };
@@ -36,7 +36,7 @@ public:
     /// Queues a single edit message. Will potentially send a pending multi-command packet. Determines which server
     /// node or nodes the packet should be sent to. Can be called even before servers are known, in which case up to 
     /// MaxPendingMessages will be buffered and processed when servers are known.
-    void queueOctreeEditMessage(PACKET_TYPE type, unsigned char* buffer, ssize_t length);
+    void queueOctreeEditMessage(PacketType type, unsigned char* buffer, ssize_t length);
 
     /// Releases all queued messages even if those messages haven't filled an MTU packet. This will move the packed message 
     /// packets onto the send queue. If running in threaded mode, the caller does not need to do any further processing to
@@ -92,9 +92,9 @@ public:
 protected:
     bool _shouldSend;
     void queuePacketToNode(const QUuid& nodeID, unsigned char* buffer, ssize_t length);
-    void queuePendingPacketToNodes(PACKET_TYPE type, unsigned char* buffer, ssize_t length);
+    void queuePendingPacketToNodes(PacketType type, unsigned char* buffer, ssize_t length);
     void queuePacketToNodes(unsigned char* buffer, ssize_t length);
-    void initializePacket(EditPacketBuffer& packetBuffer, PACKET_TYPE type);
+    void initializePacket(EditPacketBuffer& packetBuffer, PacketType type);
     void releaseQueuedPacket(EditPacketBuffer& packetBuffer); // releases specific queued packet
     
     void processPreServerExistsPackets();
diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp
index fbb2d9c08a..c536ad3eeb 100644
--- a/libraries/octree/src/OctreeElement.cpp
+++ b/libraries/octree/src/OctreeElement.cpp
@@ -23,11 +23,11 @@
 #include "OctreeElement.h"
 #include "Octree.h"
 
-uint64_t OctreeElement::_voxelMemoryUsage = 0;
-uint64_t OctreeElement::_octcodeMemoryUsage = 0;
-uint64_t OctreeElement::_externalChildrenMemoryUsage = 0;
-uint64_t OctreeElement::_voxelNodeCount = 0;
-uint64_t OctreeElement::_voxelNodeLeafCount = 0;
+quint64 OctreeElement::_voxelMemoryUsage = 0;
+quint64 OctreeElement::_octcodeMemoryUsage = 0;
+quint64 OctreeElement::_externalChildrenMemoryUsage = 0;
+quint64 OctreeElement::_voxelNodeCount = 0;
+quint64 OctreeElement::_voxelNodeLeafCount = 0;
 
 OctreeElement::OctreeElement() {
     // Note: you must call init() from your subclass, otherwise the OctreeElement will not be properly
@@ -270,23 +270,23 @@ void OctreeElement::auditChildren(const char* label) const {
 #endif // def HAS_AUDIT_CHILDREN
 
 
-uint64_t OctreeElement::_getChildAtIndexTime = 0;
-uint64_t OctreeElement::_getChildAtIndexCalls = 0;
-uint64_t OctreeElement::_setChildAtIndexTime = 0;
-uint64_t OctreeElement::_setChildAtIndexCalls = 0;
+quint64 OctreeElement::_getChildAtIndexTime = 0;
+quint64 OctreeElement::_getChildAtIndexCalls = 0;
+quint64 OctreeElement::_setChildAtIndexTime = 0;
+quint64 OctreeElement::_setChildAtIndexCalls = 0;
 
 #ifdef BLENDED_UNION_CHILDREN
-uint64_t OctreeElement::_singleChildrenCount = 0;
-uint64_t OctreeElement::_twoChildrenOffsetCount = 0;
-uint64_t OctreeElement::_twoChildrenExternalCount = 0;
-uint64_t OctreeElement::_threeChildrenOffsetCount = 0;
-uint64_t OctreeElement::_threeChildrenExternalCount = 0;
-uint64_t OctreeElement::_couldStoreFourChildrenInternally = 0;
-uint64_t OctreeElement::_couldNotStoreFourChildrenInternally = 0;
+quint64 OctreeElement::_singleChildrenCount = 0;
+quint64 OctreeElement::_twoChildrenOffsetCount = 0;
+quint64 OctreeElement::_twoChildrenExternalCount = 0;
+quint64 OctreeElement::_threeChildrenOffsetCount = 0;
+quint64 OctreeElement::_threeChildrenExternalCount = 0;
+quint64 OctreeElement::_couldStoreFourChildrenInternally = 0;
+quint64 OctreeElement::_couldNotStoreFourChildrenInternally = 0;
 #endif
 
-uint64_t OctreeElement::_externalChildrenCount = 0;
-uint64_t OctreeElement::_childrenCount[NUMBER_OF_CHILDREN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+quint64 OctreeElement::_externalChildrenCount = 0;
+quint64 OctreeElement::_childrenCount[NUMBER_OF_CHILDREN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 
 OctreeElement* OctreeElement::getChildAtIndex(int childIndex) const {
 #ifdef SIMPLE_CHILD_ARRAY
@@ -494,17 +494,17 @@ void OctreeElement::retrieveTwoChildren(OctreeElement*& childOne, OctreeElement*
 }
 
 void OctreeElement::decodeThreeOffsets(int64_t& offsetOne, int64_t& offsetTwo, int64_t& offsetThree) const {
-    const uint64_t ENCODE_BITS = 21;
-    const uint64_t ENCODE_MASK = 0xFFFFF;
-    const uint64_t ENCODE_MASK_SIGN = 0x100000;
+    const quint64 ENCODE_BITS = 21;
+    const quint64 ENCODE_MASK = 0xFFFFF;
+    const quint64 ENCODE_MASK_SIGN = 0x100000;
 
-    uint64_t offsetEncodedOne = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 2)) & ENCODE_MASK;
-    uint64_t offsetEncodedTwo = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 1)) & ENCODE_MASK;
-    uint64_t offsetEncodedThree = (_children.offsetsThreeChildrenEncoded & ENCODE_MASK);
+    quint64 offsetEncodedOne = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 2)) & ENCODE_MASK;
+    quint64 offsetEncodedTwo = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 1)) & ENCODE_MASK;
+    quint64 offsetEncodedThree = (_children.offsetsThreeChildrenEncoded & ENCODE_MASK);
 
-    uint64_t signEncodedOne = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 2)) & ENCODE_MASK_SIGN;
-    uint64_t signEncodedTwo = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 1)) & ENCODE_MASK_SIGN;
-    uint64_t signEncodedThree = (_children.offsetsThreeChildrenEncoded & ENCODE_MASK_SIGN);
+    quint64 signEncodedOne = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 2)) & ENCODE_MASK_SIGN;
+    quint64 signEncodedTwo = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 1)) & ENCODE_MASK_SIGN;
+    quint64 signEncodedThree = (_children.offsetsThreeChildrenEncoded & ENCODE_MASK_SIGN);
 
     bool oneNegative = signEncodedOne == ENCODE_MASK_SIGN;
     bool twoNegative = signEncodedTwo == ENCODE_MASK_SIGN;
@@ -516,11 +516,11 @@ void OctreeElement::decodeThreeOffsets(int64_t& offsetOne, int64_t& offsetTwo, i
 }
 
 void OctreeElement::encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int64_t offsetThree) {
-    const uint64_t ENCODE_BITS = 21;
-    const uint64_t ENCODE_MASK = 0xFFFFF;
-    const uint64_t ENCODE_MASK_SIGN = 0x100000;
+    const quint64 ENCODE_BITS = 21;
+    const quint64 ENCODE_MASK = 0xFFFFF;
+    const quint64 ENCODE_MASK_SIGN = 0x100000;
 
-    uint64_t offsetEncodedOne, offsetEncodedTwo, offsetEncodedThree;
+    quint64 offsetEncodedOne, offsetEncodedTwo, offsetEncodedThree;
     if (offsetOne < 0) {
         offsetEncodedOne = ((-offsetOne & ENCODE_MASK) | ENCODE_MASK_SIGN);
     } else {
diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h
index 240bd10f98..38a30218d1 100644
--- a/libraries/octree/src/OctreeElement.h
+++ b/libraries/octree/src/OctreeElement.h
@@ -132,9 +132,9 @@ public:
     bool isDirty() const { return _isDirty; }
     void clearDirtyBit() { _isDirty = false; }
     void setDirtyBit() { _isDirty = true; }
-    bool hasChangedSince(uint64_t time) const { return (_lastChanged > time); }
+    bool hasChangedSince(quint64 time) const { return (_lastChanged > time); }
     void markWithChangedTime();
-    uint64_t getLastChanged() const { return _lastChanged; }
+    quint64 getLastChanged() const { return _lastChanged; }
     void handleSubtreeChanged(Octree* myTree);
     
     // Used by VoxelSystem for rendering in/out of view and LOD
@@ -158,28 +158,28 @@ public:
     static unsigned long getInternalNodeCount() { return _voxelNodeCount - _voxelNodeLeafCount; }
     static unsigned long getLeafNodeCount() { return _voxelNodeLeafCount; }
 
-    static uint64_t getVoxelMemoryUsage() { return _voxelMemoryUsage; }
-    static uint64_t getOctcodeMemoryUsage() { return _octcodeMemoryUsage; }
-    static uint64_t getExternalChildrenMemoryUsage() { return _externalChildrenMemoryUsage; }
-    static uint64_t getTotalMemoryUsage() { return _voxelMemoryUsage + _octcodeMemoryUsage + _externalChildrenMemoryUsage; }
+    static quint64 getVoxelMemoryUsage() { return _voxelMemoryUsage; }
+    static quint64 getOctcodeMemoryUsage() { return _octcodeMemoryUsage; }
+    static quint64 getExternalChildrenMemoryUsage() { return _externalChildrenMemoryUsage; }
+    static quint64 getTotalMemoryUsage() { return _voxelMemoryUsage + _octcodeMemoryUsage + _externalChildrenMemoryUsage; }
 
-    static uint64_t getGetChildAtIndexTime() { return _getChildAtIndexTime; }
-    static uint64_t getGetChildAtIndexCalls() { return _getChildAtIndexCalls; }
-    static uint64_t getSetChildAtIndexTime() { return _setChildAtIndexTime; }
-    static uint64_t getSetChildAtIndexCalls() { return _setChildAtIndexCalls; }
+    static quint64 getGetChildAtIndexTime() { return _getChildAtIndexTime; }
+    static quint64 getGetChildAtIndexCalls() { return _getChildAtIndexCalls; }
+    static quint64 getSetChildAtIndexTime() { return _setChildAtIndexTime; }
+    static quint64 getSetChildAtIndexCalls() { return _setChildAtIndexCalls; }
 
 #ifdef BLENDED_UNION_CHILDREN
-    static uint64_t getSingleChildrenCount() { return _singleChildrenCount; }
-    static uint64_t getTwoChildrenOffsetCount() { return _twoChildrenOffsetCount; }
-    static uint64_t getTwoChildrenExternalCount() { return _twoChildrenExternalCount; }
-    static uint64_t getThreeChildrenOffsetCount() { return _threeChildrenOffsetCount; }
-    static uint64_t getThreeChildrenExternalCount() { return _threeChildrenExternalCount; }
-    static uint64_t getCouldStoreFourChildrenInternally() { return _couldStoreFourChildrenInternally; }
-    static uint64_t getCouldNotStoreFourChildrenInternally() { return _couldNotStoreFourChildrenInternally; }
+    static quint64 getSingleChildrenCount() { return _singleChildrenCount; }
+    static quint64 getTwoChildrenOffsetCount() { return _twoChildrenOffsetCount; }
+    static quint64 getTwoChildrenExternalCount() { return _twoChildrenExternalCount; }
+    static quint64 getThreeChildrenOffsetCount() { return _threeChildrenOffsetCount; }
+    static quint64 getThreeChildrenExternalCount() { return _threeChildrenExternalCount; }
+    static quint64 getCouldStoreFourChildrenInternally() { return _couldStoreFourChildrenInternally; }
+    static quint64 getCouldNotStoreFourChildrenInternally() { return _couldNotStoreFourChildrenInternally; }
 #endif
 
-    static uint64_t getExternalChildrenCount() { return _externalChildrenCount; }
-    static uint64_t getChildrenCount(int childCount) { return _childrenCount[childCount]; }
+    static quint64 getExternalChildrenCount() { return _externalChildrenCount; }
+    static quint64 getChildrenCount(int childCount) { return _childrenCount[childCount]; }
     
 #ifdef BLENDED_UNION_CHILDREN
 #ifdef HAS_AUDIT_CHILDREN
@@ -227,7 +227,7 @@ protected:
       unsigned char* pointer;
     } _octalCode;  
 
-    uint64_t _lastChanged; /// Client and server, timestamp this node was last changed, 8 bytes
+    quint64 _lastChanged; /// Client and server, timestamp this node was last changed, 8 bytes
 
     /// Client and server, pointers to child nodes, various encodings
 #ifdef SIMPLE_CHILD_ARRAY
@@ -245,7 +245,7 @@ protected:
     union children_t {
       OctreeElement* single;
       int32_t offsetsTwoChildren[2];
-      uint64_t offsetsThreeChildrenEncoded;
+      quint64 offsetsThreeChildrenEncoded;
       OctreeElement** external;
     } _children;
 #ifdef HAS_AUDIT_CHILDREN
@@ -278,29 +278,29 @@ protected:
     //static QReadWriteLock _updateHooksLock;
     static std::vector _updateHooks;
 
-    static uint64_t _voxelNodeCount;
-    static uint64_t _voxelNodeLeafCount;
+    static quint64 _voxelNodeCount;
+    static quint64 _voxelNodeLeafCount;
 
-    static uint64_t _voxelMemoryUsage;
-    static uint64_t _octcodeMemoryUsage;
-    static uint64_t _externalChildrenMemoryUsage;
+    static quint64 _voxelMemoryUsage;
+    static quint64 _octcodeMemoryUsage;
+    static quint64 _externalChildrenMemoryUsage;
 
-    static uint64_t _getChildAtIndexTime;
-    static uint64_t _getChildAtIndexCalls;
-    static uint64_t _setChildAtIndexTime;
-    static uint64_t _setChildAtIndexCalls;
+    static quint64 _getChildAtIndexTime;
+    static quint64 _getChildAtIndexCalls;
+    static quint64 _setChildAtIndexTime;
+    static quint64 _setChildAtIndexCalls;
 
 #ifdef BLENDED_UNION_CHILDREN
-    static uint64_t _singleChildrenCount;
-    static uint64_t _twoChildrenOffsetCount;
-    static uint64_t _twoChildrenExternalCount;
-    static uint64_t _threeChildrenOffsetCount;
-    static uint64_t _threeChildrenExternalCount;
-    static uint64_t _couldStoreFourChildrenInternally;
-    static uint64_t _couldNotStoreFourChildrenInternally;
+    static quint64 _singleChildrenCount;
+    static quint64 _twoChildrenOffsetCount;
+    static quint64 _twoChildrenExternalCount;
+    static quint64 _threeChildrenOffsetCount;
+    static quint64 _threeChildrenExternalCount;
+    static quint64 _couldStoreFourChildrenInternally;
+    static quint64 _couldNotStoreFourChildrenInternally;
 #endif
-    static uint64_t _externalChildrenCount;
-    static uint64_t _childrenCount[NUMBER_OF_CHILDREN + 1];
+    static quint64 _externalChildrenCount;
+    static quint64 _childrenCount[NUMBER_OF_CHILDREN + 1];
 };
 
 #endif /* defined(__hifi__OctreeElement__) */
\ No newline at end of file
diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp
index 9e7d88416b..bb613f57c1 100644
--- a/libraries/octree/src/OctreePacketData.cpp
+++ b/libraries/octree/src/OctreePacketData.cpp
@@ -10,12 +10,12 @@
 #include "OctreePacketData.h"
 
 bool OctreePacketData::_debug = false;
-uint64_t OctreePacketData::_totalBytesOfOctalCodes = 0;
-uint64_t OctreePacketData::_totalBytesOfBitMasks = 0;
-uint64_t OctreePacketData::_totalBytesOfColor = 0;
-uint64_t OctreePacketData::_totalBytesOfValues = 0;
-uint64_t OctreePacketData::_totalBytesOfPositions = 0;
-uint64_t OctreePacketData::_totalBytesOfRawData = 0;
+quint64 OctreePacketData::_totalBytesOfOctalCodes = 0;
+quint64 OctreePacketData::_totalBytesOfBitMasks = 0;
+quint64 OctreePacketData::_totalBytesOfColor = 0;
+quint64 OctreePacketData::_totalBytesOfValues = 0;
+quint64 OctreePacketData::_totalBytesOfPositions = 0;
+quint64 OctreePacketData::_totalBytesOfRawData = 0;
 
 
 
@@ -272,7 +272,7 @@ bool OctreePacketData::appendValue(uint32_t value) {
     return success;
 }
 
-bool OctreePacketData::appendValue(uint64_t value) {
+bool OctreePacketData::appendValue(quint64 value) {
     const unsigned char* data = (const unsigned char*)&value;
     int length = sizeof(value);
     bool success = append(data, length);
@@ -305,6 +305,19 @@ bool OctreePacketData::appendValue(const glm::vec3& value) {
     return success;
 }
 
+bool OctreePacketData::appendValue(const glm::quat& value) {
+    const size_t VALUES_PER_QUAT = 4;
+    const size_t PACKED_QUAT_SIZE = sizeof(uint16_t) * VALUES_PER_QUAT;
+    unsigned char data[PACKED_QUAT_SIZE];
+    int length = packOrientationQuatToBytes(data, value);
+    bool success = append(data, length);
+    if (success) {
+        _bytesOfValues += length;
+        _totalBytesOfValues += length;
+    }
+    return success;
+}
+
 bool OctreePacketData::appendValue(bool value) {
     bool success = append((uint8_t)value); // used unsigned char version
     if (success) {
@@ -334,8 +347,8 @@ bool OctreePacketData::appendRawData(const unsigned char* data, int length) {
     return success;
 }
 
-uint64_t OctreePacketData::_compressContentTime = 0;
-uint64_t OctreePacketData::_compressContentCalls = 0;
+quint64 OctreePacketData::_compressContentTime = 0;
+quint64 OctreePacketData::_compressContentCalls = 0;
 
 bool OctreePacketData::compressContent() { 
     PerformanceWarning warn(false, "OctreePacketData::compressContent()", false, &_compressContentTime, &_compressContentCalls);
diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h
index 87b55ace96..f004b45d14 100644
--- a/libraries/octree/src/OctreePacketData.h
+++ b/libraries/octree/src/OctreePacketData.h
@@ -25,11 +25,13 @@
 
 typedef unsigned char OCTREE_PACKET_FLAGS;
 typedef uint16_t OCTREE_PACKET_SEQUENCE;
-typedef uint64_t OCTREE_PACKET_SENT_TIME;
+typedef quint64 OCTREE_PACKET_SENT_TIME;
 typedef uint16_t OCTREE_PACKET_INTERNAL_SECTION_SIZE;
 const int MAX_OCTREE_PACKET_SIZE = MAX_PACKET_SIZE;
-const int OCTREE_PACKET_HEADER_SIZE = (sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION) + sizeof(OCTREE_PACKET_FLAGS) 
-                + sizeof(OCTREE_PACKET_SEQUENCE) + sizeof(OCTREE_PACKET_SENT_TIME));
+
+// this is overly conservative - sizeof(PacketType) is 8 bytes but a packed PacketType could be as small as one byte
+const int OCTREE_PACKET_HEADER_SIZE = MAX_PACKET_HEADER_BYTES +  sizeof(OCTREE_PACKET_FLAGS)
+                + sizeof(OCTREE_PACKET_SEQUENCE) + sizeof(OCTREE_PACKET_SENT_TIME);
 
 const int MAX_OCTREE_PACKET_DATA_SIZE = MAX_PACKET_SIZE - OCTREE_PACKET_HEADER_SIZE;
             
@@ -60,7 +62,7 @@ private:
     int _bytesOfColor;
 };
 
-/// Handles packing of the data portion of PACKET_TYPE_OCTREE_DATA messages. 
+/// Handles packing of the data portion of PacketType_OCTREE_DATA messages. 
 class OctreePacketData {
 public:
     OctreePacketData(bool enableCompression = false, int maxFinalizedSize = MAX_OCTREE_PACKET_DATA_SIZE);
@@ -122,7 +124,7 @@ public:
     bool appendValue(uint32_t value);
 
     /// appends a unsigned 64 bit int to the end of the stream, may fail if new data stream is too long to fit in packet
-    bool appendValue(uint64_t value);
+    bool appendValue(quint64 value);
 
     /// appends a float value to the end of the stream, may fail if new data stream is too long to fit in packet
     bool appendValue(float value);
@@ -130,6 +132,9 @@ public:
     /// appends a non-position vector to the end of the stream, may fail if new data stream is too long to fit in packet
     bool appendValue(const glm::vec3& value);
 
+    /// appends a packed quat to the end of the stream, may fail if new data stream is too long to fit in packet
+    bool appendValue(const glm::quat& value);
+
     /// appends a bool value to the end of the stream, may fail if new data stream is too long to fit in packet
     bool appendValue(bool value);
 
@@ -168,11 +173,11 @@ public:
     /// displays contents for debugging
     void debugContent();
     
-    static uint64_t getCompressContentTime() { return _compressContentTime; } /// total time spent compressing content
-    static uint64_t getCompressContentCalls() { return _compressContentCalls; } /// total calls to compress content
-    static uint64_t getTotalBytesOfOctalCodes() { return _totalBytesOfOctalCodes; }  /// total bytes for octal codes
-    static uint64_t getTotalBytesOfBitMasks() { return _totalBytesOfBitMasks; }  /// total bytes of bitmasks
-    static uint64_t getTotalBytesOfColor() { return _totalBytesOfColor; } /// total bytes of color
+    static quint64 getCompressContentTime() { return _compressContentTime; } /// total time spent compressing content
+    static quint64 getCompressContentCalls() { return _compressContentCalls; } /// total calls to compress content
+    static quint64 getTotalBytesOfOctalCodes() { return _totalBytesOfOctalCodes; }  /// total bytes for octal codes
+    static quint64 getTotalBytesOfBitMasks() { return _totalBytesOfBitMasks; }  /// total bytes of bitmasks
+    static quint64 getTotalBytesOfColor() { return _totalBytesOfColor; } /// total bytes of color
 
 private:
     /// appends raw bytes, might fail if byte would cause packet to be too large
@@ -208,15 +213,15 @@ private:
 
     static bool _debug;
 
-    static uint64_t _compressContentTime;
-    static uint64_t _compressContentCalls;
+    static quint64 _compressContentTime;
+    static quint64 _compressContentCalls;
 
-    static uint64_t _totalBytesOfOctalCodes;
-    static uint64_t _totalBytesOfBitMasks;
-    static uint64_t _totalBytesOfColor;
-    static uint64_t _totalBytesOfValues;
-    static uint64_t _totalBytesOfPositions;
-    static uint64_t _totalBytesOfRawData;
+    static quint64 _totalBytesOfOctalCodes;
+    static quint64 _totalBytesOfBitMasks;
+    static quint64 _totalBytesOfColor;
+    static quint64 _totalBytesOfValues;
+    static quint64 _totalBytesOfPositions;
+    static quint64 _totalBytesOfRawData;
 };
 
 #endif /* defined(__hifi__OctreePacketData__) */
\ No newline at end of file
diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp
index 052773f475..c6c3ce2a6a 100644
--- a/libraries/octree/src/OctreePersistThread.cpp
+++ b/libraries/octree/src/OctreePersistThread.cpp
@@ -25,7 +25,7 @@ OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename,
 bool OctreePersistThread::process() {
 
     if (!_initialLoadComplete) {
-        uint64_t loadStarted = usecTimestampNow();
+        quint64 loadStarted = usecTimestampNow();
         qDebug() << "loading Octrees from file: " << _filename << "...";
 
         bool persistantFileRead;
@@ -37,7 +37,7 @@ bool OctreePersistThread::process() {
         }
         _tree->unlock();
 
-        uint64_t loadDone = usecTimestampNow();
+        quint64 loadDone = usecTimestampNow();
         _loadTimeUSecs = loadDone - loadStarted;
 
         _tree->clearDirtyBit(); // the tree is clean since we just loaded it
@@ -63,8 +63,8 @@ bool OctreePersistThread::process() {
     }
 
     if (isStillRunning()) {
-        uint64_t MSECS_TO_USECS = 1000;
-        uint64_t USECS_TO_SLEEP = 10 * MSECS_TO_USECS; // every 10ms
+        quint64 MSECS_TO_USECS = 1000;
+        quint64 USECS_TO_SLEEP = 10 * MSECS_TO_USECS; // every 10ms
         usleep(USECS_TO_SLEEP);
 
         // do our updates then check to save...
@@ -72,9 +72,9 @@ bool OctreePersistThread::process() {
         _tree->update();
         _tree->unlock();
 
-        uint64_t now = usecTimestampNow();
-        uint64_t sinceLastSave = now - _lastCheck;
-        uint64_t intervalToCheck = _persistInterval * MSECS_TO_USECS;
+        quint64 now = usecTimestampNow();
+        quint64 sinceLastSave = now - _lastCheck;
+        quint64 intervalToCheck = _persistInterval * MSECS_TO_USECS;
 
         if (sinceLastSave > intervalToCheck) {
             // check the dirty bit and persist here...
diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h
index 9bdafe5225..ce6190b0e6 100644
--- a/libraries/octree/src/OctreePersistThread.h
+++ b/libraries/octree/src/OctreePersistThread.h
@@ -24,7 +24,7 @@ public:
     OctreePersistThread(Octree* tree, const QString& filename, int persistInterval = DEFAULT_PERSIST_INTERVAL);
 
     bool isInitialLoadComplete() const { return _initialLoadComplete; }
-    uint64_t getLoadElapsedTime() const { return _loadTimeUSecs; }
+    quint64 getLoadElapsedTime() const { return _loadTimeUSecs; }
 
 signals:
     void loadCompleted();
@@ -38,8 +38,8 @@ private:
     int _persistInterval;
     bool _initialLoadComplete;
 
-    uint64_t _loadTimeUSecs;
-    uint64_t _lastCheck;
+    quint64 _loadTimeUSecs;
+    quint64 _lastCheck;
 };
 
 #endif // __Octree_server__OctreePersistThread__
diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp
index ec474beb3d..5c67715ab9 100644
--- a/libraries/octree/src/OctreeQuery.cpp
+++ b/libraries/octree/src/OctreeQuery.cpp
@@ -24,7 +24,6 @@ static const float fingerVectorRadix = 4; // bits of precision when converting f
 
 OctreeQuery::OctreeQuery() :
     NodeData(),
-    _uuid(),
     _cameraPosition(0,0,0),
     _cameraOrientation(),
     _cameraFov(0.0f),
@@ -53,11 +52,6 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
     // that can pack any type given the number of bytes
     // and return the number of bytes to push the pointer
     
-    // UUID
-    QByteArray uuidByteArray = _uuid.toRfc4122();
-    memcpy(destinationBuffer, uuidByteArray.constData(), uuidByteArray.size());
-    destinationBuffer += uuidByteArray.size();
-    
     // camera details
     memcpy(destinationBuffer, &_cameraPosition, sizeof(_cameraPosition));
     destinationBuffer += sizeof(_cameraPosition);
@@ -95,20 +89,13 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
 }
 
 // called on the other nodes - assigns it to my views of the others
-int OctreeQuery::parseData(unsigned char* sourceBuffer, int numBytes) {
+int OctreeQuery::parseData(const QByteArray& packet) {
 
     // increment to push past the packet header
-    int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer);
-    sourceBuffer += numBytesPacketHeader;
+    int numBytesPacketHeader = numBytesForPacketHeader(packet);
     
-    unsigned char* startPosition = sourceBuffer;
-    
-    // push past the node session UUID
-    sourceBuffer += NUM_BYTES_RFC4122_UUID;
-    
-    // user UUID
-    _uuid = QUuid::fromRfc4122(QByteArray((char*) sourceBuffer, NUM_BYTES_RFC4122_UUID));
-    sourceBuffer += NUM_BYTES_RFC4122_UUID;
+    const unsigned char* startPosition = reinterpret_cast(packet.data());
+    const unsigned char* sourceBuffer = startPosition + numBytesPacketHeader;
     
     // camera details
     memcpy(&_cameraPosition, sourceBuffer, sizeof(_cameraPosition));
diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h
index c306c5a851..800d30b7cd 100644
--- a/libraries/octree/src/OctreeQuery.h
+++ b/libraries/octree/src/OctreeQuery.h
@@ -20,7 +20,7 @@ typedef unsigned char  uint8_t;
 typedef unsigned short uint16_t;
 typedef unsigned int   uint32_t;
 typedef signed long long   int64_t;
-typedef unsigned long long uint64_t;
+typedef unsigned long long quint64;
 #define PRId64 "I64d"
 #else
 #include 
@@ -55,10 +55,7 @@ public:
     virtual ~OctreeQuery();
 
     int getBroadcastData(unsigned char* destinationBuffer);
-    int parseData(unsigned char* sourceBuffer, int numBytes);
-
-    QUuid& getUUID() { return _uuid; }
-    void setUUID(const QUuid& uuid) { _uuid = uuid; }
+    int parseData(const QByteArray& packet);
 
     // getters for camera details
     const glm::vec3& getCameraPosition() const { return _cameraPosition; }
@@ -101,8 +98,6 @@ public slots:
     void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; }
 
 protected:
-    QUuid _uuid;
-
     // camera details for the avatar
     glm::vec3 _cameraPosition;
     glm::quat _cameraOrientation;
diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp
index 7bbeb065d6..0fe56cd936 100644
--- a/libraries/octree/src/OctreeRenderer.cpp
+++ b/libraries/octree/src/OctreeRenderer.cpp
@@ -36,14 +36,17 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Hifi
 
     unsigned char command = *packetData;
     
-    int numBytesPacketHeader = numBytesForPacketHeader(packetData);
+    int numBytesPacketHeader = numBytesForPacketHeader(dataByteArray);
     
-    PACKET_TYPE expectedType = getExpectedPacketType();
+    PacketType expectedType = getExpectedPacketType();
     
     if(command == expectedType) {
-        PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PACKET_TYPE",showTimingDetails);
+        PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PacketType", showTimingDetails);
+        
+        // if we are getting inbound packets, then our tree is also viewing, and we should remember that fact.
+        _tree->setIsViewing(true);
     
-        const unsigned char* dataAt = packetData + numBytesPacketHeader;
+        const unsigned char* dataAt = reinterpret_cast(dataByteArray.data()) + numBytesPacketHeader;
 
         OCTREE_PACKET_FLAGS flags = (*(OCTREE_PACKET_FLAGS*)(dataAt));
         dataAt += sizeof(OCTREE_PACKET_FLAGS);
diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h
index f321ed2e3e..ff385ec62d 100644
--- a/libraries/octree/src/OctreeRenderer.h
+++ b/libraries/octree/src/OctreeRenderer.h
@@ -13,9 +13,9 @@
 #include 
 #include 
 
-#include 
 #include 
 #include 
+
 #include "Octree.h"
 #include "OctreePacketData.h"
 #include "ViewFrustum.h"
@@ -37,16 +37,16 @@ public:
     virtual ~OctreeRenderer();
 
     virtual Octree* createTree() = 0;
-    virtual NODE_TYPE getMyNodeType() const = 0;
-    virtual PACKET_TYPE getMyQueryMessageType() const = 0;
-    virtual PACKET_TYPE getExpectedPacketType() const = 0;
+    virtual NodeType_t getMyNodeType() const = 0;
+    virtual PacketType getMyQueryMessageType() const = 0;
+    virtual PacketType getExpectedPacketType() const = 0;
     virtual void renderElement(OctreeElement* element, RenderArgs* args) = 0;
     
     /// process incoming data
     virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode);
 
     /// initialize and GPU/rendering related resources
-    void init();
+    virtual void init();
 
     /// render the content of the octree
     virtual void render();
diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp
index 35fe8a5d16..59287e3c5c 100644
--- a/libraries/octree/src/OctreeSceneStats.cpp
+++ b/libraries/octree/src/OctreeSceneStats.cpp
@@ -379,7 +379,7 @@ void OctreeSceneStats::childBitsRemoved(bool includesExistsBits, bool includesCo
 int OctreeSceneStats::packIntoMessage(unsigned char* destinationBuffer, int availableBytes) {
     unsigned char* bufferStart = destinationBuffer;
     
-    int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_OCTREE_STATS);
+    int headerLength = populatePacketHeader(reinterpret_cast(destinationBuffer), PacketTypeOctreeStats);
     destinationBuffer += headerLength;
     
     memcpy(destinationBuffer, &_start, sizeof(_start));
@@ -476,11 +476,11 @@ int OctreeSceneStats::packIntoMessage(unsigned char* destinationBuffer, int avai
     return destinationBuffer - bufferStart; // includes header!
 }
 
-int OctreeSceneStats::unpackFromMessage(unsigned char* sourceBuffer, int availableBytes) {
-    unsigned char* startPosition = sourceBuffer;
+int OctreeSceneStats::unpackFromMessage(const unsigned char* sourceBuffer, int availableBytes) {
+    const unsigned char* startPosition = sourceBuffer;
 
     // increment to push past the packet header
-    int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer);
+    int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast(sourceBuffer));
     sourceBuffer += numBytesPacketHeader;
     
     memcpy(&_start, sourceBuffer, sizeof(_start));
@@ -691,7 +691,7 @@ OctreeSceneStats::ItemInfo OctreeSceneStats::_ITEMS[] = {
 };
 
 const char* OctreeSceneStats::getItemValue(Item item) {
-    const uint64_t USECS_PER_SECOND = 1000 * 1000;
+    const quint64 USECS_PER_SECOND = 1000 * 1000;
     int calcFPS, calcAverageFPS, calculatedKBPS;
     switch(item) {
         case ITEM_ELAPSED: {
@@ -797,16 +797,16 @@ const char* OctreeSceneStats::getItemValue(Item item) {
     return _itemValueBuffer;
 }
 
-void OctreeSceneStats::trackIncomingOctreePacket(unsigned char* messageData, ssize_t messageLength, 
+void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
                                     bool wasStatsPacket, int nodeClockSkewUsec) {
     _incomingPacket++;
-    _incomingBytes += messageLength;
+    _incomingBytes += packet.size();
     if (!wasStatsPacket) {
-        _incomingWastedBytes += (MAX_PACKET_SIZE - messageLength);
+        _incomingWastedBytes += (MAX_PACKET_SIZE - packet.size());
     }
 
-    int numBytesPacketHeader = numBytesForPacketHeader(messageData);
-    unsigned char* dataAt = messageData + numBytesPacketHeader;
+    int numBytesPacketHeader = numBytesForPacketHeader(packet);
+    const unsigned char* dataAt = reinterpret_cast(packet.data()) + numBytesPacketHeader;
 
     //VOXEL_PACKET_FLAGS flags = (*(VOXEL_PACKET_FLAGS*)(dataAt));
     dataAt += sizeof(OCTREE_PACKET_FLAGS);
diff --git a/libraries/octree/src/OctreeSceneStats.h b/libraries/octree/src/OctreeSceneStats.h
index 833568f6f3..bf65e85ad4 100644
--- a/libraries/octree/src/OctreeSceneStats.h
+++ b/libraries/octree/src/OctreeSceneStats.h
@@ -88,7 +88,7 @@ public:
     int packIntoMessage(unsigned char* destinationBuffer, int availableBytes);
 
     /// Unpack the details of the statistics from a buffer typically received as a network packet
-    int unpackFromMessage(unsigned char* sourceBuffer, int availableBytes);
+    int unpackFromMessage(const unsigned char* sourceBuffer, int availableBytes);
 
     /// Indicates that a scene has been completed and the statistics are ready to be sent
     bool isReadyToSend() const { return _isReadyToSend; }
@@ -153,8 +153,7 @@ public:
     unsigned long getLastFullElapsedTime() const { return _lastFullElapsed; }
 
     // Used in client implementations to track individual octree packets
-    void trackIncomingOctreePacket(unsigned char* messageData, ssize_t messageLength, 
-                                        bool wasStatsPacket, int nodeClockSkewUsec);
+    void trackIncomingOctreePacket(const QByteArray& packet, bool wasStatsPacket, int nodeClockSkewUsec);
 
     unsigned int getIncomingPackets() const { return _incomingPacket; }
     unsigned long getIncomingBytes() const { return _incomingBytes; } 
@@ -173,17 +172,17 @@ private:
 
     // scene timing data in usecs
     bool _isStarted;
-    uint64_t _start;
-    uint64_t _end;
-    uint64_t _elapsed;
-    uint64_t _lastFullElapsed;
+    quint64 _start;
+    quint64 _end;
+    quint64 _elapsed;
+    quint64 _lastFullElapsed;
     
     SimpleMovingAverage _elapsedAverage;
     SimpleMovingAverage _bitsPerOctreeAverage;
 
-    uint64_t _totalEncodeTime;
-    uint64_t _lastFullTotalEncodeTime;
-    uint64_t _encodeStart;
+    quint64 _totalEncodeTime;
+    quint64 _lastFullTotalEncodeTime;
+    quint64 _encodeStart;
     
     // scene octree related data
     unsigned long _totalElements;
diff --git a/libraries/octree/src/OctreeScriptingInterface.h b/libraries/octree/src/OctreeScriptingInterface.h
index 1158f21438..34eddd8bed 100644
--- a/libraries/octree/src/OctreeScriptingInterface.h
+++ b/libraries/octree/src/OctreeScriptingInterface.h
@@ -29,7 +29,7 @@ public:
     void setJurisdictionListener(JurisdictionListener* jurisdictionListener);
     void init();
     
-    virtual NODE_TYPE getServerNodeType() const = 0;
+    virtual NodeType_t getServerNodeType() const = 0;
     virtual OctreeEditPacketSender* createPacketSender() = 0;
 
 public slots:
diff --git a/libraries/particle-server/src/ParticleNodeData.h b/libraries/particle-server/src/ParticleNodeData.h
index 1758b2e9a3..4ab16cb33a 100644
--- a/libraries/particle-server/src/ParticleNodeData.h
+++ b/libraries/particle-server/src/ParticleNodeData.h
@@ -19,13 +19,13 @@ public:
         OctreeQueryNode(),
         _lastDeletedParticlesSentAt(0) {  };
 
-    virtual PACKET_TYPE getMyPacketType() const { return PACKET_TYPE_PARTICLE_DATA; }
+    virtual PacketType getMyPacketType() const { return PacketTypeParticleData; }
 
-    uint64_t getLastDeletedParticlesSentAt() const { return _lastDeletedParticlesSentAt; }
-    void setLastDeletedParticlesSentAt(uint64_t sentAt) { _lastDeletedParticlesSentAt = sentAt; }
+    quint64 getLastDeletedParticlesSentAt() const { return _lastDeletedParticlesSentAt; }
+    void setLastDeletedParticlesSentAt(quint64 sentAt) { _lastDeletedParticlesSentAt = sentAt; }
 
 private:
-    uint64_t _lastDeletedParticlesSentAt;
+    quint64 _lastDeletedParticlesSentAt;
 };
 
 #endif /* defined(__hifi__ParticleNodeData__) */
diff --git a/libraries/particle-server/src/ParticleServer.cpp b/libraries/particle-server/src/ParticleServer.cpp
index 6df12e5ff2..9344d8c4ae 100644
--- a/libraries/particle-server/src/ParticleServer.cpp
+++ b/libraries/particle-server/src/ParticleServer.cpp
@@ -17,7 +17,7 @@ const char* PARTICLE_SERVER_NAME = "Particle";
 const char* PARTICLE_SERVER_LOGGING_TARGET_NAME = "particle-server";
 const char* LOCAL_PARTICLES_PERSIST_FILE = "resources/particles.svo";
 
-ParticleServer::ParticleServer(const unsigned char* dataBuffer, int numBytes) : OctreeServer(dataBuffer, numBytes) {
+ParticleServer::ParticleServer(const QByteArray& packet) : OctreeServer(packet) {
     // nothing special to do here...
 }
 
@@ -47,7 +47,7 @@ void ParticleServer::particleCreated(const Particle& newParticle, Node* node) {
     unsigned char outputBuffer[MAX_PACKET_SIZE];
     unsigned char* copyAt = outputBuffer;
 
-    int numBytesPacketHeader = populateTypeAndVersion(outputBuffer, PACKET_TYPE_PARTICLE_ADD_RESPONSE);
+    int numBytesPacketHeader = populatePacketHeader(reinterpret_cast(outputBuffer), PacketTypeParticleAddResponse);
     int packetLength = numBytesPacketHeader;
     copyAt += numBytesPacketHeader;
 
@@ -76,7 +76,7 @@ bool ParticleServer::hasSpecialPacketToSend(Node* node) {
     // check to see if any new particles have been added since we last sent to this node...
     ParticleNodeData* nodeData = static_cast(node->getLinkedData());
     if (nodeData) {
-        uint64_t deletedParticlesSentAt = nodeData->getLastDeletedParticlesSentAt();
+        quint64 deletedParticlesSentAt = nodeData->getLastDeletedParticlesSentAt();
 
         ParticleTree* tree = static_cast(_tree);
         shouldSendDeletedParticles = tree->hasParticlesDeletedSince(deletedParticlesSentAt);
@@ -91,8 +91,8 @@ int ParticleServer::sendSpecialPacket(Node* node) {
 
     ParticleNodeData* nodeData = static_cast(node->getLinkedData());
     if (nodeData) {
-        uint64_t deletedParticlesSentAt = nodeData->getLastDeletedParticlesSentAt();
-        uint64_t deletePacketSentAt = usecTimestampNow();
+        quint64 deletedParticlesSentAt = nodeData->getLastDeletedParticlesSentAt();
+        quint64 deletePacketSentAt = usecTimestampNow();
 
         ParticleTree* tree = static_cast(_tree);
         bool hasMoreToSend = true;
@@ -102,7 +102,7 @@ int ParticleServer::sendSpecialPacket(Node* node) {
             hasMoreToSend = tree->encodeParticlesDeletedSince(deletedParticlesSentAt,
                                                 outputBuffer, MAX_PACKET_SIZE, packetLength);
 
-            //qDebug() << "sending PACKET_TYPE_PARTICLE_ERASE packetLength:" << packetLength;
+            //qDebug() << "sending PacketType_PARTICLE_ERASE packetLength:" << packetLength;
 
             NodeList::getInstance()->getNodeSocket().writeDatagram((char*) outputBuffer, packetLength,
                                                                    node->getActiveSocket()->getAddress(),
@@ -121,11 +121,11 @@ void ParticleServer::pruneDeletedParticles() {
     if (tree->hasAnyDeletedParticles()) {
 
         //qDebug() << "there are some deleted particles to consider...";
-        uint64_t earliestLastDeletedParticlesSent = usecTimestampNow() + 1; // in the future
+        quint64 earliestLastDeletedParticlesSent = usecTimestampNow() + 1; // in the future
         foreach (const SharedNodePointer& otherNode, NodeList::getInstance()->getNodeHash()) {
             if (otherNode->getLinkedData()) {
                 ParticleNodeData* nodeData = static_cast(otherNode->getLinkedData());
-                uint64_t nodeLastDeletedParticlesSentAt = nodeData->getLastDeletedParticlesSentAt();
+                quint64 nodeLastDeletedParticlesSentAt = nodeData->getLastDeletedParticlesSentAt();
                 if (nodeLastDeletedParticlesSentAt < earliestLastDeletedParticlesSent) {
                     earliestLastDeletedParticlesSent = nodeLastDeletedParticlesSentAt;
                 }
diff --git a/libraries/particle-server/src/ParticleServer.h b/libraries/particle-server/src/ParticleServer.h
index 55e1b77285..1c1e3b5be9 100644
--- a/libraries/particle-server/src/ParticleServer.h
+++ b/libraries/particle-server/src/ParticleServer.h
@@ -20,14 +20,14 @@
 class ParticleServer : public OctreeServer, public NewlyCreatedParticleHook {
     Q_OBJECT
 public:
-    ParticleServer(const unsigned char* dataBuffer, int numBytes);
+    ParticleServer(const QByteArray& packet);
     ~ParticleServer();
 
     // Subclasses must implement these methods
     virtual OctreeQueryNode* createOctreeQueryNode();
     virtual Octree* createTree();
-    virtual unsigned char getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
-    virtual PACKET_TYPE getMyQueryMessageType() const { return PACKET_TYPE_PARTICLE_QUERY; }
+    virtual unsigned char getMyNodeType() const { return NodeType::ParticleServer; }
+    virtual PacketType getMyQueryMessageType() const { return PacketTypeParticleQuery; }
     virtual const char* getMyServerName() const { return PARTICLE_SERVER_NAME; }
     virtual const char* getMyLoggingServerTargetName() const { return PARTICLE_SERVER_LOGGING_TARGET_NAME; }
     virtual const char* getMyDefaultPersistFilename() const { return LOCAL_PARTICLES_PERSIST_FILE; }
diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp
index ecda364e85..12b59d28c9 100644
--- a/libraries/particles/src/Particle.cpp
+++ b/libraries/particles/src/Particle.cpp
@@ -13,6 +13,8 @@
 #include 
 #include  // usecTimestampNow()
 #include 
+#include 
+
 
 // This is not ideal, but adding script-engine as a linked library, will cause a circular reference
 // I'm open to other potential solutions. Could we change cmake to allow libraries to reference each others
@@ -45,9 +47,9 @@ uint32_t Particle::getNextCreatorTokenID() {
     return creatorTokenID;
 }
 
-void Particle::handleAddParticleResponse(unsigned char* packetData , int packetLength) {
-    unsigned char* dataAt = packetData;
-    int numBytesPacketHeader = numBytesForPacketHeader(packetData);
+void Particle::handleAddParticleResponse(const QByteArray& packet) {
+    const unsigned char* dataAt = reinterpret_cast(packet.data());
+    int numBytesPacketHeader = numBytesForPacketHeader(packet);
     dataAt += numBytesPacketHeader;
 
     uint32_t creatorTokenID;
@@ -58,26 +60,43 @@ void Particle::handleAddParticleResponse(unsigned char* packetData , int packetL
     memcpy(&particleID, dataAt, sizeof(particleID));
     dataAt += sizeof(particleID);
 
-    //qDebug() << "handleAddParticleResponse()... particleID=" << particleID << " creatorTokenID=" << creatorTokenID;
-
     // add our token to id mapping
     _tokenIDsToIDs[creatorTokenID] = particleID;
 }
 
-
-
-Particle::Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity,
-                    float damping, float lifetime, bool inHand, QString updateScript, uint32_t id) {
-
-    init(position, radius, color, velocity, gravity, damping, lifetime, inHand, updateScript, id);
-}
-
 Particle::Particle() {
     rgbColor noColor = { 0, 0, 0 };
     init(glm::vec3(0,0,0), 0, noColor, glm::vec3(0,0,0),
             DEFAULT_GRAVITY, DEFAULT_DAMPING, DEFAULT_LIFETIME, NOT_IN_HAND, DEFAULT_SCRIPT, NEW_PARTICLE);
 }
 
+Particle::Particle(const ParticleID& particleID, const ParticleProperties& properties) {
+    _id = particleID.id;
+    _creatorTokenID = particleID.creatorTokenID;
+
+    // init values with defaults before calling setProperties
+    uint64_t now = usecTimestampNow();
+    _lastEdited = now;
+    _lastUpdated = now;
+    _created = now; // will get updated as appropriate in setAge()
+
+    _position = glm::vec3(0,0,0);
+    _radius = 0;
+    _mass = 1.0f;
+    rgbColor noColor = { 0, 0, 0 };
+    memcpy(_color, noColor, sizeof(_color));
+    _velocity = glm::vec3(0,0,0);
+    _damping = DEFAULT_DAMPING;
+    _lifetime = DEFAULT_LIFETIME;
+    _gravity = DEFAULT_GRAVITY;
+    _script = DEFAULT_SCRIPT;
+    _inHand = NOT_IN_HAND;
+    _shouldDie = false;
+    
+    setProperties(properties);
+}
+
+
 Particle::~Particle() {
 }
 
@@ -86,12 +105,10 @@ void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3
     if (id == NEW_PARTICLE) {
         _id = _nextID;
         _nextID++;
-        //qDebug() << "Particle::init()... assigning new id... _id=" << _id;
     } else {
         _id = id;
-        //qDebug() << "Particle::init()... assigning id from init... _id=" << _id;
     }
-    uint64_t now = usecTimestampNow();
+    quint64 now = usecTimestampNow();
     _lastEdited = now;
     _lastUpdated = now;
     _created = now; // will get updated as appropriate in setAge()
@@ -157,7 +174,6 @@ bool Particle::appendParticleData(OctreePacketData* packetData) const {
     if (success) {
         success = packetData->appendValue(getShouldDie());
     }
-
     if (success) {
         uint16_t scriptLength = _script.size() + 1; // include NULL
         success = packetData->appendValue(scriptLength);
@@ -165,29 +181,36 @@ bool Particle::appendParticleData(OctreePacketData* packetData) const {
             success = packetData->appendRawData((const unsigned char*)qPrintable(_script), scriptLength);
         }
     }
+
+    // modelURL
+    if (success) {
+        uint16_t modelURLLength = _modelURL.size() + 1; // include NULL
+        success = packetData->appendValue(modelURLLength);
+        if (success) {
+            success = packetData->appendRawData((const unsigned char*)qPrintable(_modelURL), modelURLLength);
+        }
+    }
+    // modelTranslation
+    if (success) {
+        success = packetData->appendValue(getModelTranslation());
+    }
+    // modelRotation
+    if (success) {
+        success = packetData->appendValue(getModelRotation());
+    }
+    // modelScale
+    if (success) {
+        success = packetData->appendValue(getModelScale());
+    }
+
     return success;
 }
 
 int Particle::expectedBytes() {
     int expectedBytes = sizeof(uint32_t) // id
                 + sizeof(float) // age
-                + sizeof(uint64_t) // last updated
-                + sizeof(uint64_t) // lasted edited
-                + sizeof(float) // radius
-                + sizeof(glm::vec3) // position
-                + sizeof(rgbColor) // color
-                + sizeof(glm::vec3) // velocity
-                + sizeof(glm::vec3) // gravity
-                + sizeof(float) // damping
-                + sizeof(float) // lifetime
-                + sizeof(bool); // inhand
-                // potentially more...
-    return expectedBytes;
-}
-
-int Particle::expectedEditMessageBytes() {
-    int expectedBytes = sizeof(uint32_t) // id
-                + sizeof(uint64_t) // lasted edited
+                + sizeof(quint64) // last updated
+                + sizeof(quint64) // lasted edited
                 + sizeof(float) // radius
                 + sizeof(glm::vec3) // position
                 + sizeof(rgbColor) // color
@@ -286,15 +309,40 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef
         dataAt += scriptLength;
         bytesRead += scriptLength;
 
+        // modelURL
+        uint16_t modelURLLength;
+        memcpy(&modelURLLength, dataAt, sizeof(modelURLLength));
+        dataAt += sizeof(modelURLLength);
+        bytesRead += sizeof(modelURLLength);
+        QString modelURLString((const char*)dataAt);
+        _modelURL = modelURLString;
+        dataAt += modelURLLength;
+        bytesRead += modelURLLength;
+
+        // modelTranslation
+        memcpy(&_modelTranslation, dataAt, sizeof(_modelTranslation));
+        dataAt += sizeof(_modelTranslation);
+        bytesRead += sizeof(_modelTranslation);
+
+        // modelRotation
+        int bytes = unpackOrientationQuatFromBytes(dataAt, _modelRotation);
+        dataAt += bytes;
+        bytesRead += bytes;
+
+        // modelScale
+        memcpy(&_modelScale, dataAt, sizeof(_modelScale));
+        dataAt += sizeof(_modelScale);
+        bytesRead += sizeof(_modelScale);
+
         //printf("Particle::readParticleDataFromBuffer()... "); debugDump();
     }
     return bytesRead;
 }
 
+Particle Particle::fromEditPacket(const unsigned char* data, int length, int& processedBytes, ParticleTree* tree, bool& valid) {
 
-Particle Particle::fromEditPacket(unsigned char* data, int length, int& processedBytes, ParticleTree* tree, bool& valid) {
     Particle newParticle; // id and _lastUpdated will get set here...
-    unsigned char* dataAt = data;
+    const unsigned char* dataAt = data;
     processedBytes = 0;
 
     // the first part of the data is our octcode...
@@ -311,8 +359,6 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
     dataAt += sizeof(editID);
     processedBytes += sizeof(editID);
 
-    //qDebug() << "editID:" << editID;
-
     bool isNewParticle = (editID == NEW_PARTICLE);
 
     // special case for handling "new" particles
@@ -361,54 +407,53 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
         memcpy(&packetContainsBits, dataAt, sizeof(packetContainsBits));
         dataAt += sizeof(packetContainsBits);
         processedBytes += sizeof(packetContainsBits);
-        //qDebug() << "packetContainsBits:" << packetContainsBits;
     }
 
 
     // radius
-    if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_RADIUS) == PACKET_CONTAINS_RADIUS)) {
+    if (isNewParticle || ((packetContainsBits & CONTAINS_RADIUS) == CONTAINS_RADIUS)) {
         memcpy(&newParticle._radius, dataAt, sizeof(newParticle._radius));
         dataAt += sizeof(newParticle._radius);
         processedBytes += sizeof(newParticle._radius);
     }
 
     // position
-    if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_POSITION) == PACKET_CONTAINS_POSITION)) {
+    if (isNewParticle || ((packetContainsBits & CONTAINS_POSITION) == CONTAINS_POSITION)) {
         memcpy(&newParticle._position, dataAt, sizeof(newParticle._position));
         dataAt += sizeof(newParticle._position);
         processedBytes += sizeof(newParticle._position);
     }
 
     // color
-    if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_COLOR) == PACKET_CONTAINS_COLOR)) {
+    if (isNewParticle || ((packetContainsBits & CONTAINS_COLOR) == CONTAINS_COLOR)) {
         memcpy(newParticle._color, dataAt, sizeof(newParticle._color));
         dataAt += sizeof(newParticle._color);
         processedBytes += sizeof(newParticle._color);
     }
 
     // velocity
-    if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_VELOCITY) == PACKET_CONTAINS_VELOCITY)) {
+    if (isNewParticle || ((packetContainsBits & CONTAINS_VELOCITY) == CONTAINS_VELOCITY)) {
         memcpy(&newParticle._velocity, dataAt, sizeof(newParticle._velocity));
         dataAt += sizeof(newParticle._velocity);
         processedBytes += sizeof(newParticle._velocity);
     }
 
     // gravity
-    if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_GRAVITY) == PACKET_CONTAINS_GRAVITY)) {
+    if (isNewParticle || ((packetContainsBits & CONTAINS_GRAVITY) == CONTAINS_GRAVITY)) {
         memcpy(&newParticle._gravity, dataAt, sizeof(newParticle._gravity));
         dataAt += sizeof(newParticle._gravity);
         processedBytes += sizeof(newParticle._gravity);
     }
 
     // damping
-    if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_DAMPING) == PACKET_CONTAINS_DAMPING)) {
+    if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) {
         memcpy(&newParticle._damping, dataAt, sizeof(newParticle._damping));
         dataAt += sizeof(newParticle._damping);
         processedBytes += sizeof(newParticle._damping);
     }
 
     // lifetime
-    if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_LIFETIME) == PACKET_CONTAINS_LIFETIME)) {
+    if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) {
         memcpy(&newParticle._lifetime, dataAt, sizeof(newParticle._lifetime));
         dataAt += sizeof(newParticle._lifetime);
         processedBytes += sizeof(newParticle._lifetime);
@@ -416,21 +461,21 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
 
     // TODO: make inHand and shouldDie into single bits
     // inHand
-    if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_INHAND) == PACKET_CONTAINS_INHAND)) {
+    if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) {
         memcpy(&newParticle._inHand, dataAt, sizeof(newParticle._inHand));
         dataAt += sizeof(newParticle._inHand);
         processedBytes += sizeof(newParticle._inHand);
     }
 
     // shouldDie
-    if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_SHOULDDIE) == PACKET_CONTAINS_SHOULDDIE)) {
+    if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) {
         memcpy(&newParticle._shouldDie, dataAt, sizeof(newParticle._shouldDie));
         dataAt += sizeof(newParticle._shouldDie);
         processedBytes += sizeof(newParticle._shouldDie);
     }
 
     // script
-    if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_SCRIPT) == PACKET_CONTAINS_SCRIPT)) {
+    if (isNewParticle || ((packetContainsBits & CONTAINS_SCRIPT) == CONTAINS_SCRIPT)) {
         uint16_t scriptLength;
         memcpy(&scriptLength, dataAt, sizeof(scriptLength));
         dataAt += sizeof(scriptLength);
@@ -441,11 +486,43 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
         processedBytes += scriptLength;
     }
 
+    // modelURL
+    if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_URL) == CONTAINS_MODEL_URL)) {
+        uint16_t modelURLLength;
+        memcpy(&modelURLLength, dataAt, sizeof(modelURLLength));
+        dataAt += sizeof(modelURLLength);
+        processedBytes += sizeof(modelURLLength);
+        QString tempString((const char*)dataAt);
+        newParticle._modelURL = tempString;
+        dataAt += modelURLLength;
+        processedBytes += modelURLLength;
+    }
+
+    // modelTranslation
+    if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_TRANSLATION) == CONTAINS_MODEL_TRANSLATION)) {
+        memcpy(&newParticle._modelTranslation, dataAt, sizeof(newParticle._modelTranslation));
+        dataAt += sizeof(newParticle._modelTranslation);
+        processedBytes += sizeof(newParticle._modelTranslation);
+    }
+
+    // modelRotation
+    if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_ROTATION) == CONTAINS_MODEL_ROTATION)) {
+        int bytes = unpackOrientationQuatFromBytes(dataAt, newParticle._modelRotation);
+        dataAt += bytes;
+        processedBytes += bytes;
+    }
+
+    // modelScale
+    if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_SCALE) == CONTAINS_MODEL_SCALE)) {
+        memcpy(&newParticle._modelScale, dataAt, sizeof(newParticle._modelScale));
+        dataAt += sizeof(newParticle._modelScale);
+        processedBytes += sizeof(newParticle._modelScale);
+    }
+
     const bool wantDebugging = false;
     if (wantDebugging) {
         qDebug("Particle::fromEditPacket()...");
         qDebug() << "   Particle id in packet:" << editID;
-        //qDebug() << "    position: " << newParticle._position;
         newParticle.debugDump();
     }
 
@@ -464,7 +541,7 @@ void Particle::debugDump() const {
     printf(" color:%d,%d,%d\n", _color[0], _color[1], _color[2]);
 }
 
-bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID id, const ParticleProperties& properties,
+bool Particle::encodeParticleEditMessageDetails(PacketType command, ParticleID id, const ParticleProperties& properties,
         unsigned char* bufferOut, int sizeIn, int& sizeOut) {
 
     bool success = true; // assume the best
@@ -487,145 +564,171 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID
 
     int octets = numberOfThreeBitSectionsInCode(octcode);
     int lengthOfOctcode = bytesRequiredForCodeLength(octets);
-    int lenfthOfEditData = lengthOfOctcode + expectedEditMessageBytes();
 
-    // make sure we have room to copy this particle
-    if (sizeOut + lenfthOfEditData > sizeIn) {
-        success = false;
-    } else {
-        // add it to our message
-        memcpy(copyAt, octcode, lengthOfOctcode);
-        copyAt += lengthOfOctcode;
-        sizeOut += lengthOfOctcode;
+    // add it to our message
+    memcpy(copyAt, octcode, lengthOfOctcode);
+    copyAt += lengthOfOctcode;
+    sizeOut += lengthOfOctcode;
 
-        // Now add our edit content details...
-        bool isNewParticle = (id.id == NEW_PARTICLE);
+    // Now add our edit content details...
+    bool isNewParticle = (id.id == NEW_PARTICLE);
 
-        // id
-        memcpy(copyAt, &id.id, sizeof(id.id));
-        copyAt += sizeof(id.id);
-        sizeOut += sizeof(id.id);
+    // id
+    memcpy(copyAt, &id.id, sizeof(id.id));
+    copyAt += sizeof(id.id);
+    sizeOut += sizeof(id.id);
 
-        // special case for handling "new" particles
-        if (isNewParticle) {
-            // If this is a NEW_PARTICLE, then we assume that there's an additional uint32_t creatorToken, that
-            // we want to send back to the creator as an map to the actual id
-            memcpy(copyAt, &id.creatorTokenID, sizeof(id.creatorTokenID));
-            copyAt += sizeof(id.creatorTokenID);
-            sizeOut += sizeof(id.creatorTokenID);
-        }
+    // special case for handling "new" particles
+    if (isNewParticle) {
+        // If this is a NEW_PARTICLE, then we assume that there's an additional uint32_t creatorToken, that
+        // we want to send back to the creator as an map to the actual id
+        memcpy(copyAt, &id.creatorTokenID, sizeof(id.creatorTokenID));
+        copyAt += sizeof(id.creatorTokenID);
+        sizeOut += sizeof(id.creatorTokenID);
+    }
+    
+    // lastEdited
+    quint64 lastEdited = properties.getLastEdited();
+    memcpy(copyAt, &lastEdited, sizeof(lastEdited));
+    copyAt += sizeof(lastEdited);
+    sizeOut += sizeof(lastEdited);
+    
+    // For new particles, all remaining items are mandatory, for an edited particle, All of the remaining items are
+    // optional, and may or may not be included based on their included values in the properties included bits
+    uint16_t packetContainsBits = properties.getChangedBits();
+    if (!isNewParticle) {
+        memcpy(copyAt, &packetContainsBits, sizeof(packetContainsBits));
+        copyAt += sizeof(packetContainsBits);
+        sizeOut += sizeof(packetContainsBits);
+    }
 
-        // lastEdited
-        uint64_t lastEdited = properties.getLastEdited();
-        memcpy(copyAt, &lastEdited, sizeof(lastEdited));
-        copyAt += sizeof(lastEdited);
-        sizeOut += sizeof(lastEdited);
+    // radius
+    if (isNewParticle || ((packetContainsBits & CONTAINS_RADIUS) == CONTAINS_RADIUS)) {
+        float radius = properties.getRadius() / (float) TREE_SCALE;
+        memcpy(copyAt, &radius, sizeof(radius));
+        copyAt += sizeof(radius);
+        sizeOut += sizeof(radius);
+    }
 
-        // For new particles, all remaining items are mandatory, for an edited particle, All of the remaining items are
-        // optional, and may or may not be included based on their included values in the properties included bits
-        uint16_t packetContainsBits = properties.getChangedBits();
-        if (!isNewParticle) {
-            memcpy(copyAt, &packetContainsBits, sizeof(packetContainsBits));
-            copyAt += sizeof(packetContainsBits);
-            sizeOut += sizeof(packetContainsBits);
-        }
+    // position
+    if (isNewParticle || ((packetContainsBits & CONTAINS_POSITION) == CONTAINS_POSITION)) {
+        glm::vec3 position = properties.getPosition() / (float)TREE_SCALE;
+        memcpy(copyAt, &position, sizeof(position));
+        copyAt += sizeof(position);
+        sizeOut += sizeof(position);
+    }
 
-        // radius
-        if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_RADIUS) == PACKET_CONTAINS_RADIUS)) {
-            float radius = properties.getRadius() / (float) TREE_SCALE;
-            memcpy(copyAt, &radius, sizeof(radius));
-            copyAt += sizeof(radius);
-            sizeOut += sizeof(radius);
-        }
+    // color
+    if (isNewParticle || ((packetContainsBits & CONTAINS_COLOR) == CONTAINS_COLOR)) {
+        rgbColor color = { properties.getColor().red, properties.getColor().green, properties.getColor().blue };
+        memcpy(copyAt, color, sizeof(color));
+        copyAt += sizeof(color);
+        sizeOut += sizeof(color);
+    }
 
-        // position
-        if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_POSITION) == PACKET_CONTAINS_POSITION)) {
-            glm::vec3 position = properties.getPosition() / (float)TREE_SCALE;
-            memcpy(copyAt, &position, sizeof(position));
-            copyAt += sizeof(position);
-            sizeOut += sizeof(position);
-        }
+    // velocity
+    if (isNewParticle || ((packetContainsBits & CONTAINS_VELOCITY) == CONTAINS_VELOCITY)) {
+        glm::vec3 velocity = properties.getVelocity() / (float)TREE_SCALE;
+        memcpy(copyAt, &velocity, sizeof(velocity));
+        copyAt += sizeof(velocity);
+        sizeOut += sizeof(velocity);
+    }
 
-        // color
-        if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_COLOR) == PACKET_CONTAINS_COLOR)) {
-            rgbColor color = { properties.getColor().red, properties.getColor().green, properties.getColor().blue };
-            memcpy(copyAt, color, sizeof(color));
-            copyAt += sizeof(color);
-            sizeOut += sizeof(color);
-        }
+    // gravity
+    if (isNewParticle || ((packetContainsBits & CONTAINS_GRAVITY) == CONTAINS_GRAVITY)) {
+        glm::vec3 gravity = properties.getGravity() / (float)TREE_SCALE;
+        memcpy(copyAt, &gravity, sizeof(gravity));
+        copyAt += sizeof(gravity);
+        sizeOut += sizeof(gravity);
+    }
 
-        // velocity
-        if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_VELOCITY) == PACKET_CONTAINS_VELOCITY)) {
-            glm::vec3 velocity = properties.getVelocity() / (float)TREE_SCALE;
-            memcpy(copyAt, &velocity, sizeof(velocity));
-            copyAt += sizeof(velocity);
-            sizeOut += sizeof(velocity);
-        }
+    // damping
+    if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) {
+        float damping = properties.getDamping();
+        memcpy(copyAt, &damping, sizeof(damping));
+        copyAt += sizeof(damping);
+        sizeOut += sizeof(damping);
+    }
 
-        // gravity
-        if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_GRAVITY) == PACKET_CONTAINS_GRAVITY)) {
-            glm::vec3 gravity = properties.getGravity() / (float)TREE_SCALE;
-            memcpy(copyAt, &gravity, sizeof(gravity));
-            copyAt += sizeof(gravity);
-            sizeOut += sizeof(gravity);
-        }
+    // lifetime
+    if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) {
+        float lifetime = properties.getLifetime();
+        memcpy(copyAt, &lifetime, sizeof(lifetime));
+        copyAt += sizeof(lifetime);
+        sizeOut += sizeof(lifetime);
+    }
 
-        // damping
-        if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_DAMPING) == PACKET_CONTAINS_DAMPING)) {
-            float damping = properties.getDamping();
-            memcpy(copyAt, &damping, sizeof(damping));
-            copyAt += sizeof(damping);
-            sizeOut += sizeof(damping);
-        }
+    // inHand
+    if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) {
+        bool inHand = properties.getInHand();
+        memcpy(copyAt, &inHand, sizeof(inHand));
+        copyAt += sizeof(inHand);
+        sizeOut += sizeof(inHand);
+    }
 
-        // lifetime
-        if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_LIFETIME) == PACKET_CONTAINS_LIFETIME)) {
-            float lifetime = properties.getLifetime();
-            memcpy(copyAt, &lifetime, sizeof(lifetime));
-            copyAt += sizeof(lifetime);
-            sizeOut += sizeof(lifetime);
-        }
+    // shoulDie
+    if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) {
+        bool shouldDie = properties.getShouldDie();
+        memcpy(copyAt, &shouldDie, sizeof(shouldDie));
+        copyAt += sizeof(shouldDie);
+        sizeOut += sizeof(shouldDie);
+    }
 
-        // inHand
-        if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_INHAND) == PACKET_CONTAINS_INHAND)) {
-            bool inHand = properties.getInHand();
-            memcpy(copyAt, &inHand, sizeof(inHand));
-            copyAt += sizeof(inHand);
-            sizeOut += sizeof(inHand);
-        }
+    // script
+    if (isNewParticle || ((packetContainsBits & CONTAINS_SCRIPT) == CONTAINS_SCRIPT)) {
+        uint16_t scriptLength = properties.getScript().size() + 1;
+        memcpy(copyAt, &scriptLength, sizeof(scriptLength));
+        copyAt += sizeof(scriptLength);
+        sizeOut += sizeof(scriptLength);
+        memcpy(copyAt, qPrintable(properties.getScript()), scriptLength);
+        copyAt += scriptLength;
+        sizeOut += scriptLength;
+    }
 
-        // shoulDie
-        if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_SHOULDDIE) == PACKET_CONTAINS_SHOULDDIE)) {
-            bool shouldDie = properties.getShouldDie();
-            memcpy(copyAt, &shouldDie, sizeof(shouldDie));
-            copyAt += sizeof(shouldDie);
-            sizeOut += sizeof(shouldDie);
-        }
+    // modelURL
+    if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_URL) == CONTAINS_MODEL_URL)) {
+        uint16_t urlLength = properties.getModelURL().size() + 1;
+        memcpy(copyAt, &urlLength, sizeof(urlLength));
+        copyAt += sizeof(urlLength);
+        sizeOut += sizeof(urlLength);
+        memcpy(copyAt, qPrintable(properties.getModelURL()), urlLength);
+        copyAt += urlLength;
+        sizeOut += urlLength;
+    }
 
-        // script
-        if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_SCRIPT) == PACKET_CONTAINS_SCRIPT)) {
-            uint16_t scriptLength = properties.getScript().size() + 1;
-            memcpy(copyAt, &scriptLength, sizeof(scriptLength));
-            copyAt += sizeof(scriptLength);
-            sizeOut += sizeof(scriptLength);
-            memcpy(copyAt, qPrintable(properties.getScript()), scriptLength);
-            copyAt += scriptLength;
-            sizeOut += scriptLength;
-        }
+    // modelTranslation
+    if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_TRANSLATION) == CONTAINS_MODEL_TRANSLATION)) {
+        glm::vec3 modelTranslation = properties.getModelTranslation(); // should this be relative to TREE_SCALE??
+        memcpy(copyAt, &modelTranslation, sizeof(modelTranslation));
+        copyAt += sizeof(modelTranslation);
+        sizeOut += sizeof(modelTranslation);
+    }
 
-        bool wantDebugging = false;
-        if (wantDebugging) {
-            printf("encodeParticleEditMessageDetails()....\n");
-            printf("Particle id  :%u\n", id.id);
-            printf(" nextID:%u\n", _nextID);
-        }
+    // modelRotation
+    if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_ROTATION) == CONTAINS_MODEL_ROTATION)) {
+        int bytes = packOrientationQuatToBytes(copyAt, properties.getModelRotation());
+        copyAt += bytes;
+        sizeOut += bytes;
+    }
+
+    // modelScale
+    if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_SCALE) == CONTAINS_MODEL_SCALE)) {
+        float modelScale = properties.getModelScale();
+        memcpy(copyAt, &modelScale, sizeof(modelScale));
+        copyAt += sizeof(modelScale);
+        sizeOut += sizeof(modelScale);
+    }
+
+    bool wantDebugging = false;
+    if (wantDebugging) {
+        printf("encodeParticleEditMessageDetails()....\n");
+        printf("Particle id  :%u\n", id.id);
+        printf(" nextID:%u\n", _nextID);
     }
 
     // cleanup
     delete[] octcode;
     
-    //qDebug() << "encoding... sizeOut:" << sizeOut;
-
     return success;
 }
 
@@ -648,9 +751,9 @@ void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssiz
     }
 
     // lastEdited
-    uint64_t lastEditedInLocalTime;
+    quint64 lastEditedInLocalTime;
     memcpy(&lastEditedInLocalTime, dataAt, sizeof(lastEditedInLocalTime));
-    uint64_t lastEditedInServerTime = lastEditedInLocalTime + clockSkew;
+    quint64 lastEditedInServerTime = lastEditedInLocalTime + clockSkew;
     memcpy(dataAt, &lastEditedInServerTime, sizeof(lastEditedInServerTime));
     const bool wantDebug = false;
     if (wantDebug) {
@@ -661,24 +764,63 @@ void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssiz
     }
 }
 
+// HALTING_* params are determined using expected acceleration of gravity over some timescale.  
+// This is a HACK for particles that bounce in a 1.0 gravitational field and should eventually be made more universal.
+const float HALTING_PARTICLE_PERIOD = 0.0167f;  // ~1/60th of a second
+const float HALTING_PARTICLE_SPEED = 9.8 * HALTING_PARTICLE_PERIOD / (float)(TREE_SCALE);
+
+void Particle::applyHardCollision(const CollisionInfo& collisionInfo) {
+    //
+    //  Update the particle in response to a hard collision.  Position will be reset exactly
+    //  to outside the colliding surface.  Velocity will be modified according to elasticity.
+    //
+    //  if elasticity = 0.0, collision is inelastic (vel normal to collision is lost)
+    //  if elasticity = 1.0, collision is 100% elastic.
+    //
+    glm::vec3 position = getPosition();
+    glm::vec3 velocity = getVelocity();
+
+    const float EPSILON = 0.0f;
+    glm::vec3 relativeVelocity = collisionInfo._addedVelocity - velocity;
+    float velocityDotPenetration = glm::dot(relativeVelocity, collisionInfo._penetration);
+    if (velocityDotPenetration < EPSILON) {
+        // particle is moving into collision surface
+        //
+        // TODO: do something smarter here by comparing the mass of the particle vs that of the other thing 
+        // (other's mass could be stored in the Collision Info).  The smaller mass should surrender more 
+        // position offset and should slave more to the other's velocity in the static-friction case.
+        position -= collisionInfo._penetration;
+
+        if (glm::length(relativeVelocity) < HALTING_PARTICLE_SPEED) {
+            // static friction kicks in and particle moves with colliding object
+            velocity = collisionInfo._addedVelocity;
+        } else {
+            glm::vec3 direction = glm::normalize(collisionInfo._penetration);
+            velocity += glm::dot(relativeVelocity, direction) * (1.0f + collisionInfo._elasticity) * direction;    // dynamic reflection
+            velocity += glm::clamp(collisionInfo._damping, 0.0f, 1.0f) * (relativeVelocity - glm::dot(relativeVelocity, direction) * direction);   // dynamic friction
+        }
+    }
+
+    // change the local particle too...
+    setPosition(position);
+    setVelocity(velocity);
+}
+
 // MIN_VALID_SPEED is obtained by computing speed gained at one gravity during the shortest expected frame period
+// This is a HACK for particles that bounce in a 1.0 gravitational field and should eventually be made more universal.
 const float MIN_EXPECTED_FRAME_PERIOD = 0.005f;  // 1/200th of a second
 const float MIN_VALID_SPEED = 9.8 * MIN_EXPECTED_FRAME_PERIOD / (float)(TREE_SCALE);
 
-void Particle::update(const uint64_t& now) {
+void Particle::update(const quint64& now) {
     float timeElapsed = (float)(now - _lastUpdated) / (float)(USECS_PER_SECOND);
     _lastUpdated = now;
 
     // calculate our default shouldDie state... then allow script to change it if it wants...
-    float speed = glm::length(_velocity);
-    bool isStopped = (speed < MIN_VALID_SPEED);
-    const uint64_t REALLY_OLD = 30 * USECS_PER_SECOND; // 30 seconds
-    bool isReallyOld = ((now - _created) > REALLY_OLD);
     bool isInHand = getInHand();
-    bool shouldDie = (getAge() > getLifetime()) || getShouldDie() || (!isInHand && isStopped && isReallyOld);
+    bool shouldDie = (getAge() > getLifetime()) || getShouldDie();
     setShouldDie(shouldDie);
 
-    runUpdateScript(); // allow the javascript to alter our state
+    executeUpdateScripts(); // allow the javascript to alter our state
 
     // If the ball is in hand, it doesn't move or have gravity effect it
     if (!isInHand) {
@@ -700,108 +842,64 @@ void Particle::update(const uint64_t& now) {
     }
 }
 
-void Particle::runUpdateScript() {
+void Particle::startParticleScriptContext(ScriptEngine& engine, ParticleScriptObject& particleScriptable) {
+    if (_voxelEditSender) {
+        engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender);
+    }
+    if (_particleEditSender) {
+        engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender);
+    }
+
+    // Add the "this" Particle object
+    engine.registerGlobalObject("Particle", &particleScriptable);
+    engine.evaluate();
+}
+
+void Particle::endParticleScriptContext(ScriptEngine& engine, ParticleScriptObject& particleScriptable) {
+    if (_voxelEditSender) {
+        _voxelEditSender->releaseQueuedMessages();
+    }
+    if (_particleEditSender) {
+        _particleEditSender->releaseQueuedMessages();
+    }
+}
+
+void Particle::executeUpdateScripts() {
+    // Only run this particle script if there's a script attached directly to the particle.
     if (!_script.isEmpty()) {
-        ScriptEngine engine(_script); // no menu or controller interface...
-
-        if (_voxelEditSender) {
-            engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender);
-        }
-        if (_particleEditSender) {
-            engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender);
-        }
-
-        // Add the Particle object
+        ScriptEngine engine(_script);
         ParticleScriptObject particleScriptable(this);
-        engine.registerGlobalObject("Particle", &particleScriptable);
-
-        // init and evaluate the script, but return so we can emit the collision
-        engine.evaluate();
-
+        startParticleScriptContext(engine, particleScriptable);
         particleScriptable.emitUpdate();
-
-        // it seems like we may need to send out particle edits if the state of our particle was changed.
-
-        if (_voxelEditSender) {
-            _voxelEditSender->releaseQueuedMessages();
-        }
-        if (_particleEditSender) {
-            _particleEditSender->releaseQueuedMessages();
-        }
+        endParticleScriptContext(engine, particleScriptable);
     }
 }
 
 void Particle::collisionWithParticle(Particle* other) {
+    // Only run this particle script if there's a script attached directly to the particle.
     if (!_script.isEmpty()) {
-        ScriptEngine engine(_script); // no menu or controller interface...
-
-        if (_voxelEditSender) {
-            engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender);
-        }
-        if (_particleEditSender) {
-            engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender);
-        }
-
-        // Add the Particle object
+        ScriptEngine engine(_script);
         ParticleScriptObject particleScriptable(this);
-        engine.registerGlobalObject("Particle", &particleScriptable);
-
-        // init and evaluate the script, but return so we can emit the collision
-        engine.evaluate();
-
+        startParticleScriptContext(engine, particleScriptable);
         ParticleScriptObject otherParticleScriptable(other);
         particleScriptable.emitCollisionWithParticle(&otherParticleScriptable);
-
-        // it seems like we may need to send out particle edits if the state of our particle was changed.
-
-        if (_voxelEditSender) {
-            _voxelEditSender->releaseQueuedMessages();
-        }
-        if (_particleEditSender) {
-            _particleEditSender->releaseQueuedMessages();
-        }
+        endParticleScriptContext(engine, particleScriptable);
     }
 }
 
 void Particle::collisionWithVoxel(VoxelDetail* voxelDetails) {
+    // Only run this particle script if there's a script attached directly to the particle.
     if (!_script.isEmpty()) {
-
-        ScriptEngine engine(_script); // no menu or controller interface...
-
-        // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
-        // we can use the same ones as our context.
-        if (_voxelEditSender) {
-            engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender);
-        }
-        if (_particleEditSender) {
-            engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender);
-        }
-
-        // Add the Particle object
+        ScriptEngine engine(_script);
         ParticleScriptObject particleScriptable(this);
-        engine.registerGlobalObject("Particle", &particleScriptable);
-
-        // init and evaluate the script, but return so we can emit the collision
-        engine.evaluate();
-
-        VoxelDetailScriptObject voxelDetailsScriptable(voxelDetails);
-        particleScriptable.emitCollisionWithVoxel(&voxelDetailsScriptable);
-
-        // it seems like we may need to send out particle edits if the state of our particle was changed.
-
-        if (_voxelEditSender) {
-            _voxelEditSender->releaseQueuedMessages();
-        }
-        if (_particleEditSender) {
-            _particleEditSender->releaseQueuedMessages();
-        }
+        startParticleScriptContext(engine, particleScriptable);
+        particleScriptable.emitCollisionWithVoxel(*voxelDetails);
+        endParticleScriptContext(engine, particleScriptable);
     }
 }
 
-
-
 void Particle::setAge(float age) {
-    uint64_t ageInUsecs = age * USECS_PER_SECOND;
+    quint64 ageInUsecs = age * USECS_PER_SECOND;
     _created = usecTimestampNow() - ageInUsecs;
 }
 
@@ -832,10 +930,15 @@ ParticleProperties::ParticleProperties() :
     _script(""),
     _inHand(false),
     _shouldDie(false),
+    _modelURL(""),
+    _modelTranslation(DEFAULT_MODEL_TRANSLATION),
+    _modelRotation(DEFAULT_MODEL_ROTATION),
+    _modelScale(DEFAULT_MODEL_SCALE),
 
     _id(UNKNOWN_PARTICLE_ID),
     _idSet(false),
     _lastEdited(usecTimestampNow()),
+
     _positionChanged(false),
     _colorChanged(false),
     _radiusChanged(false),
@@ -846,6 +949,10 @@ ParticleProperties::ParticleProperties() :
     _scriptChanged(false),
     _inHandChanged(false),
     _shouldDieChanged(false),
+    _modelURLChanged(false),
+    _modelTranslationChanged(false),
+    _modelRotationChanged(false),
+    _modelScaleChanged(false),
     _defaultSettings(true)
 {
 }
@@ -854,44 +961,59 @@ ParticleProperties::ParticleProperties() :
 uint16_t ParticleProperties::getChangedBits() const {
     uint16_t changedBits = 0;
     if (_radiusChanged) {
-        changedBits += PACKET_CONTAINS_RADIUS;
+        changedBits += CONTAINS_RADIUS;
     }
 
     if (_positionChanged) {
-        changedBits += PACKET_CONTAINS_POSITION;
+        changedBits += CONTAINS_POSITION;
     }
 
     if (_colorChanged) {
-        changedBits += PACKET_CONTAINS_COLOR;
+        changedBits += CONTAINS_COLOR;
     }
 
     if (_velocityChanged) {
-        changedBits += PACKET_CONTAINS_VELOCITY;
+        changedBits += CONTAINS_VELOCITY;
     }
 
     if (_gravityChanged) {
-        changedBits += PACKET_CONTAINS_GRAVITY;
+        changedBits += CONTAINS_GRAVITY;
     }
 
     if (_dampingChanged) {
-        changedBits += PACKET_CONTAINS_DAMPING;
+        changedBits += CONTAINS_DAMPING;
     }
 
     if (_lifetimeChanged) {
-        changedBits += PACKET_CONTAINS_LIFETIME;
+        changedBits += CONTAINS_LIFETIME;
     }
 
     if (_inHandChanged) {
-        changedBits += PACKET_CONTAINS_INHAND;
+        changedBits += CONTAINS_INHAND;
     }
 
     if (_scriptChanged) {
-        changedBits += PACKET_CONTAINS_SCRIPT;
+        changedBits += CONTAINS_SCRIPT;
     }
 
-    // how do we want to handle this?
     if (_shouldDieChanged) {
-        changedBits += PACKET_CONTAINS_SHOULDDIE;
+        changedBits += CONTAINS_SHOULDDIE;
+    }
+
+    if (_modelURLChanged) {
+        changedBits += CONTAINS_MODEL_URL;
+    }
+
+    if (_modelTranslationChanged) {
+        changedBits += CONTAINS_MODEL_TRANSLATION;
+    }
+
+    if (_modelRotationChanged) {
+        changedBits += CONTAINS_MODEL_ROTATION;
+    }
+
+    if (_modelScaleChanged) {
+        changedBits += CONTAINS_MODEL_SCALE;
     }
 
     return changedBits;
@@ -920,10 +1042,21 @@ QScriptValue ParticleProperties::copyToScriptValue(QScriptEngine* engine) const
     properties.setProperty("script", _script);
     properties.setProperty("inHand", _inHand);
     properties.setProperty("shouldDie", _shouldDie);
-    
+
+    properties.setProperty("modelURL", _modelURL);
+
+    QScriptValue modelTranslation = vec3toScriptValue(engine, _modelTranslation);
+    properties.setProperty("modelTranslation", modelTranslation);
+
+    QScriptValue modelRotation = quatToScriptValue(engine, _modelRotation);
+    properties.setProperty("modelRotation", modelRotation);
+
+    properties.setProperty("modelScale", _modelScale);
+
+
     if (_idSet) {
         properties.setProperty("id", _id);
-        properties.setProperty("isKnownID", (_id == UNKNOWN_PARTICLE_ID));
+        properties.setProperty("isKnownID", (_id != UNKNOWN_PARTICLE_ID));
     }
 
     return properties;
@@ -1061,48 +1194,147 @@ void ParticleProperties::copyFromScriptValue(const QScriptValue &object) {
         }
     }
 
+    QScriptValue modelURL = object.property("modelURL");
+    if (modelURL.isValid()) {
+        QString newModelURL;
+        newModelURL = modelURL.toVariant().toString();
+        if (_defaultSettings || newModelURL != _modelURL) {
+            _modelURL = newModelURL;
+            _modelURLChanged = true;
+        }
+    }
+    
+    QScriptValue modelTranslation = object.property("modelTranslation");
+    if (modelTranslation.isValid()) {
+        QScriptValue x = modelTranslation.property("x");
+        QScriptValue y = modelTranslation.property("y");
+        QScriptValue z = modelTranslation.property("z");
+        if (x.isValid() && y.isValid() && z.isValid()) {
+            glm::vec3 newModelTranslation;
+            newModelTranslation.x = x.toVariant().toFloat();
+            newModelTranslation.y = y.toVariant().toFloat();
+            newModelTranslation.z = z.toVariant().toFloat();
+            if (_defaultSettings || newModelTranslation != _modelTranslation) {
+                _modelTranslation = newModelTranslation;
+                _modelTranslationChanged = true;
+            }
+        }
+    }
+
+    
+    QScriptValue modelRotation = object.property("modelRotation");
+    if (modelRotation.isValid()) {
+        QScriptValue x = modelRotation.property("x");
+        QScriptValue y = modelRotation.property("y");
+        QScriptValue z = modelRotation.property("z");
+        QScriptValue w = modelRotation.property("w");
+        if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) {
+            glm::quat newModelRotation;
+            newModelRotation.x = x.toVariant().toFloat();
+            newModelRotation.y = y.toVariant().toFloat();
+            newModelRotation.z = z.toVariant().toFloat();
+            newModelRotation.w = w.toVariant().toFloat();
+            if (_defaultSettings || newModelRotation != _modelRotation) {
+                _modelRotation = newModelRotation;
+                _modelRotationChanged = true;
+            }
+        }
+    }
+
+    QScriptValue modelScale = object.property("modelScale");
+    if (modelScale.isValid()) {
+        float newModelScale;
+        newModelScale = modelScale.toVariant().toFloat();
+        if (_defaultSettings || newModelScale != _modelScale) {
+            _modelScale = newModelScale;
+            _modelScaleChanged = true;
+        }
+    }
+
     _lastEdited = usecTimestampNow();
 }
 
 void ParticleProperties::copyToParticle(Particle& particle) const {
+    bool somethingChanged = false;
     if (_positionChanged) {
         particle.setPosition(_position / (float) TREE_SCALE);
+        somethingChanged = true;
     }
 
     if (_colorChanged) {
         particle.setColor(_color);
+        somethingChanged = true;
     }
 
     if (_radiusChanged) {
         particle.setRadius(_radius / (float) TREE_SCALE);
+        somethingChanged = true;
     }
 
     if (_velocityChanged) {
         particle.setVelocity(_velocity / (float) TREE_SCALE);
+        somethingChanged = true;
     }
 
     if (_gravityChanged) {
         particle.setGravity(_gravity / (float) TREE_SCALE);
+        somethingChanged = true;
     }
 
     if (_dampingChanged) {
         particle.setDamping(_damping);
+        somethingChanged = true;
     }
 
     if (_lifetimeChanged) {
         particle.setLifetime(_lifetime);
+        somethingChanged = true;
     }
 
     if (_scriptChanged) {
         particle.setScript(_script);
+        somethingChanged = true;
     }
 
     if (_inHandChanged) {
         particle.setInHand(_inHand);
+        somethingChanged = true;
     }
 
     if (_shouldDieChanged) {
         particle.setShouldDie(_shouldDie);
+        somethingChanged = true;
+    }
+
+    if (_modelURLChanged) {
+        particle.setModelURL(_modelURL);
+        somethingChanged = true;
+    }
+    
+    if (_modelTranslationChanged) {
+        particle.setModelTranslation(_modelTranslation);
+        somethingChanged = true;
+    }
+    
+    if (_modelRotationChanged) {
+        particle.setModelRotation(_modelRotation);
+        somethingChanged = true;
+    }
+    
+    if (_modelScaleChanged) {
+        particle.setModelScale(_modelScale);
+        somethingChanged = true;
+    }
+    
+    if (somethingChanged) {
+        bool wantDebug = false;
+        if (wantDebug) {
+            uint64_t now = usecTimestampNow();
+            int elapsed = now - _lastEdited;
+            qDebug() << "ParticleProperties::copyToParticle() AFTER update... edited AGO=" << elapsed <<
+                    "now=" << now << " _lastEdited=" << _lastEdited;
+        }
+        particle.setLastEdited(_lastEdited);
     }
 }
 
@@ -1117,6 +1349,10 @@ void ParticleProperties::copyFromParticle(const Particle& particle) {
     _script = particle.getScript();
     _inHand = particle.getInHand();
     _shouldDie = particle.getShouldDie();
+    _modelURL = particle.getModelURL();
+    _modelTranslation = particle.getModelTranslation();
+    _modelRotation = particle.getModelRotation();
+    _modelScale = particle.getModelScale();
 
     _id = particle.getID();
     _idSet = true;
@@ -1131,6 +1367,10 @@ void ParticleProperties::copyFromParticle(const Particle& particle) {
     _scriptChanged = false;
     _inHandChanged = false;
     _shouldDieChanged = false;
+    _modelURLChanged = false;
+    _modelTranslationChanged = false;
+    _modelRotationChanged = false;
+    _modelScaleChanged = false;
     _defaultSettings = false;
 }
 
diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h
index ebd21a6b52..925227055f 100644
--- a/libraries/particles/src/Particle.h
+++ b/libraries/particles/src/Particle.h
@@ -16,31 +16,39 @@
 #include 
 #include 
 
+#include 
 #include 
 #include 
 
-class VoxelsScriptingInterface;
-class ParticlesScriptingInterface;
-class VoxelEditPacketSender;
+class Particle;
 class ParticleEditPacketSender;
 class ParticleProperties;
-class Particle;
+class ParticlesScriptingInterface;
+class ParticleScriptObject;
 class ParticleTree;
+class ScriptEngine;
+class VoxelEditPacketSender;
+class VoxelsScriptingInterface;
+struct VoxelDetail;
 
 const uint32_t NEW_PARTICLE = 0xFFFFFFFF;
 const uint32_t UNKNOWN_TOKEN = 0xFFFFFFFF;
 const uint32_t UNKNOWN_PARTICLE_ID = 0xFFFFFFFF;
 
-const uint16_t PACKET_CONTAINS_RADIUS = 1;
-const uint16_t PACKET_CONTAINS_POSITION = 2;
-const uint16_t PACKET_CONTAINS_COLOR = 4;
-const uint16_t PACKET_CONTAINS_VELOCITY = 8;
-const uint16_t PACKET_CONTAINS_GRAVITY = 16;
-const uint16_t PACKET_CONTAINS_DAMPING = 32;
-const uint16_t PACKET_CONTAINS_LIFETIME = 64;
-const uint16_t PACKET_CONTAINS_INHAND = 128;
-const uint16_t PACKET_CONTAINS_SCRIPT = 256;
-const uint16_t PACKET_CONTAINS_SHOULDDIE = 512;
+const uint16_t CONTAINS_RADIUS = 1;
+const uint16_t CONTAINS_POSITION = 2;
+const uint16_t CONTAINS_COLOR = 4;
+const uint16_t CONTAINS_VELOCITY = 8;
+const uint16_t CONTAINS_GRAVITY = 16;
+const uint16_t CONTAINS_DAMPING = 32;
+const uint16_t CONTAINS_LIFETIME = 64;
+const uint16_t CONTAINS_INHAND = 128;
+const uint16_t CONTAINS_SCRIPT = 256;
+const uint16_t CONTAINS_SHOULDDIE = 512;
+const uint16_t CONTAINS_MODEL_URL = 1024;
+const uint16_t CONTAINS_MODEL_TRANSLATION = 1024;
+const uint16_t CONTAINS_MODEL_ROTATION = 2048;
+const uint16_t CONTAINS_MODEL_SCALE = 4096;
 
 const float DEFAULT_LIFETIME = 10.0f; // particles live for 10 seconds by default
 const float DEFAULT_DAMPING = 0.99f;
@@ -48,9 +56,13 @@ const float DEFAULT_RADIUS = 0.1f / TREE_SCALE;
 const float MINIMUM_PARTICLE_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container
 const glm::vec3 DEFAULT_GRAVITY(0, (-9.8f / TREE_SCALE), 0);
 const QString DEFAULT_SCRIPT("");
+const glm::vec3 DEFAULT_MODEL_TRANSLATION(0, 0, 0);
+const glm::quat DEFAULT_MODEL_ROTATION(0, 0, 0, 0);
+const float DEFAULT_MODEL_SCALE = 1.0f;
 const bool IN_HAND = true; // it's in a hand
 const bool NOT_IN_HAND = !IN_HAND; // it's not in a hand
 
+
 /// A collection of properties of a particle used in the scripting API. Translates between the actual properties of a particle
 /// and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete set of
 /// particle properties via JavaScript hashes/QScriptValues
@@ -75,8 +87,12 @@ public:
     const QString& getScript() const { return _script; }
     bool getInHand() const { return _inHand; }
     bool getShouldDie() const { return _shouldDie; }
+    const QString& getModelURL() const { return _modelURL; }
+    const glm::vec3& getModelTranslation() const { return _modelTranslation; }
+    const glm::quat& getModelRotation() const { return _modelRotation; }
+    float getModelScale() const { return _modelScale; }
 
-    uint64_t getLastEdited() const { return _lastEdited; }
+    quint64 getLastEdited() const { return _lastEdited; }
     uint16_t getChangedBits() const;
 
     /// set position in meter units
@@ -93,7 +109,14 @@ public:
     void setDamping(float value) { _damping = value; _dampingChanged = true;  }
     void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; _shouldDieChanged = true;  }
     void setLifetime(float value) { _lifetime = value; _lifetimeChanged = true;  }
-    void setScript(QString updateScript) { _script = updateScript; _scriptChanged = true;  }
+    void setScript(const QString& updateScript) { _script = updateScript; _scriptChanged = true;  }
+
+    // model related properties
+    void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; }
+    void setModelTranslation(const glm::vec3&  translation) { _modelTranslation = translation; 
+                                                              _modelTranslationChanged = true; }
+    void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; _modelRotationChanged = true; }
+    void setModelScale(float scale) { _modelScale = scale; _modelScaleChanged = true; }
     
     /// used by ParticleScriptingInterface to return ParticleProperties for unknown particles
     void setIsUnknownID() { _id = UNKNOWN_PARTICLE_ID; _idSet = true; }
@@ -109,10 +132,15 @@ private:
     QString _script;
     bool _inHand;
     bool _shouldDie;
+    QString _modelURL;
+    glm::vec3 _modelTranslation;
+    glm::quat _modelRotation;
+    float _modelScale;
 
     uint32_t _id;
     bool _idSet;
-    uint64_t _lastEdited;
+    quint64 _lastEdited;
+
     bool _positionChanged;
     bool _colorChanged;
     bool _radiusChanged;
@@ -123,6 +151,10 @@ private:
     bool _scriptChanged;
     bool _inHandChanged;
     bool _shouldDieChanged;
+    bool _modelURLChanged;
+    bool _modelTranslationChanged;
+    bool _modelRotationChanged;
+    bool _modelScaleChanged;
     bool _defaultSettings;
 };
 Q_DECLARE_METATYPE(ParticleProperties);
@@ -161,14 +193,11 @@ class Particle  {
 
 public:
     Particle();
-    
-    /// all position, velocity, gravity, radius units are in domain units (0.0 to 1.0)
-    Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity,
-            glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, float lifetime = DEFAULT_LIFETIME,
-            bool inHand = NOT_IN_HAND, QString updateScript = DEFAULT_SCRIPT, uint32_t id = NEW_PARTICLE);
 
+    Particle(const ParticleID& particleID, const ParticleProperties& properties);
+    
     /// creates an NEW particle from an PACKET_TYPE_PARTICLE_ADD_OR_EDIT edit data buffer
-    static Particle fromEditPacket(unsigned char* data, int length, int& processedBytes, ParticleTree* tree, bool& valid);
+    static Particle fromEditPacket(const unsigned char* data, int length, int& processedBytes, ParticleTree* tree, bool& valid);
 
     virtual ~Particle();
     virtual void init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity,
@@ -194,18 +223,29 @@ public:
     bool getInHand() const { return _inHand; }
     float getDamping() const { return _damping; }
     float getLifetime() const { return _lifetime; }
+    
+    // model related properties
+    bool hasModel() const { return !_modelURL.isEmpty(); }
+    const QString& getModelURL() const { return _modelURL; }
+    const glm::vec3& getModelTranslation() const { return _modelTranslation; }
+    const glm::quat& getModelRotation() const { return _modelRotation; }
+    float getModelScale() const { return _modelScale; }
+
+    ParticleID getParticleID() const { return ParticleID(getID(), getCreatorTokenID(), getID() != UNKNOWN_PARTICLE_ID); }
     ParticleProperties getProperties() const;
 
     /// The last updated/simulated time of this particle from the time perspective of the authoritative server/source
-    uint64_t getLastUpdated() const { return _lastUpdated; }
+    quint64 getLastUpdated() const { return _lastUpdated; }
 
     /// The last edited time of this particle from the time perspective of the authoritative server/source
-    uint64_t getLastEdited() const { return _lastEdited; }
+    quint64 getLastEdited() const { return _lastEdited; }
+    void setLastEdited(quint64 lastEdited) { _lastEdited = lastEdited; }
 
     /// lifetime of the particle in seconds
     float getAge() const { return static_cast(usecTimestampNow() - _created) / static_cast(USECS_PER_SECOND); }
     float getEditedAgo() const { return static_cast(usecTimestampNow() - _lastEdited) / static_cast(USECS_PER_SECOND); }
     uint32_t getID() const { return _id; }
+    void setID(uint32_t id) { _id = id; }
     bool getShouldDie() const { return _shouldDie; }
     QString getScript() const { return _script; }
     uint32_t getCreatorTokenID() const { return _creatorTokenID; }
@@ -234,19 +274,27 @@ public:
     void setLifetime(float value) { _lifetime = value; }
     void setScript(QString updateScript) { _script = updateScript; }
     void setCreatorTokenID(uint32_t creatorTokenID) { _creatorTokenID = creatorTokenID; }
+    
+    // model related properties
+    void setModelURL(const QString& url) { _modelURL = url; }
+    void setModelTranslation(const glm::vec3&  translation) { _modelTranslation = translation; }
+    void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; }
+    void setModelScale(float scale) { _modelScale = scale; }
+    
     void setProperties(const ParticleProperties& properties);
 
     bool appendParticleData(OctreePacketData* packetData) const;
     int readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
     static int expectedBytes();
-    static int expectedEditMessageBytes();
 
-    static bool encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID id, const ParticleProperties& details,
+    static bool encodeParticleEditMessageDetails(PacketType command, ParticleID id, const ParticleProperties& details,
                         unsigned char* bufferOut, int sizeIn, int& sizeOut);
 
     static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew);
+    
+    void applyHardCollision(const CollisionInfo& collisionInfo);
 
-    void update(const uint64_t& now);
+    void update(const quint64& now);
     void collisionWithParticle(Particle* other);
     void collisionWithVoxel(VoxelDetail* voxel);
 
@@ -268,17 +316,15 @@ public:
     // these methods allow you to create particles, and later edit them.
     static uint32_t getIDfromCreatorTokenID(uint32_t creatorTokenID);
     static uint32_t getNextCreatorTokenID();
-    static void handleAddParticleResponse(unsigned char* packetData , int packetLength);
+    static void handleAddParticleResponse(const QByteArray& packet);
 
 protected:
     static VoxelEditPacketSender* _voxelEditSender;
     static ParticleEditPacketSender* _particleEditSender;
 
-    void runUpdateScript();
-    static QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3);
-    static void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3);
-    static QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color);
-    static void xColorFromScriptValue(const QScriptValue &object, xColor& color);
+    void startParticleScriptContext(ScriptEngine& engine, ParticleScriptObject& particleScriptable);
+    void endParticleScriptContext(ScriptEngine& engine, ParticleScriptObject& particleScriptable);
+    void executeUpdateScripts();
 
     void setAge(float age);
 
@@ -296,14 +342,20 @@ protected:
     QString _script;
     bool _inHand;
 
+    // model related items
+    QString _modelURL;
+    glm::vec3 _modelTranslation;
+    glm::quat _modelRotation;
+    float _modelScale;
+
     uint32_t _creatorTokenID;
     bool _newlyCreated;
 
-    uint64_t _lastUpdated;
-    uint64_t _lastEdited;
+    quint64 _lastUpdated;
+    quint64 _lastEdited;
 
     // this doesn't go on the wire, we send it as lifetime
-    uint64_t _created;
+    quint64 _created;
 
     // used by the static interfaces for creator token ids
     static uint32_t _nextCreatorTokenID;
@@ -316,10 +368,11 @@ class ParticleScriptObject  : public QObject {
     Q_OBJECT
 public:
     ParticleScriptObject(Particle* particle) { _particle = particle; }
+    //~ParticleScriptObject() { qDebug() << "~ParticleScriptObject() this=" << this; }
 
     void emitUpdate() { emit update(); }
     void emitCollisionWithParticle(QObject* other) { emit collisionWithParticle(other); }
-    void emitCollisionWithVoxel(QObject* voxel) { emit collisionWithVoxel(voxel); }
+    void emitCollisionWithVoxel(const VoxelDetail& voxel) { emit collisionWithVoxel(voxel); }
 
 public slots:
     unsigned int getID() const { return _particle->getID(); }
@@ -364,7 +417,7 @@ public slots:
 
 signals:
     void update();
-    void collisionWithVoxel(QObject* voxel);
+    void collisionWithVoxel(const VoxelDetail& voxel);
     void collisionWithParticle(QObject* other);
 
 private:
diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp
index 7e8866e7d4..c718ab3ddc 100644
--- a/libraries/particles/src/ParticleCollisionSystem.cpp
+++ b/libraries/particles/src/ParticleCollisionSystem.cpp
@@ -20,17 +20,19 @@
 #include "ParticleTree.h"
 
 ParticleCollisionSystem::ParticleCollisionSystem(ParticleEditPacketSender* packetSender,
-    ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio, AvatarData* selfAvatar) {
-    init(packetSender, particles, voxels, audio, selfAvatar);
+    ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio,
+    AvatarHashMap* avatars) {
+    init(packetSender, particles, voxels, audio, avatars);
 }
 
 void ParticleCollisionSystem::init(ParticleEditPacketSender* packetSender,
-    ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio, AvatarData* selfAvatar) {
+    ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio,
+    AvatarHashMap* avatars) {
     _packetSender = packetSender;
     _particles = particles;
     _voxels = voxels;
     _audio = audio;
-    _selfAvatar = selfAvatar;
+    _avatars = avatars;
 }
 
 ParticleCollisionSystem::~ParticleCollisionSystem() {
@@ -54,9 +56,10 @@ bool ParticleCollisionSystem::updateOperation(OctreeElement* element, void* extr
 
 void ParticleCollisionSystem::update() {
     // update all particles
-    _particles->lockForWrite();
-    _particles->recurseTreeWithOperation(updateOperation, this);
-    _particles->unlock();
+    if (_particles->tryLockForWrite()) {
+        _particles->recurseTreeWithOperation(updateOperation, this);
+        _particles->unlock();
+    }
 }
 
 
@@ -66,6 +69,17 @@ void ParticleCollisionSystem::checkParticle(Particle* particle) {
     updateCollisionWithAvatars(particle);
 }
 
+void ParticleCollisionSystem::emitGlobalParticleCollisionWithVoxel(Particle* particle, VoxelDetail* voxelDetails) {
+    ParticleID particleID = particle->getParticleID();
+    emit particleCollisionWithVoxel(particleID, *voxelDetails);
+}
+
+void ParticleCollisionSystem::emitGlobalParticleCollisionWithParticle(Particle* particleA, Particle* particleB) {
+    ParticleID idA = particleA->getParticleID();
+    ParticleID idB = particleB->getParticleID();
+    emit particleCollisionWithParticle(idA, idB);
+}
+
 void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) {
     glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE);
     float radius = particle->getRadius() * (float)(TREE_SCALE);
@@ -73,15 +87,21 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) {
     const float DAMPING = 0.05f;
     const float COLLISION_FREQUENCY = 0.5f;
     CollisionInfo collisionInfo;
+    collisionInfo._damping = DAMPING;
+    collisionInfo._elasticity = ELASTICITY;
     VoxelDetail* voxelDetails = NULL;
     if (_voxels->findSpherePenetration(center, radius, collisionInfo._penetration, (void**)&voxelDetails)) {
 
         // let the particles run their collision scripts if they have them
         particle->collisionWithVoxel(voxelDetails);
 
+        // let the global script run their collision scripts for particles if they have them
+        emitGlobalParticleCollisionWithVoxel(particle, voxelDetails);
+
         updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
         collisionInfo._penetration /= (float)(TREE_SCALE);
-        applyHardCollision(particle, ELASTICITY, DAMPING, collisionInfo);
+        particle->applyHardCollision(collisionInfo);
+        queueParticlePropertiesUpdate(particle);
 
         delete voxelDetails; // cleanup returned details
     }
@@ -105,6 +125,7 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA)
         if (glm::dot(relativeVelocity, penetration) > 0.0f) {
             particleA->collisionWithParticle(particleB);
             particleB->collisionWithParticle(particleA);
+            emitGlobalParticleCollisionWithParticle(particleA, particleB);
 
             glm::vec3 axis = glm::normalize(penetration);
             glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis;
@@ -122,7 +143,7 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA)
             ParticleID particleAid(particleA->getID());
             propertiesA.copyFromParticle(*particleA);
             propertiesA.setVelocity(particleA->getVelocity() * (float)TREE_SCALE);
-            _packetSender->queueParticleEditMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleAid, propertiesA);
+            _packetSender->queueParticleEditMessage(PacketTypeParticleAddOrEdit, particleAid, propertiesA);
 
             // handle B particle
             particleB->setVelocity(particleB->getVelocity() + axialVelocity * (2.0f * massA / totalMass));
@@ -130,7 +151,7 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA)
             ParticleID particleBid(particleB->getID());
             propertiesB.copyFromParticle(*particleB);
             propertiesB.setVelocity(particleB->getVelocity() * (float)TREE_SCALE);
-            _packetSender->queueParticleEditMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleBid, propertiesB);
+            _packetSender->queueParticleEditMessage(PacketTypeParticleAddOrEdit, particleBid, propertiesB);
 
             _packetSender->releaseQueuedMessages();
 
@@ -144,9 +165,8 @@ const float MIN_EXPECTED_FRAME_PERIOD = 0.0167f;  // 1/60th of a second
 const float HALTING_SPEED = 9.8 * MIN_EXPECTED_FRAME_PERIOD / (float)(TREE_SCALE);
 
 void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
-
     // particles that are in hand, don't collide with avatars
-    if (particle->getInHand()) {
+    if (!_avatars || particle->getInHand()) {
         return;
     }
 
@@ -157,10 +177,11 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
     const float COLLISION_FREQUENCY = 0.5f;
     glm::vec3 penetration;
 
-    // first check the selfAvatar if set...
-    if (_selfAvatar) {
-        AvatarData* avatar = (AvatarData*)_selfAvatar;
+    foreach (const AvatarSharedPointer& avatarPointer, _avatars->getAvatarHash()) {
+        AvatarData* avatar = avatarPointer.data();
         CollisionInfo collisionInfo;
+        collisionInfo._damping = DAMPING;
+        collisionInfo._elasticity = ELASTICITY;
         if (avatar->findSphereCollision(center, radius, collisionInfo)) {
             collisionInfo._addedVelocity /= (float)(TREE_SCALE);
             glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity();
@@ -185,90 +206,22 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
 
                 updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
                 collisionInfo._penetration /= (float)(TREE_SCALE);
-                applyHardCollision(particle, elasticity, damping, collisionInfo);
+                particle->applyHardCollision(collisionInfo);
+                queueParticlePropertiesUpdate(particle);
             }
         }
     }
-
-    // loop through all the other avatars for potential interactions...
-//    foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
-//        //qDebug() << "updateCollisionWithAvatars()... node:" << *node << "\n";
-//        if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
-//            AvatarData* avatar = static_cast(node->getLinkedData());
-//            CollisionInfo collisionInfo;
-//            if (avatar->findSphereCollision(center, radius, collisionInfo)) {
-//                collisionInfo._addedVelocity /= (float)(TREE_SCALE);
-//                glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity();
-//                if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) {
-//                    // HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar.
-//                    // NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
-//                    // TODO: make this less hacky when we have more per-collision details
-//                    float elasticity = ELASTICITY;
-//                    float attenuationFactor = glm::length(collisionInfo._addedVelocity) / HALTING_SPEED;
-//                    float damping = DAMPING;
-//                    if (attenuationFactor < 1.f) {
-//                        collisionInfo._addedVelocity *= attenuationFactor;
-//                        elasticity *= attenuationFactor;
-//                        // NOTE: the math below keeps the damping piecewise continuous,
-//                        // while ramping it up to 1.0 when attenuationFactor = 0
-//                        damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING);
-//                    }
-//                    // HACK END
-//
-//                    updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
-//                    collisionInfo._penetration /= (float)(TREE_SCALE);
-//                    applyHardCollision(particle, ELASTICITY, damping, collisionInfo);
-//                }
-//            }
-//        }
-//    }
 }
 
-// TODO: convert applyHardCollision() to take a CollisionInfo& instead of penetration + addedVelocity
-void ParticleCollisionSystem::applyHardCollision(Particle* particle, float elasticity, float damping, const CollisionInfo& collisionInfo) {
-    //
-    //  Update the particle in response to a hard collision.  Position will be reset exactly
-    //  to outside the colliding surface.  Velocity will be modified according to elasticity.
-    //
-    //  if elasticity = 0.0, collision is inelastic (vel normal to collision is lost)
-    //  if elasticity = 1.0, collision is 100% elastic.
-    //
-    glm::vec3 position = particle->getPosition();
-    glm::vec3 velocity = particle->getVelocity();
-
-    const float EPSILON = 0.0f;
-    glm::vec3 relativeVelocity = collisionInfo._addedVelocity - velocity;
-    float velocityDotPenetration = glm::dot(relativeVelocity, collisionInfo._penetration);
-    if (velocityDotPenetration < EPSILON) {
-        // particle is moving into collision surface
-        position -= collisionInfo._penetration;
-
-        if (glm::length(relativeVelocity) < HALTING_SPEED) {
-            // static friction kicks in and particle moves with colliding object
-            velocity = collisionInfo._addedVelocity;
-        } else {
-            glm::vec3 direction = glm::normalize(collisionInfo._penetration);
-            velocity += glm::dot(relativeVelocity, direction) * (1.0f + elasticity) * direction;    // dynamic reflection
-            velocity += glm::clamp(damping, 0.0f, 1.0f) * (relativeVelocity - glm::dot(relativeVelocity, direction) * direction);   // dynamic friction
-        }
-    }
-    const bool wantDebug = false;
-    if (wantDebug) {
-        printf("ParticleCollisionSystem::applyHardCollision() particle id:%d new velocity:%f,%f,%f inHand:%s\n",
-            particle->getID(), velocity.x, velocity.y, velocity.z, debug::valueOf(particle->getInHand()));
-    }
-
-    // send off the result to the particle server
+void ParticleCollisionSystem::queueParticlePropertiesUpdate(Particle* particle) {
+    // queue the result for sending to the particle server
     ParticleProperties properties;
     ParticleID particleID(particle->getID());
     properties.copyFromParticle(*particle);
-    properties.setPosition(position * (float)TREE_SCALE);
-    properties.setVelocity(velocity * (float)TREE_SCALE);
-    _packetSender->queueParticleEditMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties);
 
-    // change the local particle too...
-    particle->setPosition(position);
-    particle->setVelocity(velocity);
+    properties.setPosition(particle->getPosition() * (float)TREE_SCALE);
+    properties.setVelocity(particle->getVelocity() * (float)TREE_SCALE);
+    _packetSender->queueParticleEditMessage(PacketTypeParticleAddOrEdit, particleID, properties);
 }
 
 
diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h
index cf52d01a7a..c525d3ddfc 100644
--- a/libraries/particles/src/ParticleCollisionSystem.h
+++ b/libraries/particles/src/ParticleCollisionSystem.h
@@ -16,6 +16,7 @@
 #include 
 #include 
 
+#include 
 #include 
 #include 
 #include 
@@ -30,35 +31,41 @@ class VoxelTree;
 
 const glm::vec3 NO_ADDED_VELOCITY = glm::vec3(0);
 
-class ParticleCollisionSystem {
+class ParticleCollisionSystem : public QObject {
+Q_OBJECT
 public:
     ParticleCollisionSystem(ParticleEditPacketSender* packetSender = NULL, ParticleTree* particles = NULL, 
-                                VoxelTree* voxels = NULL, 
-                                AbstractAudioInterface* audio = NULL,
-                                AvatarData* selfAvatar = NULL);
+                                VoxelTree* voxels = NULL, AbstractAudioInterface* audio = NULL, 
+                                AvatarHashMap* avatars = NULL);
 
     void init(ParticleEditPacketSender* packetSender, ParticleTree* particles, VoxelTree* voxels, 
-                                AbstractAudioInterface* audio = NULL, AvatarData* selfAvatar = NULL);
+                                AbstractAudioInterface* audio = NULL, AvatarHashMap* _avatars = NULL);
                                 
     ~ParticleCollisionSystem();
 
     void update();
+
     void checkParticle(Particle* particle);
     void updateCollisionWithVoxels(Particle* particle);
     void updateCollisionWithParticles(Particle* particle);
     void updateCollisionWithAvatars(Particle* particle);
-    void applyHardCollision(Particle* particle, float elasticity, float damping, const CollisionInfo& collisionInfo);
+    void queueParticlePropertiesUpdate(Particle* particle);
     void updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency);
 
+signals:
+    void particleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel);
+    void particleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB);
+
 private:
     static bool updateOperation(OctreeElement* element, void* extraData);
-
+    void emitGlobalParticleCollisionWithVoxel(Particle* particle, VoxelDetail* voxelDetails);
+    void emitGlobalParticleCollisionWithParticle(Particle* particleA, Particle* particleB);
 
     ParticleEditPacketSender* _packetSender;
     ParticleTree* _particles;
     VoxelTree* _voxels;
     AbstractAudioInterface* _audio;
-    AvatarData* _selfAvatar;
+    AvatarHashMap* _avatars;
 };
 
 #endif /* defined(__hifi__ParticleCollisionSystem__) */
diff --git a/libraries/particles/src/ParticleEditPacketSender.cpp b/libraries/particles/src/ParticleEditPacketSender.cpp
index 3206a0d200..f06b19aa2d 100644
--- a/libraries/particles/src/ParticleEditPacketSender.cpp
+++ b/libraries/particles/src/ParticleEditPacketSender.cpp
@@ -16,7 +16,7 @@
 #include "Particle.h"
 
 
-void ParticleEditPacketSender::sendEditParticleMessage(PACKET_TYPE type, ParticleID particleID, const ParticleProperties& properties) {
+void ParticleEditPacketSender::sendEditParticleMessage(PacketType type, ParticleID particleID, const ParticleProperties& properties) {
     // allows app to disable sending if for example voxels have been disabled
     if (!_shouldSend) {
         return; // bail early
@@ -42,7 +42,7 @@ void ParticleEditPacketSender::adjustEditPacketForClockSkew(unsigned char* codeC
 }
 
 
-void ParticleEditPacketSender::queueParticleEditMessage(PACKET_TYPE type, ParticleID particleID, 
+void ParticleEditPacketSender::queueParticleEditMessage(PacketType type, ParticleID particleID, 
                                                                 const ParticleProperties& properties) {
     if (!_shouldSend) {
         return; // bail early
diff --git a/libraries/particles/src/ParticleEditPacketSender.h b/libraries/particles/src/ParticleEditPacketSender.h
index 51f0fd9b96..3169c5629d 100644
--- a/libraries/particles/src/ParticleEditPacketSender.h
+++ b/libraries/particles/src/ParticleEditPacketSender.h
@@ -20,16 +20,16 @@ class ParticleEditPacketSender :  public OctreeEditPacketSender {
 public:
     /// Send particle add message immediately
     /// NOTE: ParticleProperties assumes that all distances are in meter units
-    void sendEditParticleMessage(PACKET_TYPE type, ParticleID particleID, const ParticleProperties& properties);
+    void sendEditParticleMessage(PacketType type, ParticleID particleID, const ParticleProperties& properties);
 
     /// Queues an array of several voxel edit messages. Will potentially send a pending multi-command packet. Determines
     /// which voxel-server node or nodes the packet should be sent to. Can be called even before voxel servers are known, in
     /// which case up to MaxPendingMessages will be buffered and processed when voxel servers are known.
     /// NOTE: ParticleProperties assumes that all distances are in meter units
-    void queueParticleEditMessage(PACKET_TYPE type, ParticleID particleID, const ParticleProperties& properties);
+    void queueParticleEditMessage(PacketType type, ParticleID particleID, const ParticleProperties& properties);
 
     // My server type is the particle server
-    virtual unsigned char getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
+    virtual unsigned char getMyNodeType() const { return NodeType::ParticleServer; }
     virtual void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew);
 };
 #endif // __shared__ParticleEditPacketSender__
diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp
index 7e83c56a42..51150722d0 100644
--- a/libraries/particles/src/ParticleTree.cpp
+++ b/libraries/particles/src/ParticleTree.cpp
@@ -19,14 +19,15 @@ ParticleTreeElement* ParticleTree::createNewElement(unsigned char * octalCode) {
     return newElement;
 }
 
-bool ParticleTree::handlesEditPacketType(PACKET_TYPE packetType) const {
+bool ParticleTree::handlesEditPacketType(PacketType packetType) const {
     // we handle these types of "edit" packets
     switch (packetType) {
-        case PACKET_TYPE_PARTICLE_ADD_OR_EDIT:
-        case PACKET_TYPE_PARTICLE_ERASE:
+        case PacketTypeParticleAddOrEdit:
+        case PacketTypeParticleErase:
             return true;
+        default:
+            return false;
     }
-    return false;
 }
 
 class FindAndDeleteParticlesArgs {
@@ -81,8 +82,8 @@ public:
 bool ParticleTree::findAndUpdateOperation(OctreeElement* element, void* extraData) {
     FindAndUpdateParticleArgs* args = static_cast(extraData);
     ParticleTreeElement* particleTreeElement = static_cast(element);
-    if (particleTreeElement->containsParticle(args->searchParticle)) {
-        particleTreeElement->updateParticle(args->searchParticle);
+    // Note: updateParticle() will only operate on correctly found particles
+    if (particleTreeElement->updateParticle(args->searchParticle)) {
         args->found = true;
         return false; // stop searching
     }
@@ -106,6 +107,119 @@ void ParticleTree::storeParticle(const Particle& particle, Node* senderNode) {
     _isDirty = true;
 }
 
+class FindAndUpdateParticleWithIDandPropertiesArgs {
+public:
+    const ParticleID& particleID;
+    const ParticleProperties& properties;
+    bool found;
+};
+
+bool ParticleTree::findAndUpdateWithIDandPropertiesOperation(OctreeElement* element, void* extraData) {
+    FindAndUpdateParticleWithIDandPropertiesArgs* args = static_cast(extraData);
+    ParticleTreeElement* particleTreeElement = static_cast(element);
+    // Note: updateParticle() will only operate on correctly found particles
+    if (particleTreeElement->updateParticle(args->particleID, args->properties)) {
+        args->found = true;
+        return false; // stop searching
+    }
+
+    // if we've found our particle stop searching
+    if (args->found) {
+        return false;
+    }
+
+    return true;
+}
+
+void ParticleTree::updateParticle(const ParticleID& particleID, const ParticleProperties& properties) {
+    // First, look for the existing particle in the tree..
+    FindAndUpdateParticleWithIDandPropertiesArgs args = { particleID, properties, false };
+    recurseTreeWithOperation(findAndUpdateWithIDandPropertiesOperation, &args);
+    // if we found it in the tree, then mark the tree as dirty
+    if (args.found) {
+        _isDirty = true;
+    }
+}
+
+void ParticleTree::addParticle(const ParticleID& particleID, const ParticleProperties& properties) {
+    // This only operates on locally created particles
+    if (particleID.isKnownID) {
+        return; // not allowed
+    }
+    Particle particle(particleID, properties);
+    glm::vec3 position = particle.getPosition();
+    float size = std::max(MINIMUM_PARTICLE_ELEMENT_SIZE, particle.getRadius());
+    ParticleTreeElement* element = (ParticleTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size);
+
+    element->storeParticle(particle);
+    
+    _isDirty = true;
+}
+
+void ParticleTree::deleteParticle(const ParticleID& particleID) {
+    if (particleID.isKnownID) {
+        FindAndDeleteParticlesArgs args;
+        args._idsToDelete.push_back(particleID.id);
+        recurseTreeWithOperation(findAndDeleteOperation, &args);
+    }
+}
+
+// scans the tree and handles mapping locally created particles to know IDs.
+// in the event that this tree is also viewing the scene, then we need to also
+// search the tree to make sure we don't have a duplicate particle from the viewing
+// operation.
+bool ParticleTree::findAndUpdateParticleIDOperation(OctreeElement* element, void* extraData) {
+    bool keepSearching = true;
+
+    FindAndUpdateParticleIDArgs* args = static_cast(extraData);
+    ParticleTreeElement* particleTreeElement = static_cast(element);
+
+    // Note: updateParticleID() will only operate on correctly found particles
+    particleTreeElement->updateParticleID(args);
+
+    // if we've found and replaced both the creatorTokenID and the viewedParticle, then we
+    // can stop looking, otherwise we will keep looking    
+    if (args->creatorTokenFound && args->viewedParticleFound) {
+        keepSearching = false;
+    }
+    
+    return keepSearching;
+}
+
+void ParticleTree::handleAddParticleResponse(const QByteArray& packet) {
+    int numBytesPacketHeader = numBytesForPacketHeader(packet);
+    
+    const unsigned char* dataAt = reinterpret_cast(packet.data()) + numBytesPacketHeader;
+
+    uint32_t creatorTokenID;
+    memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID));
+    dataAt += sizeof(creatorTokenID);
+
+    uint32_t particleID;
+    memcpy(&particleID, dataAt, sizeof(particleID));
+    dataAt += sizeof(particleID);
+
+    // update particles in our tree
+    bool assumeParticleFound = !getIsViewing(); // if we're not a viewing tree, then we don't have to find the actual particle
+    FindAndUpdateParticleIDArgs args = { 
+        particleID, 
+        creatorTokenID, 
+        false, 
+        assumeParticleFound,
+        getIsViewing() 
+    };
+    
+    const bool wantDebug = false;
+    if (wantDebug) {
+        qDebug() << "looking for creatorTokenID=" << creatorTokenID << " particleID=" << particleID 
+                << " getIsViewing()=" << getIsViewing();
+    }
+    lockForWrite();
+    recurseTreeWithOperation(findAndUpdateParticleIDOperation, &args);
+    unlock();
+}
+
+
 class FindNearPointArgs {
 public:
     glm::vec3 position;
@@ -170,16 +284,14 @@ public:
 
 bool ParticleTree::findInSphereOperation(OctreeElement* element, void* extraData) {
     FindAllNearPointArgs* args = static_cast(extraData);
-    ParticleTreeElement* particleTreeElement = static_cast(element);
-
     glm::vec3 penetration;
-    bool sphereIntersection = particleTreeElement->getAABox().findSpherePenetration(args->position,
+    bool sphereIntersection = element->getAABox().findSpherePenetration(args->position,
                                                                     args->targetRadius, penetration);
 
-    // If this particleTreeElement contains the point, then search it...
+    // If this element contains the point, then search it...
     if (sphereIntersection) {
-        QVector moreParticles = particleTreeElement->getParticles(args->position, args->targetRadius);
-        args->particles << moreParticles;
+        ParticleTreeElement* particleTreeElement = static_cast(element);
+        particleTreeElement->getParticles(args->position, args->targetRadius, args->particles);
         return true; // keep searching in case children have closer particles
     }
 
@@ -187,13 +299,43 @@ bool ParticleTree::findInSphereOperation(OctreeElement* element, void* extraData
     return false;
 }
 
-QVector ParticleTree::findParticles(const glm::vec3& center, float radius) {
-    QVector result;
+void ParticleTree::findParticles(const glm::vec3& center, float radius, QVector& foundParticles) {
     FindAllNearPointArgs args = { center, radius };
     lockForRead();
     recurseTreeWithOperation(findInSphereOperation, &args);
     unlock();
-    return args.particles;
+    // swap the two lists of particle pointers instead of copy
+    foundParticles.swap(args.particles);
+}
+
+class FindParticlesInBoxArgs {
+public:
+    FindParticlesInBoxArgs(const AABox& box) 
+        : _box(box), _foundParticles() {
+    }
+
+    AABox _box;
+    QVector _foundParticles;
+};
+
+bool findInBoxForUpdateOperation(OctreeElement* element, void* extraData) {
+    FindParticlesInBoxArgs* args = static_cast< FindParticlesInBoxArgs*>(extraData);
+    const AABox& elementBox = element->getAABox();
+    if (elementBox.touches(args->_box)) {
+        ParticleTreeElement* particleTreeElement = static_cast(element);
+        particleTreeElement->getParticlesForUpdate(args->_box, args->_foundParticles);
+        return true;
+    }
+    return false;
+}
+
+void ParticleTree::findParticlesForUpdate(const AABox& box, QVector foundParticles) {
+    FindParticlesInBoxArgs args(box);
+    lockForRead();
+    recurseTreeWithOperation(findInBoxForUpdateOperation, &args);
+    unlock();
+    // swap the two lists of particle pointers instead of copy
+    foundParticles.swap(args._foundParticles);
 }
 
 class FindByIDArgs {
@@ -243,13 +385,13 @@ const Particle* ParticleTree::findParticleByID(uint32_t id, bool alreadyLocked)
 }
 
 
-int ParticleTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength,
-                    unsigned char* editData, int maxLength, Node* senderNode) {
+int ParticleTree::processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
+                    const unsigned char* editData, int maxLength, Node* senderNode) {
 
     int processedBytes = 0;
     // we handle these types of "edit" packets
     switch (packetType) {
-        case PACKET_TYPE_PARTICLE_ADD_OR_EDIT: {
+        case PacketTypeParticleAddOrEdit: {
             bool isValid;
             Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes, this, isValid);
             if (isValid) {
@@ -262,10 +404,14 @@ int ParticleTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* p
 
         // TODO: wire in support here for server to get PACKET_TYPE_PARTICLE_ERASE messages
         // instead of using PACKET_TYPE_PARTICLE_ADD_OR_EDIT messages to delete particles
-        case PACKET_TYPE_PARTICLE_ERASE: {
+        case PacketTypeParticleErase:
             processedBytes = 0;
-        } break;
+            break;
+        default:
+            processedBytes = 0;
+            break;
     }
+    
     return processedBytes;
 }
 
@@ -331,7 +477,7 @@ void ParticleTree::update() {
             storeParticle(args._movingParticles[i]);
         } else {
             uint32_t particleID = args._movingParticles[i].getID();
-            uint64_t deletedAt = usecTimestampNow();
+            quint64 deletedAt = usecTimestampNow();
             _recentlyDeletedParticlesLock.lockForWrite();
             _recentlyDeletedParticleIDs.insert(deletedAt, particleID);
             _recentlyDeletedParticlesLock.unlock();
@@ -343,12 +489,12 @@ void ParticleTree::update() {
 }
 
 
-bool ParticleTree::hasParticlesDeletedSince(uint64_t sinceTime) {
+bool ParticleTree::hasParticlesDeletedSince(quint64 sinceTime) {
     // we can probably leverage the ordered nature of QMultiMap to do this quickly...
     bool hasSomethingNewer = false;
 
     _recentlyDeletedParticlesLock.lockForRead();
-    QMultiMap::const_iterator iterator = _recentlyDeletedParticleIDs.constBegin();
+    QMultiMap::const_iterator iterator = _recentlyDeletedParticleIDs.constBegin();
     while (iterator != _recentlyDeletedParticleIDs.constEnd()) {
         //qDebug() << "considering... time/key:" << iterator.key();
         if (iterator.key() > sinceTime) {
@@ -362,13 +508,13 @@ bool ParticleTree::hasParticlesDeletedSince(uint64_t sinceTime) {
 }
 
 // sinceTime is an in/out parameter - it will be side effected with the last time sent out
-bool ParticleTree::encodeParticlesDeletedSince(uint64_t& sinceTime, unsigned char* outputBuffer, size_t maxLength,
+bool ParticleTree::encodeParticlesDeletedSince(quint64& sinceTime, unsigned char* outputBuffer, size_t maxLength,
                                                     size_t& outputLength) {
 
     bool hasMoreToSend = true;
 
     unsigned char* copyAt = outputBuffer;
-    size_t numBytesPacketHeader = populateTypeAndVersion(outputBuffer, PACKET_TYPE_PARTICLE_ERASE);
+    size_t numBytesPacketHeader = populatePacketHeader(reinterpret_cast(outputBuffer), PacketTypeParticleErase);
     copyAt += numBytesPacketHeader;
     outputLength = numBytesPacketHeader;
 
@@ -381,15 +527,13 @@ bool ParticleTree::encodeParticlesDeletedSince(uint64_t& sinceTime, unsigned cha
     // we keep a multi map of particle IDs to timestamps, we only want to include the particle IDs that have been
     // deleted since we last sent to this node
     _recentlyDeletedParticlesLock.lockForRead();
-    QMultiMap::const_iterator iterator = _recentlyDeletedParticleIDs.constBegin();
+    QMultiMap::const_iterator iterator = _recentlyDeletedParticleIDs.constBegin();
     while (iterator != _recentlyDeletedParticleIDs.constEnd()) {
         QList values = _recentlyDeletedParticleIDs.values(iterator.key());
         for (int valueItem = 0; valueItem < values.size(); ++valueItem) {
-            //qDebug() << "considering... " << iterator.key() << ": " << values.at(valueItem);
 
             // if the timestamp is more recent then out last sent time, include it
             if (iterator.key() > sinceTime) {
-                //qDebug() << "including... " << iterator.key() << ": " << values.at(valueItem);
                 uint32_t particleID = values.at(valueItem);
                 memcpy(copyAt, &particleID, sizeof(particleID));
                 copyAt += sizeof(particleID);
@@ -426,42 +570,40 @@ bool ParticleTree::encodeParticlesDeletedSince(uint64_t& sinceTime, unsigned cha
 }
 
 // called by the server when it knows all nodes have been sent deleted packets
-void ParticleTree::forgetParticlesDeletedBefore(uint64_t sinceTime) {
+
+void ParticleTree::forgetParticlesDeletedBefore(quint64 sinceTime) {
     //qDebug() << "forgetParticlesDeletedBefore()";
-    QSet keysToRemove;
+    QSet keysToRemove;
 
     _recentlyDeletedParticlesLock.lockForWrite();
-    QMultiMap::iterator iterator = _recentlyDeletedParticleIDs.begin();
-    // First find all the keys in the map that are older and need to be deleted    
+    QMultiMap::iterator iterator = _recentlyDeletedParticleIDs.begin();
+
+    // First find all the keys in the map that are older and need to be deleted
     while (iterator != _recentlyDeletedParticleIDs.end()) {
-        //qDebug() << "considering... time/key:" << iterator.key();
         if (iterator.key() <= sinceTime) {
-            //qDebug() << "YES older... time/key:" << iterator.key();
             keysToRemove << iterator.key();
         }
         ++iterator;
     }
 
-    // Now run through the keysToRemove and remove them    
-    foreach (uint64_t value, keysToRemove) {
+    // Now run through the keysToRemove and remove them
+    foreach (quint64 value, keysToRemove) {
         //qDebug() << "removing the key, _recentlyDeletedParticleIDs.remove(value); time/key:" << value;
         _recentlyDeletedParticleIDs.remove(value);
     }
     
     _recentlyDeletedParticlesLock.unlock();
-    //qDebug() << "DONE forgetParticlesDeletedBefore()";
 }
 
 
 void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr,
         Node* sourceNode) {
-    //qDebug() << "ParticleTree::processEraseMessage()...";
 
     const unsigned char* packetData = (const unsigned char*)dataByteArray.constData();
     const unsigned char* dataAt = packetData;
     size_t packetLength = dataByteArray.size();
 
-    size_t numBytesPacketHeader = numBytesForPacketHeader(packetData);
+    size_t numBytesPacketHeader = numBytesForPacketHeader(dataByteArray);
     size_t processedBytes = numBytesPacketHeader;
     dataAt += numBytesPacketHeader;
 
@@ -470,14 +612,11 @@ void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const Hi
     dataAt += sizeof(numberOfIds);
     processedBytes += sizeof(numberOfIds);
 
-    //qDebug() << "got erase message for numberOfIds:" << numberOfIds;
-
     if (numberOfIds > 0) {
         FindAndDeleteParticlesArgs args;
 
         for (size_t i = 0; i < numberOfIds; i++) {
             if (processedBytes + sizeof(uint32_t) > packetLength) {
-                //qDebug() << "bailing?? processedBytes:" << processedBytes << " packetLength:" << packetLength;
                 break; // bail to prevent buffer overflow
             }
 
@@ -486,12 +625,10 @@ void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const Hi
             dataAt += sizeof(particleID);
             processedBytes += sizeof(particleID);
 
-            //qDebug() << "got erase message for particleID:" << particleID;
             args._idsToDelete.push_back(particleID);
         }
 
         // calling recurse to actually delete the particles
-        //qDebug() << "calling recurse to actually delete the particles";
         recurseTreeWithOperation(findAndDeleteOperation, &args);
     }
 }
diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h
index 5fa25b2c2d..e3b7906778 100644
--- a/libraries/particles/src/ParticleTree.h
+++ b/libraries/particles/src/ParticleTree.h
@@ -32,37 +32,55 @@ public:
     // These methods will allow the OctreeServer to send your tree inbound edit packets of your
     // own definition. Implement these to allow your octree based server to support editing
     virtual bool getWantSVOfileVersions() const { return true; }
-    virtual PACKET_TYPE expectedDataPacketType() const { return PACKET_TYPE_PARTICLE_DATA; }
-    virtual bool handlesEditPacketType(PACKET_TYPE packetType) const;
-    virtual int processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength,
-                    unsigned char* editData, int maxLength, Node* senderNode);
+    virtual PacketType expectedDataPacketType() const { return PacketTypeParticleData; }
+    virtual bool handlesEditPacketType(PacketType packetType) const;
+    virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
+                    const unsigned char* editData, int maxLength, Node* senderNode);
 
     virtual void update();
 
     void storeParticle(const Particle& particle, Node* senderNode = NULL);
+    void updateParticle(const ParticleID& particleID, const ParticleProperties& properties);
+    void addParticle(const ParticleID& particleID, const ParticleProperties& properties);
+    void deleteParticle(const ParticleID& particleID);
     const Particle* findClosestParticle(glm::vec3 position, float targetRadius);
     const Particle* findParticleByID(uint32_t id, bool alreadyLocked = false);
-    QVector findParticles(const glm::vec3& center, float radius);
+
+    /// finds all particles that touch a sphere
+    /// \param center the center of the sphere
+    /// \param radius the radius of the sphere
+    /// \param foundParticles[out] vector of const Particle*
+    /// \remark Side effect: any initial contents in foundParticles will be lost
+    void findParticles(const glm::vec3& center, float radius, QVector& foundParticles);
+
+    /// finds all particles that touch a box
+    /// \param box the query box
+    /// \param foundParticles[out] vector of non-const Particle*
+    /// \remark Side effect: any initial contents in particles will be lost
+    void findParticlesForUpdate(const AABox& box, QVector foundParticles);
 
     void addNewlyCreatedHook(NewlyCreatedParticleHook* hook);
     void removeNewlyCreatedHook(NewlyCreatedParticleHook* hook);
 
     bool hasAnyDeletedParticles() const { return _recentlyDeletedParticleIDs.size() > 0; }
-    bool hasParticlesDeletedSince(uint64_t sinceTime);
-    bool encodeParticlesDeletedSince(uint64_t& sinceTime, unsigned char* packetData, size_t maxLength, size_t& outputLength);
-    void forgetParticlesDeletedBefore(uint64_t sinceTime);
+    bool hasParticlesDeletedSince(quint64 sinceTime);
+    bool encodeParticlesDeletedSince(quint64& sinceTime, unsigned char* packetData, size_t maxLength, size_t& outputLength);
+    void forgetParticlesDeletedBefore(quint64 sinceTime);
 
     void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode);
+    void handleAddParticleResponse(const QByteArray& packet);
 
 private:
 
     static bool updateOperation(OctreeElement* element, void* extraData);
     static bool findAndUpdateOperation(OctreeElement* element, void* extraData);
+    static bool findAndUpdateWithIDandPropertiesOperation(OctreeElement* element, void* extraData);
     static bool findNearPointOperation(OctreeElement* element, void* extraData);
     static bool findInSphereOperation(OctreeElement* element, void* extraData);
     static bool pruneOperation(OctreeElement* element, void* extraData);
     static bool findByIDOperation(OctreeElement* element, void* extraData);
     static bool findAndDeleteOperation(OctreeElement* element, void* extraData);
+    static bool findAndUpdateParticleIDOperation(OctreeElement* element, void* extraData);
 
     void notifyNewlyCreatedParticle(const Particle& newParticle, Node* senderNode);
 
@@ -71,7 +89,7 @@ private:
 
 
     QReadWriteLock _recentlyDeletedParticlesLock;
-    QMultiMap _recentlyDeletedParticleIDs;
+    QMultiMap _recentlyDeletedParticleIDs;
 };
 
 #endif /* defined(__hifi__ParticleTree__) */
diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp
index 1454eadcc9..48e619da59 100644
--- a/libraries/particles/src/ParticleTreeElement.cpp
+++ b/libraries/particles/src/ParticleTreeElement.cpp
@@ -125,18 +125,6 @@ bool ParticleTreeElement::findSpherePenetration(const glm::vec3& center, float r
     return false;
 }
 
-bool ParticleTreeElement::containsParticle(const Particle& particle) const {
-    // TODO: remove this method and force callers to use getParticleWithID() instead
-    uint16_t numberOfParticles = _particles->size();
-    uint32_t particleID = particle.getID();
-    for (uint16_t i = 0; i < numberOfParticles; i++) {
-        if ((*_particles)[i].getID() == particleID) {
-            return true;
-        }
-    }
-    return false;
-}
-
 bool ParticleTreeElement::updateParticle(const Particle& particle) {
     // NOTE: this method must first lookup the particle by ID, hence it is O(N)
     // and "particle is not found" is worst-case (full N) but maybe we don't care?
@@ -172,6 +160,63 @@ bool ParticleTreeElement::updateParticle(const Particle& particle) {
     return false;
 }
 
+bool ParticleTreeElement::updateParticle(const ParticleID& particleID, const ParticleProperties& properties) {
+    uint16_t numberOfParticles = _particles->size();
+    for (uint16_t i = 0; i < numberOfParticles; i++) {
+        // note: unlike storeParticle() which is called from inbound packets, this is only called by local editors
+        // and therefore we can be confident that this change is higher priority and should be honored
+        Particle& thisParticle = (*_particles)[i];
+        
+        bool found = false;
+        if (particleID.isKnownID) {
+            found = thisParticle.getID() == particleID.id;
+        } else {
+            found = thisParticle.getCreatorTokenID() == particleID.creatorTokenID;
+        }
+        if (found) {
+            thisParticle.setProperties(properties);
+
+            const bool wantDebug = false;
+            if (wantDebug) {
+                uint64_t now = usecTimestampNow();
+                int elapsed = now - thisParticle.getLastEdited();
+
+                qDebug() << "ParticleTreeElement::updateParticle() AFTER update... edited AGO=" << elapsed <<
+                        "now=" << now << " thisParticle.getLastEdited()=" << thisParticle.getLastEdited();
+            }                
+            return true;
+        }
+    }
+    return false;
+}
+
+void ParticleTreeElement::updateParticleID(FindAndUpdateParticleIDArgs* args) {
+    uint16_t numberOfParticles = _particles->size();
+    for (uint16_t i = 0; i < numberOfParticles; i++) {
+        Particle& thisParticle = (*_particles)[i];
+        
+        if (!args->creatorTokenFound) {
+            // first, we're looking for matching creatorTokenIDs, if we find that, then we fix it to know the actual ID
+            if (thisParticle.getCreatorTokenID() == args->creatorTokenID) {
+                thisParticle.setID(args->particleID);
+                args->creatorTokenFound = true;
+            }
+        }
+        
+        // if we're in an isViewing tree, we also need to look for an kill any viewed particles
+        if (!args->viewedParticleFound && args->isViewing) {
+            if (thisParticle.getCreatorTokenID() == UNKNOWN_TOKEN && thisParticle.getID() == args->particleID) {
+                _particles->removeAt(i); // remove the particle at this index
+                numberOfParticles--; // this means we have 1 fewer particle in this list
+                i--; // and we actually want to back up i as well.
+                args->viewedParticleFound = true;
+            }
+        }
+    }
+}
+
+
+
 const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) const {
     const Particle* closestParticle = NULL;
     float closestParticleDistance = FLT_MAX;
@@ -185,21 +230,34 @@ const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) cons
     return closestParticle;
 }
 
-QVector ParticleTreeElement::getParticles(glm::vec3 searchPosition, float searchRadius) const {
-    QVector results;
+void ParticleTreeElement::getParticles(const glm::vec3& searchPosition, float searchRadius, QVector& foundParticles) const {
     uint16_t numberOfParticles = _particles->size();
     for (uint16_t i = 0; i < numberOfParticles; i++) {
         const Particle* particle = &(*_particles)[i];
         glm::vec3 particlePosition = particle->getPosition();
-        float particleRadius = particle->getRadius();
-        glm::vec3 penetration;
-
-        // check to see that the particle (penetrator) penetrates the search area
-        if (findSphereSpherePenetration(particlePosition, particleRadius, searchPosition, searchRadius, penetration)) {
-            results << particle;
+        float distance = glm::length(particle->getPosition() - searchPosition);
+        if (distance < searchRadius + particle->getRadius()) {
+            foundParticles.push_back(particle);
         }
     }
-    return results;
+}
+
+void ParticleTreeElement::getParticlesForUpdate(const AABox& box, QVector& foundParticles) {
+    QList::iterator particleItr = _particles->begin();
+    QList::iterator particleEnd = _particles->end();
+    AABox particleBox;
+    while(particleItr != particleEnd) {
+        Particle* particle = &(*particleItr);
+        float radius = particle->getRadius();
+        // NOTE: we actually do box-box collision queries here, which is sloppy but good enough for now
+        // TODO: decide whether to replace particleBox-box query with sphere-box (requires a square root
+        // but will be slightly more accurate).
+        particleBox.setBox(particle->getPosition() - glm::vec3(radius), 2.f * radius);
+        if (particleBox.touches(_box)) {
+            foundParticles.push_back(particle);
+        }
+        ++particleItr;
+    }
 }
 
 const Particle* ParticleTreeElement::getParticleWithID(uint32_t id) const {
@@ -228,8 +286,6 @@ bool ParticleTreeElement::removeParticleWithID(uint32_t id) {
     return foundParticle;
 }
 
-
-
 int ParticleTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
             ReadBitstreamToTreeParams& args) {
 
diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h
index 2705ad4292..59a03f7b41 100644
--- a/libraries/particles/src/ParticleTreeElement.h
+++ b/libraries/particles/src/ParticleTreeElement.h
@@ -26,6 +26,17 @@ public:
     QList _movingParticles;
 };
 
+class FindAndUpdateParticleIDArgs {
+public:
+    uint32_t particleID;
+    uint32_t creatorTokenID;
+    bool creatorTokenFound;
+    bool viewedParticleFound;
+    bool isViewing;
+};
+
+
+
 class ParticleTreeElement : public OctreeElement {
     friend class ParticleTree; // to allow createElement to new us...
 
@@ -86,15 +97,27 @@ public:
     void update(ParticleTreeUpdateArgs& args);
     void setTree(ParticleTree* tree) { _myTree = tree; }
 
-    bool containsParticle(const Particle& particle) const;
     bool updateParticle(const Particle& particle);
+    bool updateParticle(const ParticleID& particleID, const ParticleProperties& properties);
+    void updateParticleID(FindAndUpdateParticleIDArgs* args);
+
     const Particle* getClosestParticle(glm::vec3 position) const;
-    QVector getParticles(glm::vec3 position, float radius) const;
+
+    /// finds all particles that touch a sphere
+    /// \param position the center of the query sphere
+    /// \param radius the radius of the query sphere
+    /// \param particles[out] vector of const Particle*
+    void getParticles(const glm::vec3& position, float radius, QVector& foundParticles) const;
+
+    /// finds all particles that touch a box
+    /// \param box the query box
+    /// \param particles[out] vector of non-const Particle*
+    void getParticlesForUpdate(const AABox& box, QVector& foundParticles);
+
     const Particle* getParticleWithID(uint32_t id) const;
 
     bool removeParticleWithID(uint32_t id);
 
-
 protected:
     virtual void init(unsigned char * octalCode);
 
diff --git a/libraries/particles/src/ParticlesScriptingInterface.cpp b/libraries/particles/src/ParticlesScriptingInterface.cpp
index 0641cdba7c..a25dde1b9e 100644
--- a/libraries/particles/src/ParticlesScriptingInterface.cpp
+++ b/libraries/particles/src/ParticlesScriptingInterface.cpp
@@ -16,7 +16,7 @@ ParticlesScriptingInterface::ParticlesScriptingInterface() :
 }
 
 
-void ParticlesScriptingInterface::queueParticleMessage(PACKET_TYPE packetType,
+void ParticlesScriptingInterface::queueParticleMessage(PacketType packetType,
         ParticleID particleID, const ParticleProperties& properties) {
     getParticlePacketSender()->queueParticleEditMessage(packetType, particleID, properties);
 }
@@ -29,13 +29,21 @@ ParticleID ParticlesScriptingInterface::addParticle(const ParticleProperties& pr
     ParticleID id(NEW_PARTICLE, creatorTokenID, false );
 
     // queue the packet
-    queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, id, properties);
+    queueParticleMessage(PacketTypeParticleAddOrEdit, id, properties);
+
+    // If we have a local particle tree set, then also update it.
+    if (_particleTree) {
+        _particleTree->lockForWrite();
+        _particleTree->addParticle(id, properties);
+        _particleTree->unlock();
+    }
 
     return id;
 }
 
 ParticleID ParticlesScriptingInterface::identifyParticle(ParticleID particleID) {
     uint32_t actualID = particleID.id;
+
     if (!particleID.isKnownID) {
         actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID);
         if (actualID == UNKNOWN_PARTICLE_ID) {
@@ -57,8 +65,14 @@ ParticleProperties ParticlesScriptingInterface::getParticleProperties(ParticleID
         return results;
     }
     if (_particleTree) {
-        const Particle* particle = _particleTree->findParticleByID(identity.id);
-        results.copyFromParticle(*particle);
+        _particleTree->lockForRead();
+        const Particle* particle = _particleTree->findParticleByID(identity.id, true);
+        if (particle) {
+            results.copyFromParticle(*particle);
+        } else {
+            results.setIsUnknownID();
+        }
+        _particleTree->unlock();
     }
     
     return results;
@@ -68,45 +82,34 @@ ParticleProperties ParticlesScriptingInterface::getParticleProperties(ParticleID
 
 ParticleID ParticlesScriptingInterface::editParticle(ParticleID particleID, const ParticleProperties& properties) {
     uint32_t actualID = particleID.id;
+    
+    // if the particle is unknown, attempt to look it up
     if (!particleID.isKnownID) {
         actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID);
-        if (actualID == UNKNOWN_PARTICLE_ID) {
-            return particleID; // bailing early
-        }
     }
 
-    particleID.id = actualID;
-    particleID.isKnownID = true;
-    //qDebug() << "ParticlesScriptingInterface::editParticle()... FOUND IT!!! actualID=" << actualID;
-
-    bool wantDebugging = false;
-    if (wantDebugging) {
-        uint16_t containsBits = properties.getChangedBits();
-        qDebug() << "ParticlesScriptingInterface::editParticle()... containsBits=" << containsBits;
-        if ((containsBits & PACKET_CONTAINS_POSITION) == PACKET_CONTAINS_POSITION) {
-            qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getPositon()=" 
-                << properties.getPosition().x << ", "
-                << properties.getPosition().y << ", "
-                << properties.getPosition().z << "...";
-        }
-        if ((containsBits & PACKET_CONTAINS_VELOCITY) == PACKET_CONTAINS_VELOCITY) {
-            qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getVelocity()=" 
-                << properties.getVelocity().x << ", "
-                << properties.getVelocity().y << ", "
-                << properties.getVelocity().z << "...";
-        }
-        if ((containsBits & PACKET_CONTAINS_INHAND) == PACKET_CONTAINS_INHAND) {
-            qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getInHand()=" << properties.getInHand();
-        }
+    // if at this point, we know the id, send the update to the particle server
+    if (actualID != UNKNOWN_PARTICLE_ID) {
+        particleID.id = actualID;
+        particleID.isKnownID = true;
+        queueParticleMessage(PacketTypeParticleAddOrEdit, particleID, properties);
     }
-    queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties);
+    
+    // If we have a local particle tree set, then also update it. We can do this even if we don't know
+    // the actual id, because we can edit out local particles just with creatorTokenID
+    if (_particleTree) {
+        _particleTree->lockForWrite();
+        _particleTree->updateParticle(particleID, properties);
+        _particleTree->unlock();
+    }
+    
     return particleID;
 }
 
 
-// TODO: This deleteParticle() method uses the PACKET_TYPE_PARTICLE_ADD_OR_EDIT message to send
+// TODO: This deleteParticle() method uses the PacketType_PARTICLE_ADD_OR_EDIT message to send
 // a changed particle with a shouldDie() property set to true. This works and is currently the only
-// way to tell the particle server to delete a particle. But we should change this to use the PACKET_TYPE_PARTICLE_ERASE
+// way to tell the particle server to delete a particle. But we should change this to use the PacketType_PARTICLE_ERASE
 // message which takes a list of particle id's to delete.
 void ParticlesScriptingInterface::deleteParticle(ParticleID particleID) {
 
@@ -115,29 +118,34 @@ void ParticlesScriptingInterface::deleteParticle(ParticleID particleID) {
     properties.setShouldDie(true);
 
     uint32_t actualID = particleID.id;
+    
+    // if the particle is unknown, attempt to look it up
     if (!particleID.isKnownID) {
         actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID);
-
-        // hmmm... we kind of want to bail if someone attempts to edit an unknown
-        if (actualID == UNKNOWN_PARTICLE_ID) {
-            //qDebug() << "ParticlesScriptingInterface::deleteParticle(), bailing - unknown particle...";
-            return; // bailing early
-        }
     }
 
-    particleID.id = actualID;
-    particleID.isKnownID = true;
+    // if at this point, we know the id, send the update to the particle server
+    if (actualID != UNKNOWN_PARTICLE_ID) {
+        particleID.id = actualID;
+        particleID.isKnownID = true;
+        queueParticleMessage(PacketTypeParticleAddOrEdit, particleID, properties);
+    }
 
-    //qDebug() << "ParticlesScriptingInterface::deleteParticle(), queueParticleMessage......";
-    queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties);
+    // If we have a local particle tree set, then also update it.
+    if (_particleTree) {
+        _particleTree->lockForWrite();
+        _particleTree->deleteParticle(particleID);
+        _particleTree->unlock();
+    }
 }
 
 ParticleID ParticlesScriptingInterface::findClosestParticle(const glm::vec3& center, float radius) const {
     ParticleID result(UNKNOWN_PARTICLE_ID, UNKNOWN_TOKEN, false);
     if (_particleTree) {
+        _particleTree->lockForRead();
         const Particle* closestParticle = _particleTree->findClosestParticle(center/(float)TREE_SCALE, 
                                                                                 radius/(float)TREE_SCALE);
-
+        _particleTree->unlock();
         if (closestParticle) {
             result.id = closestParticle->getID();
             result.isKnownID = true;
@@ -150,7 +158,10 @@ ParticleID ParticlesScriptingInterface::findClosestParticle(const glm::vec3& cen
 QVector ParticlesScriptingInterface::findParticles(const glm::vec3& center, float radius) const {
     QVector result;
     if (_particleTree) {
-        QVector particles = _particleTree->findParticles(center/(float)TREE_SCALE, radius/(float)TREE_SCALE);
+        _particleTree->lockForRead();
+        QVector particles;
+        _particleTree->findParticles(center/(float)TREE_SCALE, radius/(float)TREE_SCALE, particles);
+        _particleTree->unlock();
 
         foreach (const Particle* particle, particles) {
             ParticleID thisParticleID(particle->getID(), UNKNOWN_TOKEN, true);
diff --git a/libraries/particles/src/ParticlesScriptingInterface.h b/libraries/particles/src/ParticlesScriptingInterface.h
index 9a3ceb51af..729561571e 100644
--- a/libraries/particles/src/ParticlesScriptingInterface.h
+++ b/libraries/particles/src/ParticlesScriptingInterface.h
@@ -21,7 +21,7 @@ public:
     ParticlesScriptingInterface();
     
     ParticleEditPacketSender* getParticlePacketSender() const { return (ParticleEditPacketSender*)getPacketSender(); }
-    virtual NODE_TYPE getServerNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
+    virtual NodeType_t getServerNodeType() const { return NodeType::ParticleServer; }
     virtual OctreeEditPacketSender* createPacketSender() { return new ParticleEditPacketSender(); }
 
     void setParticleTree(ParticleTree* particleTree) { _particleTree = particleTree; }
@@ -53,10 +53,22 @@ public slots:
     /// finds particles within the search sphere specified by the center point and radius
     /// this function will not find any particles in script engine contexts which don't have access to particles
     QVector findParticles(const glm::vec3& center, float radius) const;
+
+    /// inbound slots for external collision systems
+    void forwardParticleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel) {
+        emit particleCollisionWithVoxel(particleID, voxel);
+    }
+
+    void forwardParticleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB) {
+        emit particleCollisionWithParticle(idA, idB);
+    }
     
+signals:
+    void particleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel);
+    void particleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB);
 
 private:
-    void queueParticleMessage(PACKET_TYPE packetType, ParticleID particleID, const ParticleProperties& properties);
+    void queueParticleMessage(PacketType packetType, ParticleID particleID, const ParticleProperties& properties);
 
     uint32_t _nextCreatorTokenID;
     ParticleTree* _particleTree;
diff --git a/libraries/script-engine/src/AbstractControllerScriptingInterface.h b/libraries/script-engine/src/AbstractControllerScriptingInterface.h
index 5c791af0a4..4fad5f6edc 100644
--- a/libraries/script-engine/src/AbstractControllerScriptingInterface.h
+++ b/libraries/script-engine/src/AbstractControllerScriptingInterface.h
@@ -10,8 +10,12 @@
 #define __hifi__AbstractControllerScriptingInterface__
 
 #include 
+
 #include 
 
+#include "EventTypes.h"
+
+
 /// handles scripting of input controller commands from JS
 class AbstractControllerScriptingInterface : public QObject {
     Q_OBJECT
@@ -33,6 +37,34 @@ public slots:
     virtual glm::vec3 getSpatialControlPosition(int controlIndex) const = 0;
     virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const = 0;
     virtual glm::vec3 getSpatialControlNormal(int controlIndex) const = 0;
+
+    virtual void captureKeyEvents(const KeyEvent& event) = 0;
+    virtual void releaseKeyEvents(const KeyEvent& event) = 0;
+
+    virtual void captureMouseEvents() = 0;
+    virtual void releaseMouseEvents() = 0;
+
+    virtual void captureTouchEvents() = 0;
+    virtual void releaseTouchEvents() = 0;
+
+    virtual void captureWheelEvents() = 0;
+    virtual void releaseWheelEvents() = 0;
+
+
+signals:
+    void keyPressEvent(const KeyEvent& event);
+    void keyReleaseEvent(const KeyEvent& event);
+
+    void mouseMoveEvent(const MouseEvent& event);
+    void mousePressEvent(const MouseEvent& event);
+    void mouseReleaseEvent(const MouseEvent& event);
+
+    void touchBeginEvent(const TouchEvent& event);
+    void touchEndEvent(const TouchEvent& event);
+    void touchUpdateEvent(const TouchEvent& event);
+    
+    void wheelEvent(const WheelEvent& event);
+
 };
 
 #endif /* defined(__hifi__AbstractControllerScriptingInterface__) */
diff --git a/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp
new file mode 100644
index 0000000000..ff27282b73
--- /dev/null
+++ b/libraries/script-engine/src/EventTypes.cpp
@@ -0,0 +1,258 @@
+//
+//  EventTypes.cpp
+//  hifi
+//
+//  Created by Brad Hefta-Gaub on 1/28/14.
+//  Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
+//
+//  Used to register meta-types with Qt for very various event types so that they can be exposed to our
+//  scripting engine
+
+#include 
+#include "EventTypes.h"
+
+
+KeyEvent::KeyEvent() {
+    key = 0;
+    text = QString("");
+    isShifted = false;
+    isMeta = false;
+    isControl = false;
+    isValid = false;
+}
+
+
+KeyEvent::KeyEvent(const QKeyEvent& event) {
+    key = event.key();
+    text = event.text();
+    isShifted = event.modifiers().testFlag(Qt::ShiftModifier);
+    isMeta = event.modifiers().testFlag(Qt::MetaModifier);
+    isControl = event.modifiers().testFlag(Qt::ControlModifier);
+    isAlt = event.modifiers().testFlag(Qt::AltModifier);
+    isKeypad = event.modifiers().testFlag(Qt::KeypadModifier);
+    isValid = true;
+
+    // handle special text for special characters...
+    if (key == Qt::Key_F1) {
+        text = "F1";
+    } else if (key == Qt::Key_F2) {
+        text = "F2";
+    } else if (key == Qt::Key_F3) {
+        text = "F3";
+    } else if (key == Qt::Key_F4) {
+        text = "F4";
+    } else if (key == Qt::Key_F5) {
+        text = "F5";
+    } else if (key == Qt::Key_F6) {
+        text = "F6";
+    } else if (key == Qt::Key_F7) {
+        text = "F7";
+    } else if (key == Qt::Key_F8) {
+        text = "F8";
+    } else if (key == Qt::Key_F9) {
+        text = "F9";
+    } else if (key == Qt::Key_F10) {
+        text = "F10";
+    } else if (key == Qt::Key_F11) {
+        text = "F11";
+    } else if (key == Qt::Key_F12) {
+        text = "F12";
+    } else if (key == Qt::Key_Up) {
+        text = "UP";
+    } else if (key == Qt::Key_Down) {
+        text = "DOWN";
+    } else if (key == Qt::Key_Left) {
+        text = "LEFT";
+    } else if (key == Qt::Key_Right) {
+        text = "RIGHT";
+    } else if (key == Qt::Key_Escape) {
+        text = "ESC";
+    } else if (key == Qt::Key_Tab) {
+        text = "TAB";
+    } else if (key == Qt::Key_Delete) {
+        text = "DELETE";
+    } else if (key == Qt::Key_Backspace) {
+        text = "BACKSPACE";
+    }
+}
+
+MouseEvent::MouseEvent(const QMouseEvent& event) {
+    x = event.x();
+    y = event.y();
+}
+
+TouchEvent::TouchEvent(const QTouchEvent& event) {
+    // convert the touch points into an average    
+    const QList& tPoints = event.touchPoints();
+    float touchAvgX = 0.0f;
+    float touchAvgY = 0.0f;
+    int numTouches = tPoints.count();
+    if (numTouches > 1) {
+        for (int i = 0; i < numTouches; ++i) {
+            touchAvgX += tPoints[i].pos().x();
+            touchAvgY += tPoints[i].pos().y();
+        }
+        touchAvgX /= (float)(numTouches);
+        touchAvgY /= (float)(numTouches);
+    }
+    x = touchAvgX;
+    y = touchAvgY;
+}
+
+WheelEvent::WheelEvent(const QWheelEvent& event) {
+    x = event.x();
+    y = event.y();
+}
+
+
+void registerEventTypes(QScriptEngine* engine) {
+    qScriptRegisterMetaType(engine, keyEventToScriptValue, keyEventFromScriptValue);
+    qScriptRegisterMetaType(engine, mouseEventToScriptValue, mouseEventFromScriptValue);
+    qScriptRegisterMetaType(engine, touchEventToScriptValue, touchEventFromScriptValue);
+    qScriptRegisterMetaType(engine, wheelEventToScriptValue, wheelEventFromScriptValue);
+}
+
+QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event) {
+    QScriptValue obj = engine->newObject();
+    obj.setProperty("key", event.key);
+    obj.setProperty("text", event.text);
+    obj.setProperty("isShifted", event.isShifted);
+    obj.setProperty("isMeta", event.isMeta);
+    obj.setProperty("isControl", event.isControl);
+    obj.setProperty("isAlt", event.isAlt);
+    obj.setProperty("isKeypad", event.isKeypad);
+    return obj;
+}
+
+void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event) {
+
+    event.isValid = false; // assume the worst
+    event.isMeta = object.property("isMeta").toVariant().toBool();
+    event.isControl = object.property("isControl").toVariant().toBool();
+    event.isAlt = object.property("isAlt").toVariant().toBool();
+    event.isKeypad = object.property("isKeypad").toVariant().toBool();
+
+    QScriptValue key = object.property("key");
+    if (key.isValid()) {
+        event.key = key.toVariant().toInt();
+        event.text = QString(QChar(event.key));
+        event.isValid = true;
+    } else {
+        QScriptValue text = object.property("text");
+        if (text.isValid()) {
+            event.text = object.property("text").toVariant().toString();
+            
+            // if the text is a special command, then map it here...
+            // TODO: come up with more elegant solution here, a map? is there a Qt function that gives nice names for keys?
+            if (event.text.toUpper() == "F1") {
+                event.key = Qt::Key_F1;
+            } else if (event.text.toUpper() == "F2") {
+                event.key = Qt::Key_F2;
+            } else if (event.text.toUpper() == "F3") {
+                event.key = Qt::Key_F3;
+            } else if (event.text.toUpper() == "F4") {
+                event.key = Qt::Key_F4;
+            } else if (event.text.toUpper() == "F5") {
+                event.key = Qt::Key_F5;
+            } else if (event.text.toUpper() == "F6") {
+                event.key = Qt::Key_F6;
+            } else if (event.text.toUpper() == "F7") {
+                event.key = Qt::Key_F7;
+            } else if (event.text.toUpper() == "F8") {
+                event.key = Qt::Key_F8;
+            } else if (event.text.toUpper() == "F9") {
+                event.key = Qt::Key_F9;
+            } else if (event.text.toUpper() == "F10") {
+                event.key = Qt::Key_F10;
+            } else if (event.text.toUpper() == "F11") {
+                event.key = Qt::Key_F11;
+            } else if (event.text.toUpper() == "F12") {
+                event.key = Qt::Key_F12;
+            } else if (event.text.toUpper() == "UP") {
+                event.key = Qt::Key_Up;
+                event.isKeypad = true;
+            } else if (event.text.toUpper() == "DOWN") {
+                event.key = Qt::Key_Down;
+                event.isKeypad = true;
+            } else if (event.text.toUpper() == "LEFT") {
+                event.key = Qt::Key_Left;
+                event.isKeypad = true;
+            } else if (event.text.toUpper() == "RIGHT") {
+                event.key = Qt::Key_Right;
+                event.isKeypad = true;
+            } else if (event.text.toUpper() == "ESC") {
+                event.key = Qt::Key_Escape;
+            } else if (event.text.toUpper() == "TAB") {
+                event.key = Qt::Key_Tab;
+            } else if (event.text.toUpper() == "DELETE") {
+                event.key = Qt::Key_Delete;
+            } else if (event.text.toUpper() == "BACKSPACE") {
+                event.key = Qt::Key_Backspace;
+            } else {
+                event.key = event.text.at(0).unicode();
+            }
+            event.isValid = true;
+        }
+    }
+
+    QScriptValue isShifted = object.property("isShifted");
+    if (isShifted.isValid()) {
+        event.isShifted = isShifted.toVariant().toBool();
+    } else {
+        // if no isShifted was included, get it from the text
+        QChar character = event.text.at(0);
+        if (character.isLetter() && character.isUpper()) {
+            event.isShifted = true;
+        } else {
+            // if it's a symbol, then attempt to detect shifted-ness
+            if (QString("~!@#$%^&*()_+{}|:\"<>?").contains(character)) {
+                event.isShifted = true;
+            }
+        }
+    }
+    
+
+    const bool wantDebug = false;
+    if (wantDebug) {    
+        qDebug() << "event.key=" << event.key
+            << " event.text=" << event.text
+            << " event.isShifted=" << event.isShifted
+            << " event.isControl=" << event.isControl
+            << " event.isMeta=" << event.isMeta
+            << " event.isAlt=" << event.isAlt
+            << " event.isKeypad=" << event.isKeypad;
+    }
+}
+
+QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event) {
+    QScriptValue obj = engine->newObject();
+    obj.setProperty("x", event.x);
+    obj.setProperty("y", event.y);
+    return obj;
+}
+
+void mouseEventFromScriptValue(const QScriptValue &object, MouseEvent& event) {
+    // nothing for now...
+}
+
+QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& event) {
+    QScriptValue obj = engine->newObject();
+    obj.setProperty("x", event.x);
+    obj.setProperty("y", event.y);
+    return obj;
+}
+
+void touchEventFromScriptValue(const QScriptValue &object, TouchEvent& event) {
+    // nothing for now...
+}
+
+QScriptValue wheelEventToScriptValue(QScriptEngine* engine, const WheelEvent& event) {
+    QScriptValue obj = engine->newObject();
+    obj.setProperty("x", event.x);
+    obj.setProperty("y", event.y);
+    return obj;
+}
+
+void wheelEventFromScriptValue(const QScriptValue &object, WheelEvent& event) {
+    // nothing for now...
+}
diff --git a/libraries/script-engine/src/EventTypes.h b/libraries/script-engine/src/EventTypes.h
new file mode 100644
index 0000000000..81cba03cea
--- /dev/null
+++ b/libraries/script-engine/src/EventTypes.h
@@ -0,0 +1,88 @@
+//
+//  EventTypes.h
+//  hifi
+//
+//  Created by Brad Hefta-Gaub on 1/28/14.
+//  Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
+//
+
+#ifndef __hifi_EventTypes_h__
+#define __hifi_EventTypes_h__
+
+#include 
+
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+
+class KeyEvent {
+public:
+    KeyEvent();
+    KeyEvent(const QKeyEvent& event);
+    inline bool operator==(const KeyEvent& other) const { 
+                            return other.key == key 
+                                && other.isShifted == isShifted 
+                                && other.isControl == isControl
+                                && other.isMeta == isMeta
+                                && other.isAlt == isAlt
+                                && other.isKeypad == isKeypad; }
+    int key;
+    QString text;
+    bool isShifted;
+    bool isControl;
+    bool isMeta;
+    bool isAlt;
+    bool isKeypad;
+    bool isValid;
+};
+
+
+class MouseEvent {
+public:
+    MouseEvent() : x(0), y(0) { }; 
+    MouseEvent(const QMouseEvent& event);
+    int x;
+    int y;
+};
+
+class TouchEvent {
+public:
+    TouchEvent() : x(0), y(0) { };
+    TouchEvent(const QTouchEvent& event);
+    float x;
+    float y;
+};
+
+class WheelEvent {
+public:
+    WheelEvent() : x(0), y(0)  { }; 
+    WheelEvent(const QWheelEvent& event);
+    int x;
+    int y;
+};
+
+
+Q_DECLARE_METATYPE(KeyEvent)
+Q_DECLARE_METATYPE(MouseEvent)
+Q_DECLARE_METATYPE(TouchEvent)
+Q_DECLARE_METATYPE(WheelEvent)
+
+void registerEventTypes(QScriptEngine* engine);
+
+QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event);
+void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event);
+
+QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event);
+void mouseEventFromScriptValue(const QScriptValue &object, MouseEvent& event);
+
+QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& event);
+void touchEventFromScriptValue(const QScriptValue &object, TouchEvent& event);
+
+QScriptValue wheelEventToScriptValue(QScriptEngine* engine, const WheelEvent& event);
+void wheelEventFromScriptValue(const QScriptValue &object, WheelEvent& event);
+
+#endif // __hifi_EventTypes_h__
diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp
new file mode 100644
index 0000000000..12900c29c7
--- /dev/null
+++ b/libraries/script-engine/src/Quat.cpp
@@ -0,0 +1,20 @@
+//
+//  Quat.cpp
+//  hifi
+//
+//  Created by Brad Hefta-Gaub on 1/29/14
+//  Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
+//
+//  Scriptable Quaternion class library.
+//
+//
+
+#include "Quat.h"
+
+glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) { 
+    return q1 * q2; 
+}
+
+glm::quat Quat::fromVec3(const glm::vec3& vec3) { 
+    return glm::quat(vec3); 
+}
diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h
new file mode 100644
index 0000000000..6bcd121808
--- /dev/null
+++ b/libraries/script-engine/src/Quat.h
@@ -0,0 +1,29 @@
+//
+//  Quat.h
+//  hifi
+//
+//  Created by Brad Hefta-Gaub on 1/29/14
+//  Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
+//
+//  Scriptable Quaternion class library.
+//
+//
+
+#ifndef __hifi__Quat__
+#define __hifi__Quat__
+
+#include 
+#include 
+
+/// Scriptable interface a Quaternion helper class object. Used exclusively in the JavaScript API
+class Quat : public QObject {
+    Q_OBJECT
+
+public slots:
+    glm::quat multiply(const glm::quat& q1, const glm::quat& q2);
+    glm::quat fromVec3(const glm::vec3& vec3);
+};
+
+
+
+#endif /* defined(__hifi__Quat__) */
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 1bf8dbf696..f8dac39adc 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -19,6 +19,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #include 
@@ -39,8 +40,10 @@ static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* eng
 }
 
 
-ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, const QString& fileNameString, AbstractMenuInterface* menu,
+ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, const QString& fileNameString,
+                           AbstractMenuInterface* menu,
                            AbstractControllerScriptingInterface* controllerScriptingInterface) :
+    _isAvatar(false),
     _dataServerScriptingInterface(),
     _avatarData(NULL)
 {
@@ -102,9 +105,8 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents) {
     return true;
 }
 
-Q_SCRIPT_DECLARE_QMETAOBJECT(AudioInjectorOptions, QObject*)
-
 void ScriptEngine::init() {
+    qDebug() << "Init called!";
     if (_isInitialized) {
         return; // only initialize once
     }
@@ -114,21 +116,16 @@ void ScriptEngine::init() {
     _voxelsScriptingInterface.init();
     _particlesScriptingInterface.init();
 
-    // register meta-type for glm::vec3 conversions
+    // register various meta-types
     registerMetaTypes(&_engine);
+    registerVoxelMetaTypes(&_engine);
+    //registerParticleMetaTypes(&_engine);
+    registerEventTypes(&_engine);
+    
     qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue);
     qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue);
     qScriptRegisterSequenceMetaType >(&_engine);
 
-    QScriptValue agentValue = _engine.newQObject(this);
-    _engine.globalObject().setProperty("Agent", agentValue);
-
-    QScriptValue voxelScripterValue =  _engine.newQObject(&_voxelsScriptingInterface);
-    _engine.globalObject().setProperty("Voxels", voxelScripterValue);
-
-    QScriptValue particleScripterValue =  _engine.newQObject(&_particlesScriptingInterface);
-    _engine.globalObject().setProperty("Particles", particleScripterValue);
-
     QScriptValue soundConstructorValue = _engine.newFunction(soundConstructor);
     QScriptValue soundMetaObject = _engine.newQMetaObject(&Sound::staticMetaObject, soundConstructorValue);
     _engine.globalObject().setProperty("Sound", soundMetaObject);
@@ -136,15 +133,14 @@ void ScriptEngine::init() {
     QScriptValue injectionOptionValue = _engine.scriptValueFromQMetaObject();
     _engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue);
 
-    QScriptValue audioScriptingInterfaceValue = _engine.newQObject(&_audioScriptingInterface);
-    _engine.globalObject().setProperty("Audio", audioScriptingInterfaceValue);
-    
+    registerGlobalObject("Script", this);
+    registerGlobalObject("Audio", &_audioScriptingInterface);
+    registerGlobalObject("Controller", _controllerScriptingInterface);
     registerGlobalObject("Data", &_dataServerScriptingInterface);
+    registerGlobalObject("Particles", &_particlesScriptingInterface);
+    registerGlobalObject("Quat", &_quatLibrary);
 
-    if (_controllerScriptingInterface) {
-        QScriptValue controllerScripterValue =  _engine.newQObject(_controllerScriptingInterface);
-        _engine.globalObject().setProperty("Controller", controllerScripterValue);
-    }
+    registerGlobalObject("Voxels", &_voxelsScriptingInterface);
 
     QScriptValue treeScaleValue = _engine.newVariant(QVariant(TREE_SCALE));
     _engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
@@ -157,8 +153,10 @@ void ScriptEngine::init() {
 }
 
 void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {
-    QScriptValue value = _engine.newQObject(object);
-    _engine.globalObject().setProperty(name, value);
+    if (object) {
+        QScriptValue value = _engine.newQObject(object);
+        _engine.globalObject().setProperty(name, value);
+    }
 }
 
 void ScriptEngine::evaluate() {
@@ -209,11 +207,7 @@ void ScriptEngine::run() {
             break;
         }
 
-        bool willSendVisualDataCallBack = false;
         if (_voxelsScriptingInterface.getVoxelPacketSender()->serversExist()) {
-            // allow the scripter's call back to setup visual data
-            willSendVisualDataCallBack = true;
-
             // release the queue of edit voxel messages.
             _voxelsScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages();
 
@@ -224,9 +218,6 @@ void ScriptEngine::run() {
         }
 
         if (_particlesScriptingInterface.getParticlePacketSender()->serversExist()) {
-            // allow the scripter's call back to setup visual data
-            willSendVisualDataCallBack = true;
-
             // release the queue of edit voxel messages.
             _particlesScriptingInterface.getParticlePacketSender()->releaseQueuedMessages();
 
@@ -237,26 +228,22 @@ void ScriptEngine::run() {
         }
         
         if (_isAvatar && _avatarData) {
-            static unsigned char avatarPacket[MAX_PACKET_SIZE];
-            static int numAvatarHeaderBytes = 0;
+            static QByteArray avatarPacket;
+            int numAvatarHeaderBytes = 0;
             
-            if (numAvatarHeaderBytes == 0) {
+            if (avatarPacket.size() == 0) {
                 // pack the avatar header bytes the first time
                 // unlike the _avatar.getBroadcastData these won't change
-                numAvatarHeaderBytes = populateTypeAndVersion(avatarPacket, PACKET_TYPE_HEAD_DATA);
-                
-                // pack the owner UUID for this script
-                numAvatarHeaderBytes += NodeList::getInstance()->packOwnerUUID(avatarPacket);
+                numAvatarHeaderBytes = populatePacketHeader(avatarPacket, PacketTypeAvatarData);
             }
             
-            int numAvatarPacketBytes = _avatarData->getBroadcastData(avatarPacket + numAvatarHeaderBytes) + numAvatarHeaderBytes;
+            avatarPacket.resize(numAvatarHeaderBytes);
+            avatarPacket.append(_avatarData->toByteArray());
             
-            nodeList->broadcastToNodes(avatarPacket, numAvatarPacketBytes, QSet() << NODE_TYPE_AVATAR_MIXER);
+            nodeList->broadcastToNodes(avatarPacket, NodeSet() << NodeType::AvatarMixer);
         }
 
-        if (willSendVisualDataCallBack) {
-            emit willSendVisualDataCallback();
-        }
+        emit willSendVisualDataCallback();
 
         if (_engine.hasUncaughtException()) {
             int line = _engine.uncaughtExceptionLineNumber();
@@ -264,6 +251,27 @@ void ScriptEngine::run() {
         }
     }
     emit scriptEnding();
+    
+    if (_voxelsScriptingInterface.getVoxelPacketSender()->serversExist()) {
+        // release the queue of edit voxel messages.
+        _voxelsScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages();
+
+        // since we're in non-threaded mode, call process so that the packets are sent
+        if (!_voxelsScriptingInterface.getVoxelPacketSender()->isThreaded()) {
+            _voxelsScriptingInterface.getVoxelPacketSender()->process();
+        }
+    }
+
+    if (_particlesScriptingInterface.getParticlePacketSender()->serversExist()) {
+        // release the queue of edit voxel messages.
+        _particlesScriptingInterface.getParticlePacketSender()->releaseQueuedMessages();
+
+        // since we're in non-threaded mode, call process so that the packets are sent
+        if (!_particlesScriptingInterface.getParticlePacketSender()->isThreaded()) {
+            _particlesScriptingInterface.getParticlePacketSender()->process();
+        }
+    }
+    
     cleanMenuItems();
 
     // If we were on a thread, then wait till it's done
@@ -280,4 +288,50 @@ void ScriptEngine::stop() {
     _isFinished = true;
 }
 
+void ScriptEngine::timerFired() {
+    QTimer* callingTimer = reinterpret_cast(sender());
+    
+    // call the associated JS function, if it exists
+    QScriptValue timerFunction = _timerFunctionMap.value(callingTimer);
+    if (timerFunction.isValid()) {
+        timerFunction.call();
+    }
+    
+    if (!callingTimer->isActive()) {
+        // this timer is done, we can kill it
+        qDebug() << "Deleting a single shot timer";
+        delete callingTimer;
+    }
+}
 
+QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot) {
+    // create the timer, add it to the map, and start it
+    QTimer* newTimer = new QTimer(this);
+    newTimer->setSingleShot(isSingleShot);
+    
+    connect(newTimer, &QTimer::timeout, this, &ScriptEngine::timerFired);
+    
+    // make sure the timer stops when the script does
+    connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop);
+    
+    _timerFunctionMap.insert(newTimer, function);
+    
+    newTimer->start(intervalMS);
+    return newTimer;
+}
+
+QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) {
+    return setupTimerWithInterval(function, intervalMS, false);
+}
+
+QObject* ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) {
+    return setupTimerWithInterval(function, timeoutMS, true);
+}
+
+void ScriptEngine::stopTimer(QTimer *timer) {
+    if (_timerFunctionMap.contains(timer)) {
+        timer->stop();
+        _timerFunctionMap.remove(timer);
+        delete timer;
+    }
+}
diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h
index b36e2425fe..98d5860332 100644
--- a/libraries/script-engine/src/ScriptEngine.h
+++ b/libraries/script-engine/src/ScriptEngine.h
@@ -25,25 +25,24 @@ class ParticlesScriptingInterface;
 
 #include "AbstractControllerScriptingInterface.h"
 #include "DataServerScriptingInterface.h"
+#include "Quat.h"
 
 const QString NO_SCRIPT("");
 
 class ScriptEngine : public QObject {
     Q_OBJECT
-    
-    Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar)
 public:
     ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false,
-		 const QString& scriptMenuName = QString(""), AbstractMenuInterface* menu = NULL,
-                    AbstractControllerScriptingInterface* controllerScriptingInterface = NULL);
+                 const QString& scriptMenuName = QString(""), AbstractMenuInterface* menu = NULL,
+                 AbstractControllerScriptingInterface* controllerScriptingInterface = NULL);
 
     ~ScriptEngine();
 
     /// Access the VoxelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
-    VoxelsScriptingInterface* getVoxelsScriptingInterface() { return &_voxelsScriptingInterface; }
+    static VoxelsScriptingInterface* getVoxelsScriptingInterface() { return &_voxelsScriptingInterface; }
 
     /// Access the ParticlesScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
-    ParticlesScriptingInterface* getParticlesScriptingInterface() { return &_particlesScriptingInterface; }
+    static ParticlesScriptingInterface* getParticlesScriptingInterface() { return &_particlesScriptingInterface; }
     
     /// Access the DataServerScriptingInterface for access to its underlying UUID
     const DataServerScriptingInterface& getDataServerScriptingInterface() { return _dataServerScriptingInterface; }
@@ -60,13 +59,21 @@ public:
     bool isAvatar() const { return _isAvatar; }
     
     void setAvatarData(AvatarData* avatarData, const QString& objectName);
-
-public slots:
+    
     void init();
     void run(); /// runs continuously until Agent.stop() is called
-    void stop();
     void evaluate(); /// initializes the engine, and evaluates the script, but then returns control to caller
+    
+    void timerFired();
 
+public slots:
+    void stop();
+    
+    QObject* setInterval(const QScriptValue& function, int intervalMS);
+    QObject* setTimeout(const QScriptValue& function, int timeoutMS);
+    void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); }
+    void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(timer)); }
+    
 signals:
     void willSendAudioDataCallback();
     void willSendVisualDataCallback();
@@ -74,15 +81,18 @@ signals:
     void finished(const QString& fileNameString);
 
 protected:
-    
     QString _scriptContents;
     bool _isFinished;
     bool _isRunning;
     bool _isInitialized;
     QScriptEngine _engine;
     bool _isAvatar;
+    QHash _timerFunctionMap;
 
 private:
+    QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot);
+    void stopTimer(QTimer* timer);
+    
     static VoxelsScriptingInterface _voxelsScriptingInterface;
     static ParticlesScriptingInterface _particlesScriptingInterface;
     AbstractControllerScriptingInterface* _controllerScriptingInterface;
@@ -94,6 +104,7 @@ private:
     QString _fileNameString;
     AbstractMenuInterface* _menu;
     static int _scriptNumber;
+    Quat _quatLibrary;
 };
 
 #endif /* defined(__hifi__ScriptEngine__) */
diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp
index 8e67e7a178..83296991f3 100644
--- a/libraries/shared/src/Assignment.cpp
+++ b/libraries/shared/src/Assignment.cpp
@@ -14,22 +14,19 @@
 
 #include "Assignment.h"
 
-const char IPv4_ADDRESS_DESIGNATOR = 4;
-const char IPv6_ADDRESS_DESIGNATOR = 6;
-
-Assignment::Type Assignment::typeForNodeType(NODE_TYPE nodeType) {
+Assignment::Type Assignment::typeForNodeType(NodeType_t nodeType) {
     switch (nodeType) {
-        case NODE_TYPE_AUDIO_MIXER:
+        case NodeType::AudioMixer:
             return Assignment::AudioMixerType;
-        case NODE_TYPE_AVATAR_MIXER:
+        case NodeType::AvatarMixer:
             return Assignment::AvatarMixerType;
-        case NODE_TYPE_AGENT:
+        case NodeType::Agent:
             return Assignment::AgentType;
-        case NODE_TYPE_VOXEL_SERVER:
+        case NodeType::VoxelServer:
             return Assignment::VoxelServerType;
-        case NODE_TYPE_PARTICLE_SERVER:
+        case NodeType::ParticleServer:
             return Assignment::ParticleServerType;
-        case NODE_TYPE_METAVOXEL_SERVER:
+        case NodeType::MetavoxelServer:
             return Assignment::MetavoxelServerType;
         default:
             return Assignment::AllTypes;
@@ -46,70 +43,47 @@ Assignment::Assignment() :
     _uuid(),
     _command(Assignment::RequestCommand),
     _type(Assignment::AllTypes),
+    _pool(),
     _location(Assignment::LocalLocation),
     _numberOfInstances(1),
-    _payload(),
-    _numPayloadBytes(0)
+    _payload()
 {
-    setPool(NULL);
+    
 }
 
-Assignment::Assignment(Assignment::Command command, Assignment::Type type, const char* pool, Assignment::Location location) :
+Assignment::Assignment(Assignment::Command command, Assignment::Type type, const QString& pool, Assignment::Location location) :
+    _uuid(),
     _command(command),
     _type(type),
+    _pool(pool),
     _location(location),
     _numberOfInstances(1),
-    _payload(),
-    _numPayloadBytes(0)
+    _payload()
 {
     if (_command == Assignment::CreateCommand) {
         // this is a newly created assignment, generate a random UUID
         _uuid = QUuid::createUuid();
     }
-    
-    setPool(pool);
 }
 
-Assignment::Assignment(const unsigned char* dataBuffer, int numBytes) :
+Assignment::Assignment(const QByteArray& packet) :
+    _pool(),
     _location(GlobalLocation),
     _numberOfInstances(1),
-    _payload(),
-    _numPayloadBytes(0)
+    _payload()
 {
-    int numBytesRead = 0;
+    PacketType packetType = packetTypeForPacket(packet);
     
-    if (dataBuffer[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) {
+    if (packetType == PacketTypeRequestAssignment) {
         _command = Assignment::RequestCommand;
-    } else if (dataBuffer[0] == PACKET_TYPE_CREATE_ASSIGNMENT) {
+    } else if (packetType == PacketTypeCreateAssignment) {
         _command = Assignment::CreateCommand;
-    } else if (dataBuffer[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT) {
-        _command = Assignment::DeployCommand;
     }
     
-    numBytesRead += numBytesForPacketHeader(dataBuffer);
+    QDataStream packetStream(packet);
+    packetStream.skipRawData(numBytesForPacketHeader(packet));
     
-    memcpy(&_type, dataBuffer + numBytesRead, sizeof(Assignment::Type));
-    numBytesRead += sizeof(Assignment::Type);
-    
-    if (_command != Assignment::RequestCommand) {
-        // read the GUID for this assignment
-        _uuid = QUuid::fromRfc4122(QByteArray((const char*) dataBuffer + numBytesRead, NUM_BYTES_RFC4122_UUID));
-        numBytesRead += NUM_BYTES_RFC4122_UUID;
-    }
-    
-    if (dataBuffer[numBytesRead] != '\0') {
-        // read the pool from the data buffer
-        setPool((const char*) dataBuffer + numBytesRead);
-        numBytesRead += strlen(_pool) + sizeof('\0');
-    } else {
-        // skip past the null pool and null out our pool
-        setPool(NULL);
-        numBytesRead++;
-    }
-    
-    if (numBytes > numBytesRead) {
-        setPayload(dataBuffer + numBytesRead, numBytes - numBytesRead);
-    }
+    packetStream >> *this;
 }
 
 #ifdef WIN32
@@ -124,10 +98,9 @@ Assignment::Assignment(const Assignment& otherAssignment) {
     _command = otherAssignment._command;
     _type = otherAssignment._type;
     _location = otherAssignment._location;
-    setPool(otherAssignment._pool);
+    _pool = otherAssignment._pool;
     _numberOfInstances = otherAssignment._numberOfInstances;
-    
-    setPayload(otherAssignment._payload, otherAssignment._numPayloadBytes);
+    _payload = otherAssignment._payload;
 }
 
 Assignment& Assignment::operator=(const Assignment& rhsAssignment) {
@@ -143,42 +116,10 @@ void Assignment::swap(Assignment& otherAssignment) {
     swap(_command, otherAssignment._command);
     swap(_type, otherAssignment._type);
     swap(_location, otherAssignment._location);
-    
-    for (int i = 0; i < sizeof(_pool); i++) {
-        swap(_pool[i], otherAssignment._pool[i]);
-    }
+    swap(_pool, otherAssignment._pool);
     
     swap(_numberOfInstances, otherAssignment._numberOfInstances);
-    
-    for (int i = 0; i < MAX_PAYLOAD_BYTES; i++) {
-        swap(_payload[i], otherAssignment._payload[i]);
-    }
-    
-    swap(_numPayloadBytes, otherAssignment._numPayloadBytes);
-}
-
-void Assignment::setPayload(const uchar* payload, int numBytes) {
-    
-    if (numBytes > MAX_PAYLOAD_BYTES) {
-        qDebug("Set payload called with number of bytes greater than maximum (%d). Will only transfer %d bytes.",
-               MAX_PAYLOAD_BYTES,
-               MAX_PAYLOAD_BYTES);
-        
-        _numPayloadBytes = 1024;
-    } else {
-        _numPayloadBytes = numBytes;
-    }
-    
-    memset(_payload, 0, MAX_PAYLOAD_BYTES);
-    memcpy(_payload, payload, _numPayloadBytes);
-}
-
-void Assignment::setPool(const char* pool) {
-    memset(_pool, '\0', sizeof(_pool));
-    
-    if (pool) {
-        strcpy(_pool, pool);
-    }
+    swap(_payload, otherAssignment._payload);
 }
 
 const char* Assignment::getTypeName() const {
@@ -200,38 +141,29 @@ const char* Assignment::getTypeName() const {
     }
 }
 
-int Assignment::packToBuffer(unsigned char* buffer) {
-    int numPackedBytes = 0;
+QDebug operator<<(QDebug debug, const Assignment &assignment) {
+    debug.nospace() << "UUID: " << qPrintable(assignment.getUUID().toString()) <<
+        ", Type: " << assignment.getType();
     
-    memcpy(buffer + numPackedBytes, &_type, sizeof(_type));
-    numPackedBytes += sizeof(_type);
-    
-    // pack the UUID for this assignment, if this is an assignment create or deploy
-    if (_command != Assignment::RequestCommand) {
-        memcpy(buffer + numPackedBytes, _uuid.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
-        numPackedBytes += NUM_BYTES_RFC4122_UUID;
+    if (!assignment.getPool().isEmpty()) {
+        debug << ", Pool: " << assignment.getPool();
     }
-    
-    if (hasPool()) {
-        // pack the pool for this assignment, it exists
-        int numBytesNullTerminatedPool = strlen(_pool) + sizeof('\0');
-        memcpy(buffer + numPackedBytes, _pool, numBytesNullTerminatedPool);
-        numPackedBytes += numBytesNullTerminatedPool;
-    } else {
-        // otherwise pack the null character
-        buffer[numPackedBytes++] = '\0';
-    }
-    
-    if (_numPayloadBytes) {
-        memcpy(buffer + numPackedBytes, _payload, _numPayloadBytes);
-        numPackedBytes += _numPayloadBytes;
-    }
-    
-    return numPackedBytes;
+
+    return debug.space();
 }
 
-QDebug operator<<(QDebug debug, const Assignment &assignment) {
-    debug.nospace() << "UUID: " << assignment.getUUID().toString().toStdString().c_str() <<
-        ", Type: " << assignment.getType();
-    return debug.nospace();
+QDataStream& operator<<(QDataStream &out, const Assignment& assignment) {
+    out << (quint8) assignment._type << assignment._uuid << assignment._pool << assignment._payload;
+    
+    return out;
+}
+
+QDataStream& operator>>(QDataStream &in, Assignment& assignment) {
+    quint8 packedType;
+    in >> packedType;
+    assignment._type = (Assignment::Type) packedType;
+    
+    in >> assignment._uuid >> assignment._pool >> assignment._payload;
+    
+    return in;
 }
diff --git a/libraries/shared/src/Assignment.h b/libraries/shared/src/Assignment.h
index 3f73748251..dd34751b57 100644
--- a/libraries/shared/src/Assignment.h
+++ b/libraries/shared/src/Assignment.h
@@ -20,7 +20,8 @@
 #include "NodeList.h"
 
 const int MAX_PAYLOAD_BYTES = 1024;
-const int MAX_ASSIGNMENT_POOL_BYTES = 64 + sizeof('\0');
+
+const QString emptyPool = QString();
 
 /// Holds information used for request, creation, and deployment of assignments
 class Assignment : public NodeData {
@@ -48,12 +49,12 @@ public:
         LocalLocation
     };
 
-    static Assignment::Type typeForNodeType(NODE_TYPE nodeType);
+    static Assignment::Type typeForNodeType(NodeType_t nodeType);
 
     Assignment();
     Assignment(Assignment::Command command,
                Assignment::Type type,
-               const char* pool = NULL,
+               const QString& pool = emptyPool,
                Assignment::Location location = Assignment::LocalLocation);
     Assignment(const Assignment& otherAssignment);
     Assignment& operator=(const Assignment &rhsAssignment);
@@ -63,7 +64,7 @@ public:
     /// Constructs an Assignment from the data in the buffer
     /// \param dataBuffer the source buffer to un-pack the assignment from
     /// \param numBytes the number of bytes left to read in the source buffer
-    Assignment(const unsigned char* dataBuffer, int numBytes);
+    Assignment(const QByteArray& packet);
 
     void setUUID(const QUuid& uuid) { _uuid = uuid; }
     const QUuid& getUUID() const { return _uuid; }
@@ -73,13 +74,11 @@ public:
     Assignment::Type getType() const { return _type; }
     Assignment::Location getLocation() const { return _location; }
 
-    uchar* getPayload() { return _payload; }
-    int getNumPayloadBytes() const { return _numPayloadBytes; }
-    void setPayload(const uchar *payload, int numBytes);
+    const QByteArray& getPayload() const { return _payload; }
+    void setPayload(const QByteArray& payload) { _payload = payload.left(MAX_PAYLOAD_BYTES); }
 
-    void setPool(const char* pool);
-    const char* getPool() const { return _pool; }
-    bool hasPool() const { return (bool) strlen(_pool); }
+    void setPool(const QString& pool) { _pool = pool; };
+    const QString& getPool() const { return _pool; }
 
     int getNumberOfInstances() const { return _numberOfInstances; }
     void setNumberOfInstances(int numberOfInstances) { _numberOfInstances = numberOfInstances; }
@@ -87,13 +86,8 @@ public:
 
     const char* getTypeName() const;
 
-    /// Packs the assignment to the passed buffer
-    /// \param buffer the buffer in which to pack the assignment
-    /// \return number of bytes packed into buffer
-    int packToBuffer(unsigned char* buffer);
-
     // implement parseData to return 0 so we can be a subclass of NodeData
-    int parseData(unsigned char* sourceBuffer, int numBytes) { return 0; }
+    int parseData(const QByteArray& packet) { return 0; }
 
     friend QDebug operator<<(QDebug debug, const Assignment& assignment);
     friend QDataStream& operator<<(QDataStream &out, const Assignment& assignment);
@@ -103,11 +97,10 @@ protected:
     QUuid _uuid; /// the 16 byte UUID for this assignment
     Assignment::Command _command; /// the command for this assignment (Create, Deploy, Request)
     Assignment::Type _type; /// the type of the assignment, defines what the assignee will do
-    char _pool[MAX_ASSIGNMENT_POOL_BYTES]; /// the destination pool for this assignment
+    QString _pool; /// the destination pool for this assignment
     Assignment::Location _location; /// the location of the assignment, allows a domain to preferentially use local ACs
     int _numberOfInstances; /// the number of instances of this assignment
-    uchar _payload[MAX_PAYLOAD_BYTES]; /// an optional payload attached to this assignment, a maximum for 1024 bytes will be packed
-    int _numPayloadBytes; /// number of bytes in the payload, up to a maximum of 1024
+    QByteArray _payload; /// an optional payload attached to this assignment, a maximum for 1024 bytes will be packed
 };
 
 #endif /* defined(__hifi__Assignment__) */
diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h
index 1e4801788e..38ae64e30c 100644
--- a/libraries/shared/src/CollisionInfo.h
+++ b/libraries/shared/src/CollisionInfo.h
@@ -13,11 +13,19 @@
 
 class CollisionInfo {
 public:
-    CollisionInfo() : _penetration(0.f), _addedVelocity(0.f) { }
+    CollisionInfo() 
+        : _damping(0.f),
+        _elasticity(1.f),
+        _penetration(0.f), 
+        _addedVelocity(0.f) { 
+        }
+
     ~CollisionInfo() {}
 
     //glm::vec3 _point;
     //glm::vec3 _normal;
+    float _damping;
+    float _elasticity;
     glm::vec3 _penetration; // depth that bodyA is penetrates bodyB
     glm::vec3 _addedVelocity;
 };
diff --git a/libraries/shared/src/DataServerClient.cpp b/libraries/shared/src/DataServerClient.cpp
index 787e5ab8e2..fd003aa3bb 100644
--- a/libraries/shared/src/DataServerClient.cpp
+++ b/libraries/shared/src/DataServerClient.cpp
@@ -30,39 +30,18 @@ const HifiSockAddr& DataServerClient::dataServerSockAddr() {
 }
 
 void DataServerClient::putValueForKeyAndUserString(const QString& key, const QString& value, const QString& userString) {
-    unsigned char putPacket[MAX_PACKET_SIZE];
+    // setup the header for this packet and push packetStream to desired spot
+    QByteArray putPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerPut);
+    QDataStream packetStream(&putPacket, QIODevice::Append);
     
-    // setup the header for this packet
-    int numPacketBytes = populateTypeAndVersion(putPacket, PACKET_TYPE_DATA_SERVER_PUT);
-    
-    // pack the sequence number
-    memcpy(putPacket + numPacketBytes, &_sequenceNumber, sizeof(_sequenceNumber));
-    numPacketBytes += sizeof(_sequenceNumber);
-    
-    // pack the client UUID, null terminated
-    memcpy(putPacket + numPacketBytes, qPrintable(userString), userString.size());
-    numPacketBytes += userString.size();
-    putPacket[numPacketBytes++] = '\0';
-    
-    // pack a 1 to designate that we are putting a single value
-    putPacket[numPacketBytes++] = 1;
-    
-    // pack the key, null terminated
-    strcpy((char*) putPacket + numPacketBytes, qPrintable(key));
-    numPacketBytes += key.size();
-    putPacket[numPacketBytes++] = '\0';
-    
-    // pack the value, null terminated
-    strcpy((char*) putPacket + numPacketBytes, qPrintable(value));
-    numPacketBytes += value.size();
-    putPacket[numPacketBytes++] = '\0';
+    // pack our data for the put packet
+    packetStream << _sequenceNumber << userString << key << value;
     
     // add the putPacket to our vector of unconfirmed packets, will be deleted once put is confirmed
-    _unmatchedPackets.insert(_sequenceNumber, QByteArray((char*) putPacket, numPacketBytes));
+    _unmatchedPackets.insert(_sequenceNumber, putPacket);
     
     // send this put request to the data server
-    NodeList::getInstance()->getNodeSocket().writeDatagram((char*) putPacket, numPacketBytes,
-                                                           dataServerSockAddr().getAddress(),
+    NodeList::getInstance()->getNodeSocket().writeDatagram(putPacket, dataServerSockAddr().getAddress(),
                                                            dataServerSockAddr().getPort());
     
     // push the sequence number forwards
@@ -87,38 +66,19 @@ void DataServerClient::getValuesForKeysAndUUID(const QStringList& keys, const QU
 void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, const QString& userString,
                                                    DataServerCallbackObject* callbackObject) {
     if (!userString.isEmpty() && keys.size() <= UCHAR_MAX) {
-        unsigned char getPacket[MAX_PACKET_SIZE];
+        QByteArray getPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerGet);
+        QDataStream packetStream(&getPacket, QIODevice::Append);
         
-        // setup the header for this packet
-        int numPacketBytes = populateTypeAndVersion(getPacket, PACKET_TYPE_DATA_SERVER_GET);
+        // pack our data for the getPacket
+        packetStream << _sequenceNumber << userString << keys.join(MULTI_KEY_VALUE_SEPARATOR);
         
-        // pack the sequence number
-        memcpy(getPacket + numPacketBytes, &_sequenceNumber, sizeof(_sequenceNumber));
-        numPacketBytes += sizeof(_sequenceNumber);
-
-        // pack the user string (could be username or UUID string), null-terminate
-        memcpy(getPacket + numPacketBytes, qPrintable(userString), userString.size());
-        numPacketBytes += userString.size();
-        getPacket[numPacketBytes++] = '\0';
-
-        // pack one byte to designate the number of keys
-        getPacket[numPacketBytes++] = keys.size();
-
-        QString keyString = keys.join(MULTI_KEY_VALUE_SEPARATOR);
-
-        // pack the key string, null terminated
-        strcpy((char*) getPacket + numPacketBytes, keyString.toLocal8Bit().constData());
-        numPacketBytes += keyString.size() + sizeof('\0');
-
         // add the getPacket to our map of unconfirmed packets, will be deleted once we get a response from the nameserver
-        _unmatchedPackets.insert(_sequenceNumber, QByteArray((char*) getPacket, numPacketBytes));
+        _unmatchedPackets.insert(_sequenceNumber, getPacket);
         _callbackObjects.insert(_sequenceNumber, callbackObject);
         
         // send the get to the data server
-        NodeList::getInstance()->getNodeSocket().writeDatagram((char*) getPacket, numPacketBytes,
-                                                               dataServerSockAddr().getAddress(),
+        NodeList::getInstance()->getNodeSocket().writeDatagram(getPacket, dataServerSockAddr().getAddress(),
                                                                dataServerSockAddr().getPort());
-                
         _sequenceNumber++;
     }
 }
@@ -128,49 +88,47 @@ void DataServerClient::getValueForKeyAndUserString(const QString& key, const QSt
     getValuesForKeysAndUserString(QStringList(key), userString, callbackObject);
 }
 
-void DataServerClient::processConfirmFromDataServer(unsigned char* packetData) {
-    removeMatchedPacketFromMap(packetData);
+void DataServerClient::processConfirmFromDataServer(const QByteArray& packet) {
+    removeMatchedPacketFromMap(packet);
 }
 
-void DataServerClient::processSendFromDataServer(unsigned char* packetData, int numPacketBytes) {
+void DataServerClient::processSendFromDataServer(const QByteArray& packet) {
     // pull the user string from the packet so we know who to associate this with
-    int numHeaderBytes = numBytesForPacketHeader(packetData);
+    QDataStream packetStream(packet);
+    packetStream.skipRawData(numBytesForPacketHeader(packet));
     
-    quint8 sequenceNumber = *(packetData + numHeaderBytes);
+    quint8 sequenceNumber = 0;
+    packetStream >> sequenceNumber;
     
     if (_callbackObjects.find(sequenceNumber) != _callbackObjects.end()) {
         // remove the packet from our two maps, it's matched
         DataServerCallbackObject* callbackObject = _callbackObjects.take(sequenceNumber);
         _unmatchedPackets.remove(sequenceNumber);
         
-        char* userStringPosition = (char*) packetData + numHeaderBytes + sizeof(sequenceNumber);
-        QString userString(userStringPosition);
+        QString userString, keyListString, valueListString;
         
-        char* keysPosition = userStringPosition + userString.size() + sizeof('\0') + sizeof(unsigned char);
-        char* valuesPosition =  keysPosition + strlen(keysPosition) + sizeof('\0');
+        packetStream >> userString >> keyListString >> valueListString;
         
-        QStringList keyList = QString(keysPosition).split(MULTI_KEY_VALUE_SEPARATOR);
-        QStringList valueList = QString(valuesPosition).split(MULTI_KEY_VALUE_SEPARATOR);
-        
-        callbackObject->processDataServerResponse(userString, keyList, valueList);
+        callbackObject->processDataServerResponse(userString, keyListString.split(MULTI_KEY_VALUE_SEPARATOR),
+                                                  valueListString.split(MULTI_KEY_VALUE_SEPARATOR));
     }
 }
 
-void DataServerClient::processMessageFromDataServer(unsigned char* packetData, int numPacketBytes) {
-    switch (packetData[0]) {
-        case PACKET_TYPE_DATA_SERVER_SEND:
-            processSendFromDataServer(packetData, numPacketBytes);
+void DataServerClient::processMessageFromDataServer(const QByteArray& packet) {
+    switch (packetTypeForPacket(packet)) {
+        case PacketTypeDataServerSend:
+            processSendFromDataServer(packet);
             break;
-        case PACKET_TYPE_DATA_SERVER_CONFIRM:
-            processConfirmFromDataServer(packetData);
+        case PacketTypeDataServerConfirm:
+            processConfirmFromDataServer(packet);
             break;
         default:
             break;
     }
 }
 
-void DataServerClient::removeMatchedPacketFromMap(unsigned char* packetData) {
-    quint8 sequenceNumber = *(packetData + numBytesForPacketHeader(packetData));
+void DataServerClient::removeMatchedPacketFromMap(const QByteArray& packet) {
+    quint8 sequenceNumber = packet[numBytesForPacketHeader(packet)];
     
     // attempt to remove a packet with this sequence number from the QMap of unmatched packets
     _unmatchedPackets.remove(sequenceNumber);
diff --git a/libraries/shared/src/DataServerClient.h b/libraries/shared/src/DataServerClient.h
index 78826022ae..0360576645 100644
--- a/libraries/shared/src/DataServerClient.h
+++ b/libraries/shared/src/DataServerClient.h
@@ -37,13 +37,13 @@ public:
     static void getValuesForKeysAndUserString(const QStringList& keys, const QString& userString,
                                                     DataServerCallbackObject* callbackObject);
     
-    static void processMessageFromDataServer(unsigned char* packetData, int numPacketBytes);
+    static void processMessageFromDataServer(const QByteArray& packet);
     
     static void resendUnmatchedPackets();
 private:
-    static void processConfirmFromDataServer(unsigned char* packetData);
-    static void processSendFromDataServer(unsigned char* packetData, int numPacketBytes);
-    static void removeMatchedPacketFromMap(unsigned char* packetData);
+    static void processConfirmFromDataServer(const QByteArray& packet);
+    static void processSendFromDataServer(const QByteArray& packet);
+    static void removeMatchedPacketFromMap(const QByteArray& packet);
     
     static QMap _unmatchedPackets;
     static QMap _callbackObjects;
diff --git a/libraries/shared/src/FileUtils.cpp b/libraries/shared/src/FileUtils.cpp
index b58799104f..efbe1a189b 100644
--- a/libraries/shared/src/FileUtils.cpp
+++ b/libraries/shared/src/FileUtils.cpp
@@ -10,9 +10,9 @@
 #include 
 #include 
 
-void FileUtils::LocateFile(QString filePath) {
+void FileUtils::locateFile(QString filePath) {
 
-    // adopted from
+    // adapted from
     // http://stackoverflow.com/questions/3490336/how-to-reveal-in-finder-or-show-in-explorer-with-qt
     // and
     // http://lynxline.com/show-in-finder-show-in-explorer/
@@ -54,3 +54,26 @@ void FileUtils::LocateFile(QString filePath) {
         QDesktopServices::openUrl(QUrl::fromLocalFile(folder));
     }
 }
+
+QString FileUtils::standardPath(QString subfolder) {
+    // standard path
+    // Mac: ~/Library/Application Support/Interface
+    QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
+    path.append("/Interface");
+    
+    if (!subfolder.startsWith("/")) {
+        subfolder.prepend("/");
+    }
+    
+    if (!subfolder.endsWith("/")) {
+        subfolder.append("/");
+    }
+    
+    path.append(subfolder);
+    QDir logDir(path);
+    if (!logDir.exists(path)) {
+        logDir.mkpath(path);
+    }
+    
+    return path;
+}
diff --git a/libraries/shared/src/FileUtils.h b/libraries/shared/src/FileUtils.h
index a725e72ca1..dd4605218e 100644
--- a/libraries/shared/src/FileUtils.h
+++ b/libraries/shared/src/FileUtils.h
@@ -14,7 +14,8 @@
 class FileUtils {
 
 public:
-    static void LocateFile(QString);
+    static void locateFile(QString fileName);
+    static QString standardPath(QString subfolder);
 
 };
 
diff --git a/libraries/shared/src/HifiSockAddr.cpp b/libraries/shared/src/HifiSockAddr.cpp
index 4c9772b1c8..44eedc6b17 100644
--- a/libraries/shared/src/HifiSockAddr.cpp
+++ b/libraries/shared/src/HifiSockAddr.cpp
@@ -8,6 +8,7 @@
 
 #include "HifiSockAddr.h"
 
+#include 
 #include 
 #include 
 
@@ -59,31 +60,25 @@ void HifiSockAddr::swap(HifiSockAddr& otherSockAddr) {
     swap(_port, otherSockAddr._port);
 }
 
-int HifiSockAddr::packSockAddr(unsigned char* packetData, const HifiSockAddr& packSockAddr) {
-    quint32 addressToPack = packSockAddr._address.isNull() ? 0 : packSockAddr._address.toIPv4Address();
-    memcpy(packetData, &addressToPack, sizeof(addressToPack));
-    memcpy(packetData + sizeof(addressToPack), &packSockAddr._port, sizeof(packSockAddr._port));
-    
-    return sizeof(addressToPack) + sizeof(packSockAddr._port);
-}
-
-int HifiSockAddr::unpackSockAddr(const unsigned char* packetData, HifiSockAddr& unpackDestSockAddr) {
-    quint32* address = (quint32*) packetData;
-    unpackDestSockAddr._address = *address == 0 ? QHostAddress() : QHostAddress(*address);
-    unpackDestSockAddr._port = *((quint16*) (packetData + sizeof(quint32)));
-    
-    return sizeof(quint32) + sizeof(quint16);
-}
-
-bool HifiSockAddr::operator==(const HifiSockAddr &rhsSockAddr) const {
+bool HifiSockAddr::operator==(const HifiSockAddr& rhsSockAddr) const {
     return _address == rhsSockAddr._address && _port == rhsSockAddr._port;
 }
 
-QDebug operator<<(QDebug debug, const HifiSockAddr &hifiSockAddr) {
-    debug.nospace() << hifiSockAddr._address.toString().toLocal8Bit().constData() << ":" << hifiSockAddr._port;
+QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr) {
+    debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port;
     return debug.space();
 }
 
+QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr) {
+    dataStream << sockAddr._address << sockAddr._port;
+    return dataStream;
+}
+
+QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr) {
+    dataStream >> sockAddr._address >> sockAddr._port;
+    return dataStream;
+}
+
 quint32 getHostOrderLocalAddress() {
     
     static int localAddress = 0;
diff --git a/libraries/shared/src/HifiSockAddr.h b/libraries/shared/src/HifiSockAddr.h
index f11492805f..e8f928c36d 100644
--- a/libraries/shared/src/HifiSockAddr.h
+++ b/libraries/shared/src/HifiSockAddr.h
@@ -37,7 +37,9 @@ public:
     static int packSockAddr(unsigned char* packetData, const HifiSockAddr& packSockAddr);
     static int unpackSockAddr(const unsigned char* packetData, HifiSockAddr& unpackDestSockAddr);
     
-    friend QDebug operator<<(QDebug debug, const HifiSockAddr &hifiSockAddr);
+    friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr);
+    friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr);
+    friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr);
 private:
     QHostAddress _address;
     quint16 _port;
diff --git a/libraries/shared/src/NetworkPacket.cpp b/libraries/shared/src/NetworkPacket.cpp
index 768e82cab3..aacd95dbbf 100644
--- a/libraries/shared/src/NetworkPacket.cpp
+++ b/libraries/shared/src/NetworkPacket.cpp
@@ -14,49 +14,38 @@
 
 #include "NetworkPacket.h"
 
-NetworkPacket::NetworkPacket() {
-    _packetLength = 0;
-}
-
-NetworkPacket::~NetworkPacket() {
-    // nothing to do
-}
-
-void NetworkPacket::copyContents(const HifiSockAddr& sockAddr, const unsigned char*  packetData, ssize_t packetLength) {
-    _packetLength = 0;
-    if (packetLength >=0 && packetLength <= MAX_PACKET_SIZE) {
+void NetworkPacket::copyContents(const HifiSockAddr& sockAddr, const QByteArray& packet) {
+    if (packet.size() && packet.size() <= MAX_PACKET_SIZE) {
         _sockAddr = sockAddr;
-        _packetLength = packetLength;
-        memcpy(&_packetData[0], packetData, packetLength);
+        _byteArray = packet;
     } else {
-        qDebug(">>> NetworkPacket::copyContents() unexpected length=%lu",packetLength);
+        qDebug(">>> NetworkPacket::copyContents() unexpected length = %d", packet.size());
     }
 }
 
 NetworkPacket::NetworkPacket(const NetworkPacket& packet) {
-    copyContents(packet.getSockAddr(), packet.getData(), packet.getLength());
+    copyContents(packet.getSockAddr(), packet.getByteArray());
 }
 
-NetworkPacket::NetworkPacket(const HifiSockAddr& sockAddr, unsigned char*  packetData, ssize_t packetLength) {
-    copyContents(sockAddr, packetData, packetLength);
+NetworkPacket::NetworkPacket(const HifiSockAddr& sockAddr, const QByteArray& packet) {
+    copyContents(sockAddr, packet);
 };
 
 // copy assignment 
 NetworkPacket& NetworkPacket::operator=(NetworkPacket const& other) {
-    copyContents(other.getSockAddr(), other.getData(), other.getLength());
+    copyContents(other.getSockAddr(), other.getByteArray());
     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());
+    copyContents(packet.getAddress(), packet.getByteArray());
 }
 
 // move assignment
 NetworkPacket& NetworkPacket::operator=(NetworkPacket&& other) {
-    _packetLength = 0;
-    copyContents(other.getAddress(), other.getData(), other.getLength());
+    copyContents(other.getAddress(), other.getByteArray());
     return *this;
 }
 #endif
\ No newline at end of file
diff --git a/libraries/shared/src/NetworkPacket.h b/libraries/shared/src/NetworkPacket.h
index 7eef1e7129..480a793a49 100644
--- a/libraries/shared/src/NetworkPacket.h
+++ b/libraries/shared/src/NetworkPacket.h
@@ -25,9 +25,7 @@
 /// Storage of not-yet processed inbound, or not yet sent outbound generic UDP network packet
 class NetworkPacket {
 public:
-    NetworkPacket();
     NetworkPacket(const NetworkPacket& packet); // copy constructor
-    ~NetworkPacket(); // destructor
     NetworkPacket& operator= (const NetworkPacket& other);    // copy assignment
 
 #ifdef HAS_MOVE_SEMANTICS
@@ -35,21 +33,16 @@ public:
     NetworkPacket& operator= (NetworkPacket&& other);         // move assignment
 #endif
 
-    NetworkPacket(const HifiSockAddr& sockAddr, unsigned char*  packetData, ssize_t packetLength);
+    NetworkPacket(const HifiSockAddr& sockAddr, const QByteArray& byteArray);
 
     const HifiSockAddr& getSockAddr() const { return _sockAddr; }
-    ssize_t getLength() const { return _packetLength; }
-    unsigned char* getData() { return &_packetData[0]; }
-
-    const HifiSockAddr& getAddress() const { return _sockAddr; }
-    const unsigned char* getData() const { return &_packetData[0]; }
+    const QByteArray& getByteArray() const { return _byteArray; }
 
 private:
-    void copyContents(const HifiSockAddr& sockAddr, const unsigned char*  packetData, ssize_t packetLength);
+    void copyContents(const HifiSockAddr& sockAddr, const QByteArray& byteArray);
 
     HifiSockAddr _sockAddr;
-    ssize_t _packetLength;
-    unsigned char _packetData[MAX_PACKET_SIZE];
+    QByteArray _byteArray;
 };
 
 #endif /* defined(__shared_NetworkPacket__) */
diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp
index 603e1febba..8efbf5782a 100644
--- a/libraries/shared/src/Node.cpp
+++ b/libraries/shared/src/Node.cpp
@@ -16,11 +16,34 @@
 #endif
 
 #include "Node.h"
-#include "NodeTypes.h"
 #include "SharedUtil.h"
 
+#include 
 #include 
 
+const QString UNKNOWN_NodeType_t_NAME = "Unknown";
+
+namespace NodeType {
+    QHash TypeNameHash;
+}
+
+void NodeType::init() {
+    TypeNameHash.insert(NodeType::DomainServer, "Domain Server");
+    TypeNameHash.insert(NodeType::VoxelServer, "Voxel Server");
+    TypeNameHash.insert(NodeType::ParticleServer, "Particle Server");
+    TypeNameHash.insert(NodeType::MetavoxelServer, "Metavoxel Server");
+    TypeNameHash.insert(NodeType::Agent, "Agent");
+    TypeNameHash.insert(NodeType::AudioMixer, "Audio Mixer");
+    TypeNameHash.insert(NodeType::AvatarMixer, "Avatar Mixer");
+    TypeNameHash.insert(NodeType::AnimationServer, "Animation Server");
+    TypeNameHash.insert(NodeType::Unassigned, "Unassigned");
+}
+
+const QString& NodeType::getNodeTypeName(NodeType_t nodeType) {
+    QHash::iterator matchedTypeName = TypeNameHash.find(nodeType);
+    return matchedTypeName != TypeNameHash.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME;
+}
+
 Node::Node(const QUuid& uuid, char type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) :
     _type(type),
     _uuid(uuid),
@@ -45,44 +68,6 @@ Node::~Node() {
     delete _bytesReceivedMovingAverage;
 }
 
-// Names of Node Types
-const char* NODE_TYPE_NAME_DOMAIN = "Domain";
-const char* NODE_TYPE_NAME_VOXEL_SERVER = "Voxel Server";
-const char* NODE_TYPE_NAME_PARTICLE_SERVER = "Particle Server";
-const char* NODE_TYPE_NAME_METAVOXEL_SERVER = "Metavoxel Server";
-const char* NODE_TYPE_NAME_AGENT = "Agent";
-const char* NODE_TYPE_NAME_AUDIO_MIXER = "Audio Mixer";
-const char* NODE_TYPE_NAME_AVATAR_MIXER = "Avatar Mixer";
-const char* NODE_TYPE_NAME_AUDIO_INJECTOR = "Audio Injector";
-const char* NODE_TYPE_NAME_ANIMATION_SERVER = "Animation Server";
-const char* NODE_TYPE_NAME_UNASSIGNED = "Unassigned";
-const char* NODE_TYPE_NAME_UNKNOWN = "Unknown";
-
-const char* Node::getTypeName() const {
-	switch (this->_type) {
-		case NODE_TYPE_DOMAIN:
-			return NODE_TYPE_NAME_DOMAIN;
-		case NODE_TYPE_VOXEL_SERVER:
-			return NODE_TYPE_NAME_VOXEL_SERVER;
-		case NODE_TYPE_PARTICLE_SERVER:
-		    return NODE_TYPE_NAME_PARTICLE_SERVER;
-		case NODE_TYPE_METAVOXEL_SERVER:
-		    return NODE_TYPE_NAME_METAVOXEL_SERVER;
-		case NODE_TYPE_AGENT:
-			return NODE_TYPE_NAME_AGENT;
-		case NODE_TYPE_AUDIO_MIXER:
-			return NODE_TYPE_NAME_AUDIO_MIXER;
-        case NODE_TYPE_AVATAR_MIXER:
-            return NODE_TYPE_NAME_AVATAR_MIXER;
-        case NODE_TYPE_ANIMATION_SERVER:
-            return NODE_TYPE_NAME_ANIMATION_SERVER;
-        case NODE_TYPE_UNASSIGNED:
-            return NODE_TYPE_NAME_UNASSIGNED;
-        default:
-            return NODE_TYPE_NAME_UNKNOWN;
-	}
-}
-
 void Node::setPublicSocket(const HifiSockAddr& publicSocket) {
     if (_activeSocket == &_publicSocket) {
         // if the active socket was the public socket then reset it to NULL
@@ -135,8 +120,26 @@ float Node::getAverageKilobitsPerSecond() {
     }
 }
 
+QDataStream& operator<<(QDataStream& out, const Node& node) {
+    out << node._type;
+    out << node._uuid;
+    out << node._publicSocket;
+    out << node._localSocket;
+    
+    return out;
+}
+
+QDataStream& operator>>(QDataStream& in, Node& node) {
+    in >> node._type;
+    in >> node._uuid;
+    in >> node._publicSocket;
+    in >> node._localSocket;
+    
+    return in;
+}
+
 QDebug operator<<(QDebug debug, const Node &node) {
-    debug.nospace() << node.getTypeName() << " (" << node.getType() << ")";
+    debug.nospace() << NodeType::getNodeTypeName(node.getType()) << " (" << node.getType() << ")";
     debug << " " << node.getUUID().toString().toLocal8Bit().constData() << " ";
     debug.nospace() << node.getPublicSocket() << "/" << node.getLocalSocket();
     return debug.nospace();
diff --git a/libraries/shared/src/Node.h b/libraries/shared/src/Node.h
index 352498cd61..952e1b1be2 100644
--- a/libraries/shared/src/Node.h
+++ b/libraries/shared/src/Node.h
@@ -27,6 +27,24 @@
 #include "NodeData.h"
 #include "SimpleMovingAverage.h"
 
+typedef quint8 NodeType_t;
+
+namespace NodeType {
+    const NodeType_t DomainServer = 'D';
+    const NodeType_t VoxelServer = 'V';
+    const NodeType_t ParticleServer = 'P';
+    const NodeType_t MetavoxelServer = 'm';
+    const NodeType_t EnvironmentServer = 'E';
+    const NodeType_t Agent = 'I';
+    const NodeType_t AudioMixer = 'M';
+    const NodeType_t AvatarMixer = 'W';
+    const NodeType_t AnimationServer = 'a';
+    const NodeType_t Unassigned = 1;
+    
+    void init();
+    const QString& getNodeTypeName(NodeType_t nodeType);
+}
+
 class Node : public QObject {
     Q_OBJECT
 public:
@@ -38,16 +56,16 @@ public:
 
     char getType() const { return _type; }
     void setType(char type) { _type = type; }
-    const char* getTypeName() const;
+    
 
     const QUuid& getUUID() const { return _uuid; }
     void setUUID(const QUuid& uuid) { _uuid = uuid; }
 
-    uint64_t getWakeMicrostamp() const { return _wakeMicrostamp; }
-    void setWakeMicrostamp(uint64_t wakeMicrostamp) { _wakeMicrostamp = wakeMicrostamp; }
+    quint64 getWakeMicrostamp() const { return _wakeMicrostamp; }
+    void setWakeMicrostamp(quint64 wakeMicrostamp) { _wakeMicrostamp = wakeMicrostamp; }
 
-    uint64_t getLastHeardMicrostamp() const { return _lastHeardMicrostamp; }
-    void setLastHeardMicrostamp(uint64_t lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
+    quint64 getLastHeardMicrostamp() const { return _lastHeardMicrostamp; }
+    void setLastHeardMicrostamp(quint64 lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
 
     const HifiSockAddr& getPublicSocket() const { return _publicSocket; }
     void setPublicSocket(const HifiSockAddr& publicSocket);
@@ -75,16 +93,19 @@ public:
     int getClockSkewUsec() const { return _clockSkewUsec; }
     void setClockSkewUsec(int clockSkew) { _clockSkewUsec = clockSkew; }
     QMutex& getMutex() { return _mutex; }
+    
+    friend QDataStream& operator<<(QDataStream& out, const Node& node);
+    friend QDataStream& operator>>(QDataStream& in, Node& node);
 
 private:
     // privatize copy and assignment operator to disallow Node copying
     Node(const Node &otherNode);
     Node& operator=(Node otherNode);
 
-    char _type;
+    NodeType_t _type;
     QUuid _uuid;
-    uint64_t _wakeMicrostamp;
-    uint64_t _lastHeardMicrostamp;
+    quint64 _wakeMicrostamp;
+    quint64 _lastHeardMicrostamp;
     HifiSockAddr _publicSocket;
     HifiSockAddr _localSocket;
     HifiSockAddr* _activeSocket;
diff --git a/libraries/shared/src/NodeData.h b/libraries/shared/src/NodeData.h
index af5d13cd04..cf800fc3cd 100644
--- a/libraries/shared/src/NodeData.h
+++ b/libraries/shared/src/NodeData.h
@@ -18,7 +18,7 @@ class NodeData : public QObject {
 public:
     
     virtual ~NodeData() = 0;
-    virtual int parseData(unsigned char* sourceBuffer, int numBytes) = 0;
+    virtual int parseData(const QByteArray& packet) = 0;
 };
 
 #endif
diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp
index 5c5791d475..cd9356ab15 100644
--- a/libraries/shared/src/NodeList.cpp
+++ b/libraries/shared/src/NodeList.cpp
@@ -10,6 +10,7 @@
 #include 
 #include 
 
+#include 
 #include 
 #include 
 
@@ -17,14 +18,13 @@
 #include "HifiSockAddr.h"
 #include "Logging.h"
 #include "NodeList.h"
-#include "NodeTypes.h"
 #include "PacketHeaders.h"
 #include "SharedUtil.h"
 #include "UUID.h"
 
 const char SOLO_NODE_TYPES[2] = {
-    NODE_TYPE_AVATAR_MIXER,
-    NODE_TYPE_AUDIO_MIXER
+    NodeType::AvatarMixer,
+    NodeType::AudioMixer
 };
 
 const QString DEFAULT_DOMAIN_HOSTNAME = "alpha.highfidelity.io";
@@ -34,6 +34,8 @@ NodeList* NodeList::_sharedInstance = NULL;
 
 NodeList* NodeList::createInstance(char ownerType, unsigned short int socketListenPort) {
     if (!_sharedInstance) {
+        NodeType::init();
+        
         _sharedInstance = new NodeList(ownerType, socketListenPort);
 
         // register the SharedNodePointer meta-type for signals/slots
@@ -109,79 +111,83 @@ void NodeList::setDomainHostname(const QString& domainHostname) {
     }
 }
 
-void NodeList::timePingReply(const HifiSockAddr& nodeAddress, unsigned char *packetData) {
-    foreach (const SharedNodePointer& node, _nodeHash) {
-        if (node->getPublicSocket() == nodeAddress ||
-            node->getLocalSocket() == nodeAddress) {
-
-            unsigned char* dataAt = packetData + numBytesForPacketHeader(packetData);
-            uint64_t ourOriginalTime = *(uint64_t*)(dataAt);
-            dataAt += sizeof(ourOriginalTime);
-            uint64_t othersReplyTime = *(uint64_t*)(dataAt);
-            uint64_t now = usecTimestampNow();
-            int pingTime = now - ourOriginalTime;
-            int oneWayFlightTime = pingTime / 2; // half of the ping is our one way flight
-
-            // The other node's expected time should be our original time plus the one way flight time
-            // anything other than that is clock skew
-            uint64_t othersExprectedReply = ourOriginalTime + oneWayFlightTime;
-            int clockSkew = othersReplyTime - othersExprectedReply;
-
-            node->setPingMs(pingTime / 1000);
-            node->setClockSkewUsec(clockSkew);
-
-            const bool wantDebug = false;
-            if (wantDebug) {
-                qDebug() << "PING_REPLY from node " << *node << "\n" <<
-                    "                     now: " << now << "\n" <<
-                    "                 ourTime: " << ourOriginalTime << "\n" <<
-                    "                pingTime: " << pingTime << "\n" <<
-                    "        oneWayFlightTime: " << oneWayFlightTime << "\n" <<
-                    "         othersReplyTime: " << othersReplyTime << "\n" <<
-                    "    othersExprectedReply: " << othersExprectedReply << "\n" <<
-                    "               clockSkew: " << clockSkew;
-            }
-            break;
+void NodeList::timePingReply(const QByteArray& packet) {
+    QUuid nodeUUID;
+    deconstructPacketHeader(packet, nodeUUID);
+    
+    SharedNodePointer matchingNode = nodeWithUUID(nodeUUID);
+    
+    if (matchingNode) {
+        QDataStream packetStream(packet);
+        packetStream.skipRawData(numBytesForPacketHeader(packet));
+        
+        quint64 ourOriginalTime, othersReplyTime;
+        
+        packetStream >> ourOriginalTime >> othersReplyTime;
+        
+        quint64 now = usecTimestampNow();
+        int pingTime = now - ourOriginalTime;
+        int oneWayFlightTime = pingTime / 2; // half of the ping is our one way flight
+        
+        // The other node's expected time should be our original time plus the one way flight time
+        // anything other than that is clock skew
+        quint64 othersExprectedReply = ourOriginalTime + oneWayFlightTime;
+        int clockSkew = othersReplyTime - othersExprectedReply;
+        
+        matchingNode->setPingMs(pingTime / 1000);
+        matchingNode->setClockSkewUsec(clockSkew);
+        
+        const bool wantDebug = false;
+        
+        if (wantDebug) {
+            qDebug() << "PING_REPLY from node " << *matchingNode << "\n" <<
+            "                     now: " << now << "\n" <<
+            "                 ourTime: " << ourOriginalTime << "\n" <<
+            "                pingTime: " << pingTime << "\n" <<
+            "        oneWayFlightTime: " << oneWayFlightTime << "\n" <<
+            "         othersReplyTime: " << othersReplyTime << "\n" <<
+            "    othersExprectedReply: " << othersExprectedReply << "\n" <<
+            "               clockSkew: " << clockSkew;
         }
     }
 }
 
-void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, unsigned char* packetData, size_t dataBytes) {
-    switch (packetData[0]) {
-        case PACKET_TYPE_DOMAIN: {
+void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
+    switch (packetTypeForPacket(packet)) {
+        case PacketTypeDomainList: {
             // only process the DS if this is our current domain server
             if (_domainSockAddr == senderSockAddr) {
-                processDomainServerList(packetData, dataBytes);
+                processDomainServerList(packet);
             }
 
             break;
         }
-        case PACKET_TYPE_PING: {
+        case PacketTypePing: {
             // send back a reply
-            unsigned char replyPacket[MAX_PACKET_SIZE];
-            int replyPacketLength = fillPingReplyPacket(packetData, replyPacket);
-            _nodeSocket.writeDatagram((char*)replyPacket, replyPacketLength,
-                            senderSockAddr.getAddress(), senderSockAddr.getPort());
+            QByteArray replyPacket = constructPingReplyPacket(packet);
+            _nodeSocket.writeDatagram(replyPacket, senderSockAddr.getAddress(), senderSockAddr.getPort());
             break;
         }
-        case PACKET_TYPE_PING_REPLY: {
+        case PacketTypePingReply: {
             // activate the appropriate socket for this node, if not yet updated
             activateSocketFromNodeCommunication(senderSockAddr);
 
             // set the ping time for this node for stat collection
-            timePingReply(senderSockAddr, packetData);
+            timePingReply(packet);
             break;
         }
-        case PACKET_TYPE_STUN_RESPONSE: {
+        case PacketTypeStunResponse: {
             // a STUN packet begins with 00, we've checked the second zero with packetVersionMatch
             // pass it along so it can be processed into our public address and port
-            processSTUNResponse(packetData, dataBytes);
+            processSTUNResponse(packet);
             break;
         }
+        default:
+            break;
     }
 }
 
-int NodeList::updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, unsigned char *packetData, int dataBytes) {
+int NodeList::updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
     QMutexLocker locker(&node->getMutex());
 
     node->setLastHeardMicrostamp(usecTimestampNow());
@@ -195,13 +201,13 @@ int NodeList::updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr,
     }
 
     if (node->getActiveSocket() || senderSockAddr.isNull()) {
-        node->recordBytesReceived(dataBytes);
+        node->recordBytesReceived(packet.size());
 
         if (!node->getLinkedData() && linkedDataCreateCallback) {
             linkedDataCreateCallback(node);
         }
 
-        int numParsedBytes = node->getLinkedData()->parseData(packetData, dataBytes);
+        int numParsedBytes = node->getLinkedData()->parseData(packet);
         return numParsedBytes;
     } else {
         // we weren't able to match the sender address to the address we have for this node, unlock and don't parse
@@ -254,11 +260,11 @@ void NodeList::reset() {
     _ownerUUID = QUuid::createUuid();
 }
 
-void NodeList::addNodeTypeToInterestSet(NODE_TYPE nodeTypeToAdd) {
+void NodeList::addNodeTypeToInterestSet(NodeType_t nodeTypeToAdd) {
     _nodeTypesOfInterest << nodeTypeToAdd;
 }
 
-void NodeList::addSetOfNodeTypesToNodeInterestSet(const QSet& setOfNodeTypes) {
+void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes) {
     _nodeTypesOfInterest.unite(setOfNodeTypes);
 }
 
@@ -323,7 +329,7 @@ void NodeList::sendSTUNRequest() {
     }
 }
 
-void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes) {
+void NodeList::processSTUNResponse(const QByteArray& packet) {
     // check the cookie to make sure this is actually a STUN response
     // and read the first attribute and make sure it is a XOR_MAPPED_ADDRESS
     const int NUM_BYTES_MESSAGE_TYPE_AND_LENGTH = 4;
@@ -333,13 +339,13 @@ void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes)
 
     size_t attributeStartIndex = NUM_BYTES_STUN_HEADER;
 
-    if (memcmp(packetData + NUM_BYTES_MESSAGE_TYPE_AND_LENGTH,
+    if (memcmp(packet.data() + NUM_BYTES_MESSAGE_TYPE_AND_LENGTH,
                &RFC_5389_MAGIC_COOKIE_NETWORK_ORDER,
                sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER)) == 0) {
 
         // enumerate the attributes to find XOR_MAPPED_ADDRESS_TYPE
-        while (attributeStartIndex < dataBytes) {
-            if (memcmp(packetData + attributeStartIndex, &XOR_MAPPED_ADDRESS_TYPE, sizeof(XOR_MAPPED_ADDRESS_TYPE)) == 0) {
+        while (attributeStartIndex < packet.size()) {
+            if (memcmp(packet.data() + attributeStartIndex, &XOR_MAPPED_ADDRESS_TYPE, sizeof(XOR_MAPPED_ADDRESS_TYPE)) == 0) {
                 const int NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH = 4;
                 const int NUM_BYTES_FAMILY_ALIGN = 1;
                 const uint8_t IPV4_FAMILY_NETWORK_ORDER = htons(0x01) >> 8;
@@ -347,17 +353,17 @@ void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes)
                 // reset the number of failed STUN requests since last success
                 _stunRequestsSinceSuccess = 0;
 
-                int byteIndex = attributeStartIndex +  NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN;
-
+                int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN;
+                
                 uint8_t addressFamily = 0;
-                memcpy(&addressFamily, packetData + byteIndex, sizeof(addressFamily));
+                memcpy(&addressFamily, packet.data(), sizeof(addressFamily));
 
                 byteIndex += sizeof(addressFamily);
 
                 if (addressFamily == IPV4_FAMILY_NETWORK_ORDER) {
                     // grab the X-Port
                     uint16_t xorMappedPort = 0;
-                    memcpy(&xorMappedPort, packetData + byteIndex, sizeof(xorMappedPort));
+                    memcpy(&xorMappedPort, packet.data() + byteIndex, sizeof(xorMappedPort));
 
                     uint16_t newPublicPort = ntohs(xorMappedPort) ^ (ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER) >> 16);
 
@@ -365,7 +371,7 @@ void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes)
 
                     // grab the X-Address
                     uint32_t xorMappedAddress = 0;
-                    memcpy(&xorMappedAddress, packetData + byteIndex, sizeof(xorMappedAddress));
+                    memcpy(&xorMappedAddress, packet.data() + byteIndex, sizeof(xorMappedAddress));
 
                     uint32_t stunAddress = ntohl(xorMappedAddress) ^ ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
 
@@ -389,7 +395,8 @@ void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes)
                 const int NUM_BYTES_ATTRIBUTE_TYPE = 2;
 
                 uint16_t attributeLength = 0;
-                memcpy(&attributeLength, packetData + attributeStartIndex + NUM_BYTES_ATTRIBUTE_TYPE, sizeof(attributeLength));
+                memcpy(&attributeLength, packet.data() + attributeStartIndex + NUM_BYTES_ATTRIBUTE_TYPE,
+                       sizeof(attributeLength));
                 attributeLength = ntohs(attributeLength);
 
                 attributeStartIndex += NUM_BYTES_MESSAGE_TYPE_AND_LENGTH + attributeLength;
@@ -415,11 +422,8 @@ NodeHash::iterator NodeList::killNodeAtHashIterator(NodeHash::iterator& nodeItem
 
 void NodeList::processKillNode(const QByteArray& dataByteArray) {
     // read the node id
-    QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader(reinterpret_cast
-                                                                                  (dataByteArray.data())),
-                                                          NUM_BYTES_RFC4122_UUID));
+    QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader(dataByteArray), NUM_BYTES_RFC4122_UUID));
 
-    
     // kill the node with this UUID, if it exists
     killNodeWithUUID(nodeUUID);
 }
@@ -459,53 +463,22 @@ void NodeList::sendDomainServerCheckIn() {
         sendSTUNRequest();
     } else {
         // construct the DS check in packet if we need to
-        int numBytesNodesOfInterest = _nodeTypesOfInterest.size();
-
-        const int IP_ADDRESS_BYTES = 4;
 
         // check in packet has header, optional UUID, node type, port, IP, node types of interest, null termination
-        #ifdef _WIN32
-        const int numPacketBytes = MAX_PACKET_SIZE;
-        #else
-        int numPacketBytes = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION) + sizeof(NODE_TYPE) +
-            NUM_BYTES_RFC4122_UUID + (2 * (sizeof(uint16_t) + IP_ADDRESS_BYTES)) +
-            numBytesNodesOfInterest + sizeof(unsigned char);
-        #endif
-
-        unsigned char checkInPacket[numPacketBytes];
-        unsigned char* packetPosition = checkInPacket;
-
-        PACKET_TYPE nodePacketType = (memchr(SOLO_NODE_TYPES, _ownerType, sizeof(SOLO_NODE_TYPES)))
-            ? PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY
-            : PACKET_TYPE_DOMAIN_LIST_REQUEST;
-
-        packetPosition += populateTypeAndVersion(packetPosition, nodePacketType);
-
-        *(packetPosition++) = _ownerType;
-
-        // send our owner UUID or the null one
-        QByteArray rfcOwnerUUID = _ownerUUID.toRfc4122();
-        memcpy(packetPosition, rfcOwnerUUID.constData(), rfcOwnerUUID.size());
-        packetPosition += rfcOwnerUUID.size();
-
-        // pack our public address to send to domain-server
-        packetPosition += HifiSockAddr::packSockAddr(checkInPacket + (packetPosition - checkInPacket), _publicSockAddr);
-
-        // pack our local address to send to domain-server
-        packetPosition += HifiSockAddr::packSockAddr(checkInPacket + (packetPosition - checkInPacket),
-                                                     HifiSockAddr(QHostAddress(getHostOrderLocalAddress()),
-                                                                  _nodeSocket.localPort()));
-
-        // add the number of bytes for node types of interest
-        *(packetPosition++) = numBytesNodesOfInterest;
+        QByteArray domainServerPacket = byteArrayWithPopluatedHeader(PacketTypeDomainListRequest);
+        QDataStream packetStream(&domainServerPacket, QIODevice::Append);
 
+        // pack our data to send to the domain-server
+        packetStream << _ownerType << _publicSockAddr
+            << HifiSockAddr(QHostAddress(getHostOrderLocalAddress()), _nodeSocket.localPort())
+            << (quint8) _nodeTypesOfInterest.size();
+        
         // copy over the bytes for node types of interest, if required
-        foreach (NODE_TYPE nodeTypeOfInterest, _nodeTypesOfInterest) {
-            *(packetPosition++) = nodeTypeOfInterest;
+        foreach (NodeType_t nodeTypeOfInterest, _nodeTypesOfInterest) {
+            packetStream << nodeTypeOfInterest;
         }
-
-        _nodeSocket.writeDatagram((char*) checkInPacket, packetPosition - checkInPacket,
-                                  _domainSockAddr.getAddress(), _domainSockAddr.getPort());
+        
+        _nodeSocket.writeDatagram(domainServerPacket, _domainSockAddr.getAddress(), _domainSockAddr.getPort());
         const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;
         static unsigned int numDomainCheckins = 0;
 
@@ -519,28 +492,25 @@ void NodeList::sendDomainServerCheckIn() {
     }
 }
 
-int NodeList::processDomainServerList(unsigned char* packetData, size_t dataBytes) {
+int NodeList::processDomainServerList(const QByteArray& packet) {
     // this is a packet from the domain server, reset the count of un-replied check-ins
     _numNoReplyDomainCheckIns = 0;
 
     int readNodes = 0;
+    
+    // setup variables to read into from QDataStream
+    qint8 nodeType;
+    
+    QUuid nodeUUID;
 
-    char nodeType;
-
-    // assumes only IPv4 addresses
     HifiSockAddr nodePublicSocket;
     HifiSockAddr nodeLocalSocket;
+    
+    QDataStream packetStream(packet);
+    packetStream.skipRawData(numBytesForPacketHeader(packet));
 
-    unsigned char* readPtr = packetData + numBytesForPacketHeader(packetData);
-    unsigned char* startPtr = packetData;
-
-    while((size_t)(readPtr - startPtr) < dataBytes - sizeof(uint16_t)) {
-        nodeType = *readPtr++;
-        QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) readPtr, NUM_BYTES_RFC4122_UUID));
-        readPtr += NUM_BYTES_RFC4122_UUID;
-
-        readPtr += HifiSockAddr::unpackSockAddr(readPtr, nodePublicSocket);
-        readPtr += HifiSockAddr::unpackSockAddr(readPtr, nodeLocalSocket);
+    while(packetStream.device()->pos() < packet.size()) {
+        packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket;
 
         // if the public socket address is 0 then it's reachable at the same IP
         // as the domain server
@@ -551,19 +521,19 @@ int NodeList::processDomainServerList(unsigned char* packetData, size_t dataByte
         addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, nodeLocalSocket);
     }
 
-
     return readNodes;
 }
 
 void NodeList::sendAssignment(Assignment& assignment) {
-    unsigned char assignmentPacket[MAX_PACKET_SIZE];
-
-    PACKET_TYPE assignmentPacketType = assignment.getCommand() == Assignment::CreateCommand
-        ? PACKET_TYPE_CREATE_ASSIGNMENT
-        : PACKET_TYPE_REQUEST_ASSIGNMENT;
-
-    int numHeaderBytes = populateTypeAndVersion(assignmentPacket, assignmentPacketType);
-    int numAssignmentBytes = assignment.packToBuffer(assignmentPacket + numHeaderBytes);
+    
+    PacketType assignmentPacketType = assignment.getCommand() == Assignment::CreateCommand
+        ? PacketTypeCreateAssignment
+        : PacketTypeRequestAssignment;
+    
+    QByteArray packet = byteArrayWithPopluatedHeader(assignmentPacketType);
+    QDataStream packetStream(&packet, QIODevice::Append);
+    
+    packetStream << assignment;
 
     static HifiSockAddr DEFAULT_ASSIGNMENT_SOCKET(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME, DEFAULT_DOMAIN_SERVER_PORT);
 
@@ -571,58 +541,43 @@ void NodeList::sendAssignment(Assignment& assignment) {
         ? &DEFAULT_ASSIGNMENT_SOCKET
         : &_assignmentServerSocket;
 
-    _nodeSocket.writeDatagram((char*) assignmentPacket, numHeaderBytes + numAssignmentBytes,
-                              assignmentServerSocket->getAddress(),
-                              assignmentServerSocket->getPort());
+    _nodeSocket.writeDatagram(packet, assignmentServerSocket->getAddress(), assignmentServerSocket->getPort());
 }
 
-int NodeList::packOwnerUUID(unsigned char* packetData) {
-    QByteArray rfcUUID = _ownerUUID.toRfc4122();
-    memcpy(packetData, rfcUUID.constData(), rfcUUID.size());
-    return rfcUUID.size();
+QByteArray NodeList::constructPingPacket() {
+    QByteArray pingPacket = byteArrayWithPopluatedHeader(PacketTypePing);
+    
+    QDataStream packetStream(&pingPacket, QIODevice::Append);
+    packetStream << usecTimestampNow();
+    
+    return pingPacket;
 }
 
-int NodeList::fillPingPacket(unsigned char* buffer) {
-    int numHeaderBytes = populateTypeAndVersion(buffer, PACKET_TYPE_PING);
-    uint64_t currentTime = usecTimestampNow();
-    memcpy(buffer + numHeaderBytes, ¤tTime, sizeof(currentTime));
-    return numHeaderBytes + sizeof(currentTime);
+QByteArray NodeList::constructPingReplyPacket(const QByteArray& pingPacket) {
+    QDataStream pingPacketStream(pingPacket);
+    pingPacketStream.skipRawData(numBytesForPacketHeader(pingPacket));
+    
+    quint64 timeFromOriginalPing;
+    pingPacketStream >> timeFromOriginalPing;
+    
+    QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypePingReply);
+    QDataStream packetStream(&replyPacket, QIODevice::Append);
+    
+    packetStream << timeFromOriginalPing << usecTimestampNow();
+    
+    return replyPacket;
 }
 
-int NodeList::fillPingReplyPacket(unsigned char* pingBuffer, unsigned char* replyBuffer) {
-    int numHeaderBytesOriginal = numBytesForPacketHeader(pingBuffer);
-    uint64_t timeFromOriginalPing = *(uint64_t*)(pingBuffer + numHeaderBytesOriginal);
-
-    int numHeaderBytesReply = populateTypeAndVersion(replyBuffer, PACKET_TYPE_PING_REPLY);
-    int length = numHeaderBytesReply;
-    uint64_t ourReplyTime = usecTimestampNow();
-
-    unsigned char* dataAt = replyBuffer + numHeaderBytesReply;
-    memcpy(dataAt, &timeFromOriginalPing, sizeof(timeFromOriginalPing));
-    dataAt += sizeof(timeFromOriginalPing);
-    length += sizeof(timeFromOriginalPing);
-
-    memcpy(dataAt, &ourReplyTime, sizeof(ourReplyTime));
-    dataAt += sizeof(ourReplyTime);
-    length += sizeof(ourReplyTime);
-
-    return length;
-}
-
-
 void NodeList::pingPublicAndLocalSocketsForInactiveNode(Node* node) {
-    unsigned char pingPacket[MAX_PACKET_SIZE];
-    int pingPacketLength = fillPingPacket(pingPacket);
+    QByteArray pingPacket = constructPingPacket();
 
     // send the ping packet to the local and public sockets for this node
-    _nodeSocket.writeDatagram((char*) pingPacket, pingPacketLength,
-                              node->getLocalSocket().getAddress(), node->getLocalSocket().getPort());
-    _nodeSocket.writeDatagram((char*) pingPacket, pingPacketLength,
-                              node->getPublicSocket().getAddress(), node->getPublicSocket().getPort());
+    _nodeSocket.writeDatagram(pingPacket, node->getLocalSocket().getAddress(), node->getLocalSocket().getPort());
+    _nodeSocket.writeDatagram(pingPacket, node->getPublicSocket().getAddress(), node->getPublicSocket().getPort());
 }
 
 SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType,
-                                const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) {
+                                            const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) {
     _nodeHashMutex.lock();
     
     SharedNodePointer matchingNode = _nodeHash.value(uuid);
@@ -646,9 +601,9 @@ SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType,
         
         QMutexLocker locker(&matchingNode->getMutex());
 
-        if (matchingNode->getType() == NODE_TYPE_AUDIO_MIXER ||
-            matchingNode->getType() == NODE_TYPE_VOXEL_SERVER ||
-            matchingNode->getType() == NODE_TYPE_METAVOXEL_SERVER) {
+        if (matchingNode->getType() == NodeType::AudioMixer ||
+            matchingNode->getType() == NodeType::VoxelServer ||
+            matchingNode->getType() == NodeType::MetavoxelServer) {
             // until the Audio class also uses our nodeList, we need to update
             // the lastRecvTimeUsecs for the audio mixer so it doesn't get killed and re-added continously
             matchingNode->setLastHeardMicrostamp(usecTimestampNow());
@@ -669,8 +624,7 @@ SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType,
     }
 }
 
-unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
-                                    const QSet& destinationNodeTypes) {
+unsigned NodeList::broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes) {
     unsigned n = 0;
 
     foreach (const SharedNodePointer& node, getNodeHash()) {
@@ -678,8 +632,7 @@ unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataByt
         if (destinationNodeTypes.contains(node->getType())) {
             if (getNodeActiveSocketOrPing(node.data())) {
                 // we know which socket is good for this node, send there
-                _nodeSocket.writeDatagram((char*) broadcastData, dataBytes,
-                                          node->getActiveSocket()->getAddress(), node->getActiveSocket()->getPort());
+                _nodeSocket.writeDatagram(packet, node->getActiveSocket()->getAddress(), node->getActiveSocket()->getPort());
                 ++n;
             }
         }
diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h
index c660517add..6b8acd6e99 100644
--- a/libraries/shared/src/NodeList.h
+++ b/libraries/shared/src/NodeList.h
@@ -29,21 +29,15 @@
 #include 
 
 #include "Node.h"
-#include "NodeTypes.h"
-
-const int MAX_NUM_NODES = 10000;
-const int NODES_PER_BUCKET = 100;
 
 const int MAX_PACKET_SIZE = 1500;
 
-const uint64_t NODE_SILENCE_THRESHOLD_USECS = 2 * 1000 * 1000;
-const uint64_t DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000;
-const uint64_t PING_INACTIVE_NODE_INTERVAL_USECS = 1 * 1000 * 1000;
+const quint64 NODE_SILENCE_THRESHOLD_USECS = 2 * 1000 * 1000;
+const quint64 DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000;
+const quint64 PING_INACTIVE_NODE_INTERVAL_USECS = 1 * 1000 * 1000;
 
 extern const char SOLO_NODE_TYPES[2];
 
-const int MAX_HOSTNAME_BYTES = 256;
-
 extern const QString DEFAULT_DOMAIN_HOSTNAME;
 extern const unsigned short DEFAULT_DOMAIN_SERVER_PORT;
 
@@ -54,6 +48,8 @@ const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5;
 class Assignment;
 class HifiSockAddr;
 
+typedef QSet NodeSet;
+
 typedef QSharedPointer SharedNodePointer;
 typedef QHash NodeHash;
 Q_DECLARE_METATYPE(SharedNodePointer)
@@ -63,8 +59,8 @@ class NodeList : public QObject {
 public:
     static NodeList* createInstance(char ownerType, unsigned short int socketListenPort = 0);
     static NodeList* getInstance();
-    NODE_TYPE getOwnerType() const { return _ownerType; }
-    void setOwnerType(NODE_TYPE ownerType) { _ownerType = ownerType; }
+    NodeType_t getOwnerType() const { return _ownerType; }
+    void setOwnerType(NodeType_t ownerType) { _ownerType = ownerType; }
 
     const QString& getDomainHostname() const { return _domainHostname; }
     void setDomainHostname(const QString& domainHostname);
@@ -90,19 +86,17 @@ public:
 
     void reset();
     
-    const QSet& getNodeInterestSet() const { return _nodeTypesOfInterest; }
-    void addNodeTypeToInterestSet(NODE_TYPE nodeTypeToAdd);
-    void addSetOfNodeTypesToNodeInterestSet(const QSet& setOfNodeTypes);
+    const NodeSet& getNodeInterestSet() const { return _nodeTypesOfInterest; }
+    void addNodeTypeToInterestSet(NodeType_t nodeTypeToAdd);
+    void addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes);
 
-    int processDomainServerList(unsigned char *packetData, size_t dataBytes);
+    int processDomainServerList(const QByteArray& packet);
 
     void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; }
     void sendAssignment(Assignment& assignment);
-    
-    int packOwnerUUID(unsigned char* packetData);
 
-    int fillPingPacket(unsigned char* buffer);
-    int fillPingReplyPacket(unsigned char* pingBuffer, unsigned char* replyBuffer);
+    QByteArray constructPingPacket();
+    QByteArray constructPingReplyPacket(const QByteArray& pingPacket);
     void pingPublicAndLocalSocketsForInactiveNode(Node* node);
 
     SharedNodePointer nodeWithAddress(const HifiSockAddr& senderSockAddr);
@@ -110,13 +104,12 @@ public:
 
     SharedNodePointer addOrUpdateNode(const QUuid& uuid, char nodeType, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket);
 
-    void processNodeData(const HifiSockAddr& senderSockAddr, unsigned char *packetData, size_t dataBytes);
-    
+    void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet);
     void processKillNode(const QByteArray& datagram);
 
-    int updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, unsigned char *packetData, int dataBytes);
+    int updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, const QByteArray& packet);
 
-    unsigned broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const QSet& destinationNodeTypes);
+    unsigned broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes);
     SharedNodePointer soloNodeOfType(char nodeType);
 
     void loadData(QSettings* settings);
@@ -141,7 +134,7 @@ private:
     NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton
     void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton
     void sendSTUNRequest();
-    void processSTUNResponse(unsigned char* packetData, size_t dataBytes);
+    void processSTUNResponse(const QByteArray& packet);
 
     NodeHash::iterator killNodeAtHashIterator(NodeHash::iterator& nodeItemToKill);
 
@@ -150,8 +143,8 @@ private:
     QString _domainHostname;
     HifiSockAddr _domainSockAddr;
     QUdpSocket _nodeSocket;
-    char _ownerType;
-    QSet _nodeTypesOfInterest;
+    NodeType_t _ownerType;
+    NodeSet _nodeTypesOfInterest;
     QUuid _ownerUUID;
     int _numNoReplyDomainCheckIns;
     HifiSockAddr _assignmentServerSocket;
@@ -160,7 +153,7 @@ private:
     unsigned int _stunRequestsSinceSuccess;
 
     void activateSocketFromNodeCommunication(const HifiSockAddr& nodeSockAddr);
-    void timePingReply(const HifiSockAddr& nodeAddress, unsigned char *packetData);
+    void timePingReply(const QByteArray& packet);
     void resetDomainData(char domainField[], const char* domainData);
     void domainLookup();
     void clear();
diff --git a/libraries/shared/src/NodeTypes.h b/libraries/shared/src/NodeTypes.h
deleted file mode 100644
index 6e8523c7d7..0000000000
--- a/libraries/shared/src/NodeTypes.h
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-//  NodeTypes.h
-//  hifi
-//
-//  Created by Brad Hefta-Gaub on 2013/04/09
-//  Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
-//
-//  Single byte/character Node Types used to identify various nodes in the system.
-//  For example, an node whose is 'V' is always a voxel server.
-
-#ifndef hifi_NodeTypes_h
-#define hifi_NodeTypes_h
-
-// NOTE: If you add a new NODE_TYPE_XXX then you also should add a new NODE_TYPE_NAME_XXX and a new "case" to the
-//       switch statement in Node.cpp specifically Node::getTypeName().
-//       If you don't then it will make things harder on your co-developers in debugging because the Node
-//       class won't know the name and will report it as "Unknown".
-
-typedef char NODE_TYPE;
-const NODE_TYPE NODE_TYPE_DOMAIN = 'D';
-const NODE_TYPE NODE_TYPE_VOXEL_SERVER = 'V';
-const NODE_TYPE NODE_TYPE_PARTICLE_SERVER = 'P';
-const NODE_TYPE NODE_TYPE_METAVOXEL_SERVER = 'm';
-const NODE_TYPE NODE_TYPE_ENVIRONMENT_SERVER = 'E';
-const NODE_TYPE NODE_TYPE_AGENT = 'I';
-const NODE_TYPE NODE_TYPE_AUDIO_MIXER = 'M';
-const NODE_TYPE NODE_TYPE_AVATAR_MIXER = 'W';
-const NODE_TYPE NODE_TYPE_ANIMATION_SERVER = 'a';
-const NODE_TYPE NODE_TYPE_UNASSIGNED = 1;
-
-#endif
diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp
index 24a34f618e..3fd51949f9 100644
--- a/libraries/shared/src/PacketHeaders.cpp
+++ b/libraries/shared/src/PacketHeaders.cpp
@@ -6,105 +6,120 @@
 //  Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
 //
 
-#include 
+#include 
 
 #include 
 
+#include "NodeList.h"
+
 #include "PacketHeaders.h"
 
-PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
+int arithmeticCodingValueFromBuffer(const char* checkValue) {
+    if (((uchar) *checkValue) < 255) {
+        return *checkValue;
+    } else {
+        return 255 + arithmeticCodingValueFromBuffer(checkValue + 1);
+    }
+}
+
+int numBytesArithmeticCodingFromBuffer(const char* checkValue) {
+    if (((uchar) *checkValue) < 255) {
+        return 1;
+    } else {
+        return 1 + numBytesArithmeticCodingFromBuffer(checkValue + 1);
+    }
+}
+
+int packArithmeticallyCodedValue(int value, char* destination) {
+    if (value < 255) {
+        // less than 255, just pack our value
+        destination[0] = (uchar) value;
+        return 1;
+    } else {
+        // pack 255 and then recursively pack on
+        destination[0] = 255;
+        return 1 + packArithmeticallyCodedValue(value - 255, destination + 1);
+    }
+}
+
+PacketVersion versionForPacketType(PacketType type) {
     switch (type) {
-
-        case PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO:
-        case PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO:
-            return 2;
-
-        case PACKET_TYPE_HEAD_DATA:
-            return 17;
-
-        case PACKET_TYPE_OCTREE_STATS:
-            return 2;
-
-        case PACKET_TYPE_DOMAIN:
-        case PACKET_TYPE_DOMAIN_LIST_REQUEST:
-        case PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY:
-            return 2;
-
-        case PACKET_TYPE_VOXEL_QUERY:
-            return 2;
-
-        case PACKET_TYPE_VOXEL_SET:
-        case PACKET_TYPE_VOXEL_SET_DESTRUCTIVE:
-        case PACKET_TYPE_VOXEL_ERASE:
-            return 1;
-
-        case PACKET_TYPE_VOXEL_DATA:
-            return 1;
-
-        case PACKET_TYPE_JURISDICTION:
-            return 1;
-
-        case PACKET_TYPE_PARTICLE_ADD_OR_EDIT:
-            return 4;
-
-        case PACKET_TYPE_PARTICLE_DATA:
-            return 8;
-
-        case PACKET_TYPE_PING_REPLY:
-            return 1;
-
-        case PACKET_TYPE_DATA_SERVER_GET:
-        case PACKET_TYPE_DATA_SERVER_PUT:
-        case PACKET_TYPE_DATA_SERVER_SEND:
-        case PACKET_TYPE_DATA_SERVER_CONFIRM:
-            return 1;
-            
         default:
             return 0;
     }
 }
 
-bool packetVersionMatch(unsigned char* packetHeader, const HifiSockAddr& senderSockAddr) {
+QByteArray byteArrayWithPopluatedHeader(PacketType type, const QUuid& connectionUUID) {
+    QByteArray freshByteArray(MAX_PACKET_HEADER_BYTES, 0);
+    freshByteArray.resize(populatePacketHeader(freshByteArray, type, connectionUUID));
+    return freshByteArray;
+}
+
+int populatePacketHeader(QByteArray& packet, PacketType type, const QUuid& connectionUUID) {
+    if (packet.size() < numBytesForPacketHeaderGivenPacketType(type)) {
+        packet.resize(numBytesForPacketHeaderGivenPacketType(type));
+    }
+    
+    return populatePacketHeader(packet.data(), type, connectionUUID);
+}
+
+int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionUUID) {
+    int numTypeBytes = packArithmeticallyCodedValue(type, packet);
+    packet[numTypeBytes] = versionForPacketType(type);
+    
+    QUuid packUUID = connectionUUID.isNull() ? NodeList::getInstance()->getOwnerUUID() : connectionUUID;
+    
+    QByteArray rfcUUID = packUUID.toRfc4122();
+    memcpy(packet + numTypeBytes + sizeof(PacketVersion), rfcUUID.constData(), NUM_BYTES_RFC4122_UUID);
+    
+    // return the number of bytes written for pointer pushing
+    return numTypeBytes + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;
+}
+
+bool packetVersionMatch(const QByteArray& packet) {
     // currently this just checks if the version in the packet matches our return from versionForPacketType
     // may need to be expanded in the future for types and versions that take > than 1 byte
-    if (packetHeader[1] == versionForPacketType(packetHeader[0]) || packetHeader[0] == PACKET_TYPE_STUN_RESPONSE) {
+    
+    if (packet[1] == versionForPacketType(packetTypeForPacket(packet)) || packetTypeForPacket(packet) == PacketTypeStunResponse) {
         return true;
     } else {
-        qDebug() << "Packet mismatch on" << packetHeader[0] << "- Sender"
-        << senderSockAddr << "sent" << qPrintable(QString::number(packetHeader[1])) << "but"
-        << qPrintable(QString::number(versionForPacketType(packetHeader[0]))) << "expected.";
+        PacketType mismatchType = packetTypeForPacket(packet);
+        int numPacketTypeBytes = arithmeticCodingValueFromBuffer(packet.data());
+       
+        QUuid nodeUUID;
+        deconstructPacketHeader(packet, nodeUUID);
+        
+        qDebug() << "Packet mismatch on" << packetTypeForPacket(packet) << "- Sender"
+            << nodeUUID << "sent" << qPrintable(QString::number(packet[numPacketTypeBytes])) << "but"
+            << qPrintable(QString::number(versionForPacketType(mismatchType))) << "expected.";
+
         return false;
     }
 }
 
-int populateTypeAndVersion(unsigned char* destinationHeader, PACKET_TYPE type) {
-    destinationHeader[0] = type;
-    destinationHeader[1] = versionForPacketType(type);
-
-    // return the number of bytes written for pointer pushing
-    return 2;
+int numBytesForPacketHeader(const QByteArray& packet) {
+    // returns the number of bytes used for the type, version, and UUID
+    return numBytesArithmeticCodingFromBuffer(packet.data()) + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;
 }
 
-int numBytesForPacketType(const unsigned char* packetType) {
-    if (packetType[0] == 255) {
-        return 1 + numBytesForPacketType(packetType + 1);
-    } else {
-        return 1;
-    }
+int numBytesForPacketHeader(const char* packet) {
+    // returns the number of bytes used for the type, version, and UUID
+    return numBytesArithmeticCodingFromBuffer(packet) + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;
 }
 
-int numBytesForPacketVersion(const unsigned char* packetVersion) {
-    if (packetVersion[0] == 255) {
-        return 1 + numBytesForPacketVersion(packetVersion + 1);
-    } else {
-        return 1;
-    }
+int numBytesForPacketHeaderGivenPacketType(PacketType type) {
+    return (int) ceilf((float)type / 255) + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;
 }
 
-int numBytesForPacketHeader(const unsigned char* packetHeader) {
-    // int numBytesType = numBytesForPacketType(packetHeader);
-    // return numBytesType + numBytesForPacketVersion(packetHeader + numBytesType);
-
-    // currently this need not be dynamic - there are 2 bytes for each packet header
-    return 2;
+void deconstructPacketHeader(const QByteArray& packet, QUuid& senderUUID) {
+    senderUUID = QUuid::fromRfc4122(packet.mid(numBytesArithmeticCodingFromBuffer(packet.data()) + sizeof(PacketVersion),
+                                               NUM_BYTES_RFC4122_UUID));
+}
+
+PacketType packetTypeForPacket(const QByteArray& packet) {
+    return (PacketType) arithmeticCodingValueFromBuffer(packet.data());
+}
+
+PacketType packetTypeForPacket(const char* packet) {
+    return (PacketType) arithmeticCodingValueFromBuffer(packet);
 }
diff --git a/libraries/shared/src/PacketHeaders.h b/libraries/shared/src/PacketHeaders.h
index 1febc9367d..543dce0504 100644
--- a/libraries/shared/src/PacketHeaders.h
+++ b/libraries/shared/src/PacketHeaders.h
@@ -12,61 +12,71 @@
 #ifndef hifi_PacketHeaders_h
 #define hifi_PacketHeaders_h
 
-#include "HifiSockAddr.h"
+#include 
 
-typedef char PACKET_TYPE;
-const PACKET_TYPE PACKET_TYPE_UNKNOWN = 0;
-const PACKET_TYPE PACKET_TYPE_STUN_RESPONSE = 1;
-const PACKET_TYPE PACKET_TYPE_DOMAIN = 'D';
-const PACKET_TYPE PACKET_TYPE_PING = 'P';
-const PACKET_TYPE PACKET_TYPE_PING_REPLY = 'R';
-const PACKET_TYPE PACKET_TYPE_KILL_AVATAR = 'K';
-const PACKET_TYPE PACKET_TYPE_HEAD_DATA = 'H';
-const PACKET_TYPE PACKET_TYPE_INJECT_AUDIO = 'I';
-const PACKET_TYPE PACKET_TYPE_MIXED_AUDIO = 'A';
-const PACKET_TYPE PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO = 'M';
-const PACKET_TYPE PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO = 'm';
-const PACKET_TYPE PACKET_TYPE_BULK_AVATAR_DATA = 'X';
-const PACKET_TYPE PACKET_TYPE_TRANSMITTER_DATA_V2 = 'T';
-const PACKET_TYPE PACKET_TYPE_ENVIRONMENT_DATA = 'e';
-const PACKET_TYPE PACKET_TYPE_DOMAIN_LIST_REQUEST = 'L';
-const PACKET_TYPE PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY = 'C';
-const PACKET_TYPE PACKET_TYPE_REQUEST_ASSIGNMENT = 'r';
-const PACKET_TYPE PACKET_TYPE_CREATE_ASSIGNMENT = 's';
-const PACKET_TYPE PACKET_TYPE_DEPLOY_ASSIGNMENT = 'd';
-const PACKET_TYPE PACKET_TYPE_DATA_SERVER_PUT = 'p';
-const PACKET_TYPE PACKET_TYPE_DATA_SERVER_GET = 'g';
-const PACKET_TYPE PACKET_TYPE_DATA_SERVER_SEND = 'u';
-const PACKET_TYPE PACKET_TYPE_DATA_SERVER_CONFIRM = 'c';
-const PACKET_TYPE PACKET_TYPE_VOXEL_QUERY = 'q';
-const PACKET_TYPE PACKET_TYPE_VOXEL_DATA = 'V';
-const PACKET_TYPE PACKET_TYPE_VOXEL_SET = 'S';
-const PACKET_TYPE PACKET_TYPE_VOXEL_SET_DESTRUCTIVE = 'O';
-const PACKET_TYPE PACKET_TYPE_VOXEL_ERASE = 'E';
-const PACKET_TYPE PACKET_TYPE_OCTREE_STATS = '#';
-const PACKET_TYPE PACKET_TYPE_JURISDICTION = 'J';
-const PACKET_TYPE PACKET_TYPE_JURISDICTION_REQUEST = 'j';
-const PACKET_TYPE PACKET_TYPE_PARTICLE_QUERY = 'Q';
-const PACKET_TYPE PACKET_TYPE_PARTICLE_DATA = 'v';
-const PACKET_TYPE PACKET_TYPE_PARTICLE_ADD_OR_EDIT = 'a';
-const PACKET_TYPE PACKET_TYPE_PARTICLE_ERASE = 'x';
-const PACKET_TYPE PACKET_TYPE_PARTICLE_ADD_RESPONSE = 'b';
-const PACKET_TYPE PACKET_TYPE_METAVOXEL_DATA = 't';
+#include "UUID.h"
 
-typedef char PACKET_VERSION;
+enum PacketType {
+    PacketTypeUnknown,
+    PacketTypeStunResponse,
+    PacketTypeDomainList,
+    PacketTypePing,
+    PacketTypePingReply,
+    PacketTypeKillAvatar,
+    PacketTypeAvatarData,
+    PacketTypeInjectAudio,
+    PacketTypeMixedAudio,
+    PacketTypeMicrophoneAudioNoEcho,
+    PacketTypeMicrophoneAudioWithEcho,
+    PacketTypeBulkAvatarData,
+    PacketTypeTransmitterData,
+    PacketTypeEnvironmentData,
+    PacketTypeDomainListRequest,
+    PacketTypeRequestAssignment,
+    PacketTypeCreateAssignment,
+    PacketTypeDataServerPut,
+    PacketTypeDataServerGet,
+    PacketTypeDataServerSend,
+    PacketTypeDataServerConfirm,
+    PacketTypeVoxelQuery,
+    PacketTypeVoxelData,
+    PacketTypeVoxelSet,
+    PacketTypeVoxelSetDestructive,
+    PacketTypeVoxelErase,
+    PacketTypeOctreeStats,
+    PacketTypeJurisdiction,
+    PacketTypeJurisdictionRequest,
+    PacketTypeParticleQuery,
+    PacketTypeParticleData,
+    PacketTypeParticleAddOrEdit,
+    PacketTypeParticleErase,
+    PacketTypeParticleAddResponse,
+    PacketTypeMetavoxelData
+};
 
-PACKET_VERSION versionForPacketType(PACKET_TYPE type);
+typedef char PacketVersion;
 
-bool packetVersionMatch(unsigned char* packetHeader, const HifiSockAddr& senderSockAddr);
+const int MAX_PACKET_HEADER_BYTES = sizeof(PacketType) + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;;
 
-int populateTypeAndVersion(unsigned char* destinationHeader, PACKET_TYPE type);
-int numBytesForPacketHeader(const unsigned char* packetHeader);
+PacketVersion versionForPacketType(PacketType type);
 
-const int MAX_PACKET_HEADER_BYTES = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION);
+const QUuid nullUUID = QUuid();
 
-// These are supported Z-Command
-#define ERASE_ALL_COMMAND "erase all"
-#define ADD_SCENE_COMMAND "add scene"
-#define TEST_COMMAND      "a message"
+QByteArray byteArrayWithPopluatedHeader(PacketType type, const QUuid& connectionUUID = nullUUID);
+int populatePacketHeader(QByteArray& packet, PacketType type, const QUuid& connectionUUID = nullUUID);
+int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionUUID = nullUUID);
+
+bool packetVersionMatch(const QByteArray& packet);
+
+int numBytesForPacketHeader(const QByteArray& packet);
+int numBytesForPacketHeader(const char* packet);
+int numBytesForPacketHeaderGivenPacketType(PacketType type);
+
+void deconstructPacketHeader(const QByteArray& packet, QUuid& senderUUID);
+
+PacketType packetTypeForPacket(const QByteArray& packet);
+PacketType packetTypeForPacket(const char* packet);
+
+int arithmeticCodingValueFromBuffer(const char* checkValue);
 
 #endif
diff --git a/libraries/shared/src/PacketSender.cpp b/libraries/shared/src/PacketSender.cpp
index 52274d9e9c..8a031cbc39 100644
--- a/libraries/shared/src/PacketSender.cpp
+++ b/libraries/shared/src/PacketSender.cpp
@@ -16,8 +16,8 @@
 #include "PacketSender.h"
 #include "SharedUtil.h"
 
-const uint64_t PacketSender::USECS_PER_SECOND = 1000 * 1000;
-const uint64_t PacketSender::SENDING_INTERVAL_ADJUST = 200; // approaximate 200us
+const quint64 PacketSender::USECS_PER_SECOND = 1000 * 1000;
+const quint64 PacketSender::SENDING_INTERVAL_ADJUST = 200; // approaximate 200us
 const int PacketSender::TARGET_FPS = 60;
 const int PacketSender::MAX_SLEEP_INTERVAL = PacketSender::USECS_PER_SECOND;
 
@@ -47,13 +47,13 @@ PacketSender::~PacketSender() {
 }
 
 
-void PacketSender::queuePacketForSending(const HifiSockAddr& address, unsigned char* packetData, ssize_t packetLength) {
-    NetworkPacket packet(address, packetData, packetLength);
+void PacketSender::queuePacketForSending(const HifiSockAddr& address, const QByteArray& packet) {
+    NetworkPacket networkPacket(address, packet);
     lock();
-    _packets.push_back(packet);
+    _packets.push_back(networkPacket);
     unlock();
     _totalPacketsQueued++;
-    _totalBytesQueued += packetLength;
+    _totalBytesQueued += packet.size();
 }
 
 void PacketSender::setPacketsPerSecond(int packetsPerSecond) {
@@ -82,13 +82,13 @@ bool PacketSender::threadedProcess() {
         int packetsPerSecondTarget = (_packetsPerSecond > MINIMUM_PACKETS_PER_SECOND)
                                             ? _packetsPerSecond : MINIMUM_PACKETS_PER_SECOND;
 
-        uint64_t intervalBetweenSends = USECS_PER_SECOND / packetsPerSecondTarget;
-        uint64_t sleepInterval = (intervalBetweenSends > SENDING_INTERVAL_ADJUST) ?
+        quint64 intervalBetweenSends = USECS_PER_SECOND / packetsPerSecondTarget;
+        quint64 sleepInterval = (intervalBetweenSends > SENDING_INTERVAL_ADJUST) ?
                     intervalBetweenSends - SENDING_INTERVAL_ADJUST : intervalBetweenSends;
 
         // We'll sleep before we send, this way, we can set our last send time to be our ACTUAL last send time
-        uint64_t now = usecTimestampNow();
-        uint64_t elapsed = now - _lastSendTime;
+        quint64 now = usecTimestampNow();
+        quint64 elapsed = now - _lastSendTime;
         int usecToSleep =  sleepInterval - elapsed;
 
         // If we've never sent, or it's been a long time since we sent, then our elapsed time will be quite large
@@ -132,18 +132,18 @@ bool PacketSender::threadedProcess() {
 // We also keep a running total of packets sent over multiple calls to process() so that we can adjust up or down for
 // possible rounding error that would occur if we only considered whole integer packet counts per call to process
 bool PacketSender::nonThreadedProcess() {
-    uint64_t now = usecTimestampNow();
+    quint64 now = usecTimestampNow();
 
     if (_lastProcessCallTime == 0) {
         _lastProcessCallTime = now - _usecsPerProcessCallHint;
     }
 
-    const uint64_t MINIMUM_POSSIBLE_CALL_TIME = 10; // in usecs
-    const uint64_t USECS_PER_SECOND = 1000 * 1000;
+    const quint64 MINIMUM_POSSIBLE_CALL_TIME = 10; // in usecs
+    const quint64 USECS_PER_SECOND = 1000 * 1000;
     const float ZERO_RESET_CALLS_PER_SECOND = 1; // used in guard against divide by zero
 
     // keep track of our process call times, so we have a reliable account of how often our caller calls us
-    uint64_t elapsedSinceLastCall = now - _lastProcessCallTime;
+    quint64 elapsedSinceLastCall = now - _lastProcessCallTime;
     _lastProcessCallTime = now;
     _averageProcessCallTime.updateAverage(elapsedSinceLastCall);
 
@@ -165,7 +165,7 @@ bool PacketSender::nonThreadedProcess() {
     if (_lastPPSCheck == 0) {
         _lastPPSCheck = now;
         // pretend like our lifetime began once call cycle for now, this makes our lifetime PPS start out most accurately
-        _started = now - (uint64_t)averageCallTime;
+        _started = now - (quint64)averageCallTime;
     }
 
 
@@ -215,7 +215,7 @@ bool PacketSender::nonThreadedProcess() {
 
     // So no mater whether or not we're getting called more or less than once per second, we still need to do some bookkeeping
     // to make sure we send a few extra packets to even out our flow rate.
-    uint64_t elapsedSinceLastCheck = now - _lastPPSCheck;
+    quint64 elapsedSinceLastCheck = now - _lastPPSCheck;
 
     // we might want to tun this in the future and only check after a certain number of call intervals. for now we check
     // each time and adjust accordingly
@@ -263,15 +263,15 @@ bool PacketSender::nonThreadedProcess() {
         unlock();
 
         // send the packet through the NodeList...
-        NodeList::getInstance()->getNodeSocket().writeDatagram((char*) temporary.getData(), temporary.getLength(),
+        NodeList::getInstance()->getNodeSocket().writeDatagram(temporary.getByteArray(),
                                                                temporary.getSockAddr().getAddress(),
                                                                temporary.getSockAddr().getPort());
         packetsSentThisCall++;
         _packetsOverCheckInterval++;
         _totalPacketsSent++;
-        _totalBytesSent += temporary.getLength();
+        _totalBytesSent += temporary.getByteArray().size();
         
-        emit packetSent(temporary.getLength());
+        emit packetSent(temporary.getByteArray().size());
         
         _lastSendTime = now;
     }
diff --git a/libraries/shared/src/PacketSender.h b/libraries/shared/src/PacketSender.h
index 4d87a62e5a..4a1e62bd0c 100644
--- a/libraries/shared/src/PacketSender.h
+++ b/libraries/shared/src/PacketSender.h
@@ -20,8 +20,8 @@ class PacketSender : public GenericThread {
     Q_OBJECT
 public:
 
-    static const uint64_t USECS_PER_SECOND;
-    static const uint64_t SENDING_INTERVAL_ADJUST;
+    static const quint64 USECS_PER_SECOND;
+    static const quint64 SENDING_INTERVAL_ADJUST;
     static const int TARGET_FPS;
     static const int MAX_SLEEP_INTERVAL;
 
@@ -37,7 +37,7 @@ public:
     /// \param packetData pointer to data
     /// \param ssize_t packetLength size of data
     /// \thread any thread, typically the application thread
-    void queuePacketForSending(const HifiSockAddr& address, unsigned char*  packetData, ssize_t packetLength);
+    void queuePacketForSending(const HifiSockAddr& address, const QByteArray& packet);
 
     void setPacketsPerSecond(int packetsPerSecond);
     int getPacketsPerSecond() const { return _packetsPerSecond; }
@@ -72,46 +72,46 @@ public:
         { return getLifetimeInSeconds() == 0 ? 0 : (float)((float)_totalBytesQueued / getLifetimeInSeconds()); }
 
     /// returns lifetime of this object from first packet sent to now in usecs
-    uint64_t getLifetimeInUsecs() const { return (usecTimestampNow() - _started); }
+    quint64 getLifetimeInUsecs() const { return (usecTimestampNow() - _started); }
 
     /// returns lifetime of this object from first packet sent to now in usecs
     float getLifetimeInSeconds() const { return ((float)getLifetimeInUsecs() / (float)USECS_PER_SECOND); }
 
     /// returns the total packets sent by this object over its lifetime
-    uint64_t getLifetimePacketsSent() const { return _totalPacketsSent; }
+    quint64 getLifetimePacketsSent() const { return _totalPacketsSent; }
 
     /// returns the total bytes sent by this object over its lifetime
-    uint64_t getLifetimeBytesSent() const { return _totalBytesSent; }
+    quint64 getLifetimeBytesSent() const { return _totalBytesSent; }
 
     /// returns the total packets queued by this object over its lifetime
-    uint64_t getLifetimePacketsQueued() const { return _totalPacketsQueued; }
+    quint64 getLifetimePacketsQueued() const { return _totalPacketsQueued; }
 
     /// returns the total bytes queued by this object over its lifetime
-    uint64_t getLifetimeBytesQueued() const { return _totalBytesQueued; }
+    quint64 getLifetimeBytesQueued() const { return _totalBytesQueued; }
 signals:
     void packetSent(quint64);
 protected:
     int _packetsPerSecond;
     int _usecsPerProcessCallHint;
-    uint64_t _lastProcessCallTime;
+    quint64 _lastProcessCallTime;
     SimpleMovingAverage _averageProcessCallTime;
 
 private:
     std::vector _packets;
-    uint64_t _lastSendTime;
+    quint64 _lastSendTime;
 
     bool threadedProcess();
     bool nonThreadedProcess();
 
-    uint64_t _lastPPSCheck;
+    quint64 _lastPPSCheck;
     int _packetsOverCheckInterval;
 
-    uint64_t _started;
-    uint64_t _totalPacketsSent;
-    uint64_t _totalBytesSent;
+    quint64 _started;
+    quint64 _totalPacketsSent;
+    quint64 _totalBytesSent;
 
-    uint64_t _totalPacketsQueued;
-    uint64_t _totalBytesQueued;
+    quint64 _totalPacketsQueued;
+    quint64 _totalBytesQueued;
 };
 
 #endif // __shared__PacketSender__
diff --git a/libraries/shared/src/PerfStat.cpp b/libraries/shared/src/PerfStat.cpp
index b99b323751..7bea51dd6e 100644
--- a/libraries/shared/src/PerfStat.cpp
+++ b/libraries/shared/src/PerfStat.cpp
@@ -23,8 +23,8 @@ bool PerformanceWarning::_suppressShortTimings = false;
 
 // Destructor handles recording all of our stats
 PerformanceWarning::~PerformanceWarning() {
-    uint64_t end = usecTimestampNow();
-    uint64_t elapsedusec = (end - _start);
+    quint64 end = usecTimestampNow();
+    quint64 elapsedusec = (end - _start);
     double elapsedmsec = elapsedusec / 1000.0;
     if ((_alwaysDisplay || _renderWarningsOn) && elapsedmsec > 1) {
         if (elapsedmsec > 1000) {
diff --git a/libraries/shared/src/PerfStat.h b/libraries/shared/src/PerfStat.h
index 2e56526495..fffb095021 100644
--- a/libraries/shared/src/PerfStat.h
+++ b/libraries/shared/src/PerfStat.h
@@ -28,17 +28,17 @@
 
 class PerformanceWarning {
 private:
-	uint64_t _start;
+	quint64 _start;
 	const char* _message;
 	bool _renderWarningsOn;
 	bool _alwaysDisplay;
-	uint64_t* _runningTotal;
-	uint64_t* _totalCalls;
+	quint64* _runningTotal;
+	quint64* _totalCalls;
 	static bool _suppressShortTimings;
 public:
 
     PerformanceWarning(bool renderWarnings, const char* message, bool alwaysDisplay = false,
-                        uint64_t* runningTotal = NULL, uint64_t* totalCalls = NULL) :
+                        quint64* runningTotal = NULL, quint64* totalCalls = NULL) :
         _start(usecTimestampNow()),
         _message(message),
         _renderWarningsOn(renderWarnings),
diff --git a/libraries/shared/src/Radix2IntegerScanner.h b/libraries/shared/src/Radix2IntegerScanner.h
index 355ecfef46..84e7c7077e 100644
--- a/libraries/shared/src/Radix2IntegerScanner.h
+++ b/libraries/shared/src/Radix2IntegerScanner.h
@@ -25,7 +25,7 @@ namespace type_traits { // those are needed for the declaration, see below
     template< typename T > struct make_unsigned< T, 1 > { typedef uint8_t type; };
     template< typename T > struct make_unsigned< T, 2 > { typedef uint16_t type; };
     template< typename T > struct make_unsigned< T, 4 > { typedef uint32_t type; };
-    template< typename T > struct make_unsigned< T, 8 > { typedef uint64_t type; };
+    template< typename T > struct make_unsigned< T, 8 > { typedef quint64 type; };
 }
 
 
diff --git a/libraries/shared/src/ReceivedPacketProcessor.cpp b/libraries/shared/src/ReceivedPacketProcessor.cpp
index d61db2b184..5faec02ea1 100644
--- a/libraries/shared/src/ReceivedPacketProcessor.cpp
+++ b/libraries/shared/src/ReceivedPacketProcessor.cpp
@@ -16,16 +16,16 @@ ReceivedPacketProcessor::ReceivedPacketProcessor() {
     _dontSleep = false;
 }
 
-void ReceivedPacketProcessor::queueReceivedPacket(const HifiSockAddr& address, unsigned char* packetData, ssize_t packetLength) {
+void ReceivedPacketProcessor::queueReceivedPacket(const HifiSockAddr& address, const QByteArray& packet) {
     // Make sure our Node and NodeList knows we've heard from this node.
     SharedNodePointer node = NodeList::getInstance()->nodeWithAddress(address);
     if (node) {
         node->setLastHeardMicrostamp(usecTimestampNow());
     }
 
-    NetworkPacket packet(address, packetData, packetLength);
+    NetworkPacket networkPacket(address, packet);
     lock();
-    _packets.push_back(packet);
+    _packets.push_back(networkPacket);
     unlock();
 }
 
@@ -34,7 +34,7 @@ bool ReceivedPacketProcessor::process() {
     // If a derived class handles process sleeping, like the JurisdiciontListener, then it can set
     // this _dontSleep member and we will honor that request.
     if (_packets.size() == 0 && !_dontSleep) {
-        const uint64_t RECEIVED_THREAD_SLEEP_INTERVAL = (1000 * 1000)/60; // check at 60fps
+        const quint64 RECEIVED_THREAD_SLEEP_INTERVAL = (1000 * 1000)/60; // check at 60fps
         usleep(RECEIVED_THREAD_SLEEP_INTERVAL);
     }
     while (_packets.size() > 0) {
@@ -44,7 +44,7 @@ bool ReceivedPacketProcessor::process() {
         NetworkPacket temporary = packet; // make a copy of the packet in case the vector is resized on us
         _packets.erase(_packets.begin()); // remove the oldest packet
         unlock(); // let others add to the packets
-        processPacket(temporary.getSockAddr(), temporary.getData(), temporary.getLength()); // process our temporary copy
+        processPacket(temporary.getSockAddr(), temporary.getByteArray()); // process our temporary copy
     }
     return isStillRunning();  // keep running till they terminate us
 }
diff --git a/libraries/shared/src/ReceivedPacketProcessor.h b/libraries/shared/src/ReceivedPacketProcessor.h
index 567434a49e..dfb4ed55a9 100644
--- a/libraries/shared/src/ReceivedPacketProcessor.h
+++ b/libraries/shared/src/ReceivedPacketProcessor.h
@@ -24,7 +24,7 @@ public:
     /// \param packetData pointer to received data
     /// \param ssize_t packetLength size of received data
     /// \thread network receive thread
-    void queueReceivedPacket(const HifiSockAddr& senderSockAddr, unsigned char*  packetData, ssize_t packetLength);
+    void queueReceivedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& packet);
 
     /// Are there received packets waiting to be processed
     bool hasPacketsToProcess() const { return _packets.size() > 0; }
@@ -38,7 +38,7 @@ protected:
     /// \param packetData pointer to received data
     /// \param ssize_t packetLength size of received data
     /// \thread "this" individual processing thread
-    virtual void processPacket(const HifiSockAddr& senderAddress, unsigned char*  packetData, ssize_t packetLength) = 0;
+    virtual void processPacket(const HifiSockAddr& senderAddress, const QByteArray& packet) = 0;
 
     /// Implements generic processing behavior for this thread.
     virtual bool process();
diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp
index 9d2eec6b40..4b0f2c403e 100644
--- a/libraries/shared/src/RegisteredMetaTypes.cpp
+++ b/libraries/shared/src/RegisteredMetaTypes.cpp
@@ -13,6 +13,7 @@
 void registerMetaTypes(QScriptEngine* engine) {
     qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue);
     qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue);
+    qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue);
     qScriptRegisterMetaType(engine, xColorToScriptValue, xColorFromScriptValue);
 }
 
@@ -42,6 +43,21 @@ void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2) {
     vec2.y = object.property("y").toVariant().toFloat();
 }
 
+QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat) {
+    QScriptValue obj = engine->newObject();
+    obj.setProperty("x", quat.x);
+    obj.setProperty("y", quat.y);
+    obj.setProperty("z", quat.z);
+    obj.setProperty("w", quat.w);
+    return obj;
+}
+
+void quatFromScriptValue(const QScriptValue &object, glm::quat& quat) {
+    quat.x = object.property("x").toVariant().toFloat();
+    quat.y = object.property("y").toVariant().toFloat();
+    quat.z = object.property("z").toVariant().toFloat();
+    quat.w = object.property("w").toVariant().toFloat();
+}
 
 QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) {
     QScriptValue obj = engine->newObject();
diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h
index 4d91a21be7..b198add7c2 100644
--- a/libraries/shared/src/RegisteredMetaTypes.h
+++ b/libraries/shared/src/RegisteredMetaTypes.h
@@ -19,8 +19,10 @@
 
 Q_DECLARE_METATYPE(glm::vec3)
 Q_DECLARE_METATYPE(glm::vec2)
+Q_DECLARE_METATYPE(glm::quat)
 Q_DECLARE_METATYPE(xColor)
 
+
 void registerMetaTypes(QScriptEngine* engine);
 
 QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3);
@@ -29,6 +31,9 @@ void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3);
 QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2);
 void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2);
 
+QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat);
+void quatFromScriptValue(const QScriptValue &object, glm::quat& quat);
+
 QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color);
 void xColorFromScriptValue(const QScriptValue &object, xColor& color);
 
diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp
index 9433fe1236..3b869e0ece 100644
--- a/libraries/shared/src/SharedUtil.cpp
+++ b/libraries/shared/src/SharedUtil.cpp
@@ -26,7 +26,7 @@
 #include "PacketHeaders.h"
 #include "SharedUtil.h"
 
-uint64_t usecTimestamp(const timeval *time) {
+quint64 usecTimestamp(const timeval *time) {
     return (time->tv_sec * 1000000 + time->tv_usec);
 }
 
@@ -35,7 +35,7 @@ void usecTimestampNowForceClockSkew(int clockSkew) {
     ::usecTimestampNowAdjust = clockSkew;
 }
 
-uint64_t usecTimestampNow() {
+quint64 usecTimestampNow() {
     timeval now;
     gettimeofday(&now, NULL);
     return (now.tv_sec * 1000000 + now.tv_usec) + ::usecTimestampNowAdjust;
@@ -231,106 +231,6 @@ void sharedMessageHandler(QtMsgType type, const QMessageLogContext& context, con
     fprintf(stdout, "%s", message.toLocal8Bit().constData());
 }
 
-//////////////////////////////////////////////////////////////////////////////////////////
-// Function:    createVoxelEditMessage()
-// Description: creates an "insert" or "remove" voxel message for a voxel code
-//              corresponding to the closest voxel which encloses a cube with
-//              lower corners at x,y,z, having side of length S.
-//              The input values x,y,z range 0.0 <= v < 1.0
-//              message should be either 'S' for SET or 'E' for ERASE
-//
-// IMPORTANT:   The buffer is returned to you a buffer which you MUST delete when you are
-//              done with it.
-//
-// HACK ATTACK: Well, what if this is larger than the MTU? That's the caller's problem, we
-//              just truncate the message
-//
-// Complaints:  Brad :)
-#define GUESS_OF_VOXELCODE_SIZE 10
-#define MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE 1500
-#define SIZE_OF_COLOR_DATA sizeof(rgbColor)
-bool createVoxelEditMessage(unsigned char command, short int sequence,
-        int voxelCount, VoxelDetail* voxelDetails, unsigned char*& bufferOut, int& sizeOut) {
-
-    bool success = true; // assume the best
-    int messageSize = MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE; // just a guess for now
-    unsigned char* messageBuffer = new unsigned char[messageSize];
-
-    int numBytesPacketHeader = populateTypeAndVersion(messageBuffer, command);
-    unsigned short int* sequenceAt = (unsigned short int*) &messageBuffer[numBytesPacketHeader];
-    *sequenceAt = sequence;
-
-    // pack in timestamp
-    uint64_t now = usecTimestampNow();
-    uint64_t* timeAt = (uint64_t*)&messageBuffer[numBytesPacketHeader + sizeof(sequence)];
-    *timeAt = now;
-
-    unsigned char* copyAt = &messageBuffer[numBytesPacketHeader + sizeof(sequence) + sizeof(now)];
-    int actualMessageSize = numBytesPacketHeader + sizeof(sequence) + sizeof(now);
-
-    for (int i = 0; i < voxelCount && success; i++) {
-        // get the coded voxel
-        unsigned char* voxelData = pointToVoxel(voxelDetails[i].x,voxelDetails[i].y,voxelDetails[i].z,
-            voxelDetails[i].s,voxelDetails[i].red,voxelDetails[i].green,voxelDetails[i].blue);
-
-        int lengthOfVoxelData = bytesRequiredForCodeLength(*voxelData)+SIZE_OF_COLOR_DATA;
-
-        // make sure we have room to copy this voxel
-        if (actualMessageSize + lengthOfVoxelData > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) {
-            success = false;
-        } else {
-            // add it to our message
-            memcpy(copyAt, voxelData, lengthOfVoxelData);
-            copyAt += lengthOfVoxelData;
-            actualMessageSize += lengthOfVoxelData;
-        }
-        // cleanup
-        delete[] voxelData;
-    }
-
-    if (success) {
-        // finally, copy the result to the output
-        bufferOut = new unsigned char[actualMessageSize];
-        sizeOut = actualMessageSize;
-        memcpy(bufferOut, messageBuffer, actualMessageSize);
-    }
-
-    delete[] messageBuffer; // clean up our temporary buffer
-    return success;
-}
-
-/// encodes the voxel details portion of a voxel edit message
-bool encodeVoxelEditMessageDetails(unsigned char command, int voxelCount, VoxelDetail* voxelDetails,
-        unsigned char* bufferOut, int sizeIn, int& sizeOut) {
-
-    bool success = true; // assume the best
-    unsigned char* copyAt = bufferOut;
-    sizeOut = 0;
-
-    for (int i = 0; i < voxelCount && success; i++) {
-        // get the coded voxel
-        unsigned char* voxelData = pointToVoxel(voxelDetails[i].x,voxelDetails[i].y,voxelDetails[i].z,
-            voxelDetails[i].s,voxelDetails[i].red,voxelDetails[i].green,voxelDetails[i].blue);
-
-        int lengthOfVoxelData = bytesRequiredForCodeLength(*voxelData)+SIZE_OF_COLOR_DATA;
-
-        // make sure we have room to copy this voxel
-        if (sizeOut + lengthOfVoxelData > sizeIn) {
-            success = false;
-        } else {
-            // add it to our message
-            memcpy(copyAt, voxelData, lengthOfVoxelData);
-            copyAt += lengthOfVoxelData;
-            sizeOut += lengthOfVoxelData;
-        }
-        // cleanup
-        delete[] voxelData;
-    }
-
-    return success;
-}
-
-
 unsigned char* pointToOctalCode(float x, float y, float z, float s) {
     return pointToVoxel(x, y, z, s);
 }
@@ -572,7 +472,7 @@ int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3
     return destBuffer - startPosition;
 }
 
-int unpackFloatVec3FromSignedTwoByteFixed(unsigned char* sourceBuffer, glm::vec3& destination, int radix) {
+int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm::vec3& destination, int radix) {
     const unsigned char* startPosition = sourceBuffer;
     sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(destination.x), radix);
     sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(destination.y), radix);
@@ -590,7 +490,7 @@ int packFloatAngleToTwoByte(unsigned char* buffer, float angle) {
     return sizeof(uint16_t);
 }
 
-int unpackFloatAngleFromTwoByte(uint16_t* byteAnglePointer, float* destinationPointer) {
+int unpackFloatAngleFromTwoByte(const uint16_t* byteAnglePointer, float* destinationPointer) {
     *destinationPointer = (*byteAnglePointer / (float) std::numeric_limits::max()) * 360.0 - 180;
     return sizeof(uint16_t);
 }
@@ -607,7 +507,7 @@ int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput
     return sizeof(quatParts);
 }
 
-int unpackOrientationQuatFromBytes(unsigned char* buffer, glm::quat& quatOutput) {
+int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatOutput) {
     uint16_t quatParts[4];
     memcpy(&quatParts, buffer, sizeof(quatParts));
 
@@ -637,7 +537,7 @@ int packFloatRatioToTwoByte(unsigned char* buffer, float ratio) {
     return sizeof(ratioHolder);
 }
 
-int unpackFloatRatioFromTwoByte(unsigned char* buffer, float& ratio) {
+int unpackFloatRatioFromTwoByte(const unsigned char* buffer, float& ratio) {
     int16_t ratioHolder;
     memcpy(&ratioHolder, buffer, sizeof(ratioHolder));
 
@@ -668,7 +568,7 @@ int packClipValueToTwoByte(unsigned char* buffer, float clipValue) {
     return sizeof(holder);
 }
 
-int unpackClipValueFromTwoByte(unsigned char* buffer, float& clipValue) {
+int unpackClipValueFromTwoByte(const unsigned char* buffer, float& clipValue) {
     int16_t holder;
     memcpy(&holder, buffer, sizeof(holder));
 
@@ -690,7 +590,7 @@ int packFloatToByte(unsigned char* buffer, float value, float scaleBy) {
     return sizeof(holder);
 }
 
-int unpackFloatFromByte(unsigned char* buffer, float& value, float scaleBy) {
+int unpackFloatFromByte(const unsigned char* buffer, float& value, float scaleBy) {
     unsigned char holder;
     memcpy(&holder, buffer, sizeof(holder));
     value = ((float)holder / (float) 255) * scaleBy;
@@ -715,3 +615,31 @@ void debug::checkDeadBeef(void* memoryVoid, int size) {
     assert(memcmp(memoryAt, DEADBEEF, std::min(size, DEADBEEF_SIZE)) != 0);
 }
 
+//  Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's
+//  http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde,
+// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
+glm::vec3 safeEulerAngles(const glm::quat& q) {
+    float sy = 2.0f * (q.y * q.w - q.x * q.z);
+    if (sy < 1.0f - EPSILON) {
+        if (sy > -1.0f + EPSILON) {
+            return glm::degrees(glm::vec3(
+                atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)),
+                asinf(sy),
+                atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z))));
+
+        } else {
+            // not a unique solution; x + z = atan2(-m21, m11)
+            return glm::degrees(glm::vec3(
+                0.0f,
+                PIf * -0.5f,
+                atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z))));
+        }
+    } else {
+        // not a unique solution; x - z = atan2(-m21, m11)
+        return glm::degrees(glm::vec3(
+            0.0f,
+            PIf * 0.5f,
+            -atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z))));
+    }
+}
+
diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h
index a77663bbda..c982ab3596 100644
--- a/libraries/shared/src/SharedUtil.h
+++ b/libraries/shared/src/SharedUtil.h
@@ -27,6 +27,8 @@
 #include 
 #endif
 
+#include "PacketHeaders.h"
+
 const int BYTES_PER_COLOR = 3;
 const int BYTES_PER_FLAGS = 1;
 typedef unsigned char rgbColor[BYTES_PER_COLOR];
@@ -55,12 +57,12 @@ static const float METER            = 1.0f;
 static const float DECIMETER        = 0.1f;
 static const float CENTIMETER       = 0.01f;
 static const float MILLIIMETER      = 0.001f;
-static const uint64_t USECS_PER_MSEC = 1000;
-static const uint64_t MSECS_PER_SECOND = 1000;
-static const uint64_t USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;
+static const quint64 USECS_PER_MSEC = 1000;
+static const quint64 MSECS_PER_SECOND = 1000;
+static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;
 
-uint64_t usecTimestamp(const timeval *time);
-uint64_t usecTimestampNow();
+quint64 usecTimestamp(const timeval *time);
+quint64 usecTimestampNow();
 void usecTimestampNowForceClockSkew(int clockSkew);
 
 float randFloat();
@@ -94,27 +96,9 @@ bool cmdOptionExists(int argc, const char * argv[],const char* option);
 
 void sharedMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message);
 
-struct VoxelDetail {
-	float x;
-	float y;
-	float z;
-	float s;
-	unsigned char red;
-	unsigned char green;
-	unsigned char blue;
-};
-
 unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r = 0, unsigned char g = 0, unsigned char b = 0);
 unsigned char* pointToOctalCode(float x, float y, float z, float s);
 
-// Creates a full Voxel edit message, including command header, sequence, and details
-bool createVoxelEditMessage(unsigned char command, short int sequence,
-        int voxelCount, VoxelDetail* voxelDetails, unsigned char*& bufferOut, int& sizeOut);
-
-/// encodes the voxel details portion of a voxel edit message
-bool encodeVoxelEditMessageDetails(unsigned char command, int voxelCount, VoxelDetail* voxelDetails,
-        unsigned char* bufferOut, int sizeIn, int& sizeOut);
-
 #ifdef _WIN32
 void usleep(int waitTime);
 #endif
@@ -147,33 +131,39 @@ bool isBetween(int64_t value, int64_t max, int64_t min);
 
 // Angles are known to be between 0 and 360deg, this allows us to encode in 16bits with great accuracy
 int packFloatAngleToTwoByte(unsigned char* buffer, float angle);
-int unpackFloatAngleFromTwoByte(uint16_t* byteAnglePointer, float* destinationPointer);
+int unpackFloatAngleFromTwoByte(const uint16_t* byteAnglePointer, float* destinationPointer);
 
 // Orientation Quats are known to have 4 normalized components be between -1.0 and 1.0
 // this allows us to encode each component in 16bits with great accuracy
 int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput);
-int unpackOrientationQuatFromBytes(unsigned char* buffer, glm::quat& quatOutput);
+int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatOutput);
 
 // Ratios need the be highly accurate when less than 10, but not very accurate above 10, and they
 // are never greater than 1000 to 1, this allows us to encode each component in 16bits
 int packFloatRatioToTwoByte(unsigned char* buffer, float ratio);
-int unpackFloatRatioFromTwoByte(unsigned char* buffer, float& ratio);
+int unpackFloatRatioFromTwoByte(const unsigned char* buffer, float& ratio);
 
 // Near/Far Clip values need the be highly accurate when less than 10, but only integer accuracy above 10 and
 // they are never greater than 16,000, this allows us to encode each component in 16bits
 int packClipValueToTwoByte(unsigned char* buffer, float clipValue);
-int unpackClipValueFromTwoByte(unsigned char* buffer, float& clipValue);
+int unpackClipValueFromTwoByte(const unsigned char* buffer, float& clipValue);
 
 // Positive floats that don't need to be very precise
 int packFloatToByte(unsigned char* buffer, float value, float scaleBy);
-int unpackFloatFromByte(unsigned char* buffer, float& value, float scaleBy);
+int unpackFloatFromByte(const unsigned char* buffer, float& value, float scaleBy);
 
 // Allows sending of fixed-point numbers: radix 1 makes 15.1 number, radix 8 makes 8.8 number, etc
 int packFloatScalarToSignedTwoByteFixed(unsigned char* buffer, float scalar, int radix);
-int unpackFloatScalarFromSignedTwoByteFixed(int16_t* byteFixedPointer, float* destinationPointer, int radix);
+int unpackFloatScalarFromSignedTwoByteFixed(const int16_t* byteFixedPointer, float* destinationPointer, int radix);
 
-// A convenience for sending vec3's as fixed-poimt floats
+// A convenience for sending vec3's as fixed-point floats
 int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix);
-int unpackFloatVec3FromSignedTwoByteFixed(unsigned char* sourceBuffer, glm::vec3& destination, int radix);
+int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm::vec3& destination, int radix);
+
+#ifndef PIf
+#define PIf 3.14159265f
+#endif
+
+glm::vec3 safeEulerAngles(const glm::quat& q);
 
 #endif /* defined(__hifi__SharedUtil__) */
diff --git a/libraries/shared/src/ThreadedAssignment.cpp b/libraries/shared/src/ThreadedAssignment.cpp
index 7c7e07863a..2c1110491b 100644
--- a/libraries/shared/src/ThreadedAssignment.cpp
+++ b/libraries/shared/src/ThreadedAssignment.cpp
@@ -12,8 +12,8 @@
 #include "Logging.h"
 #include "ThreadedAssignment.h"
 
-ThreadedAssignment::ThreadedAssignment(const unsigned char* dataBuffer, int numBytes) :
-    Assignment(dataBuffer, numBytes),
+ThreadedAssignment::ThreadedAssignment(const QByteArray& packet) :
+    Assignment(packet),
     _isFinished(false)
 {
     
@@ -33,7 +33,7 @@ void ThreadedAssignment::setFinished(bool isFinished) {
     }
 }
 
-void ThreadedAssignment::commonInit(const char* targetName, NODE_TYPE nodeType) {
+void ThreadedAssignment::commonInit(const char* targetName, NodeType_t nodeType) {
     // change the logging target name while the assignment is running
     Logging::setTargetName(targetName);
     
diff --git a/libraries/shared/src/ThreadedAssignment.h b/libraries/shared/src/ThreadedAssignment.h
index 301a81bcf3..7c7aa9a63e 100644
--- a/libraries/shared/src/ThreadedAssignment.h
+++ b/libraries/shared/src/ThreadedAssignment.h
@@ -14,7 +14,7 @@
 class ThreadedAssignment : public Assignment {
     Q_OBJECT
 public:
-    ThreadedAssignment(const unsigned char* dataBuffer, int numBytes);
+    ThreadedAssignment(const QByteArray& packet);
     
     void setFinished(bool isFinished);
 public slots:
@@ -25,7 +25,7 @@ public slots:
     
     virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) = 0;
 protected:
-    void commonInit(const char* targetName, NODE_TYPE nodeType);
+    void commonInit(const char* targetName, NodeType_t nodeType);
     bool _isFinished;
 private slots:
     void checkInWithDomainServerOrExit();
diff --git a/libraries/voxel-server/src/VoxelNodeData.h b/libraries/voxel-server/src/VoxelNodeData.h
index 9697b9cec3..54ef9da5b6 100644
--- a/libraries/voxel-server/src/VoxelNodeData.h
+++ b/libraries/voxel-server/src/VoxelNodeData.h
@@ -15,7 +15,7 @@
 class VoxelNodeData : public OctreeQueryNode {
 public:
     VoxelNodeData() : OctreeQueryNode() {  };
-    virtual PACKET_TYPE getMyPacketType() const { return PACKET_TYPE_VOXEL_DATA; }
+    virtual PacketType getMyPacketType() const { return PacketTypeVoxelData; }
 };
 
 #endif /* defined(__hifi__VoxelNodeData__) */
diff --git a/libraries/voxel-server/src/VoxelServer.cpp b/libraries/voxel-server/src/VoxelServer.cpp
index 3a8c0d1364..b71bcd3dc2 100644
--- a/libraries/voxel-server/src/VoxelServer.cpp
+++ b/libraries/voxel-server/src/VoxelServer.cpp
@@ -16,7 +16,7 @@ const char* VOXEL_SERVER_NAME = "Voxel";
 const char* VOXEL_SERVER_LOGGING_TARGET_NAME = "voxel-server";
 const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo";
 
-VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) : OctreeServer(dataBuffer, numBytes) {
+VoxelServer::VoxelServer(const QByteArray& packet) : OctreeServer(packet) {
     // nothing special to do here...
 }
 
@@ -38,7 +38,7 @@ bool VoxelServer::hasSpecialPacketToSend(Node* node) {
 }
 
 int VoxelServer::sendSpecialPacket(Node* node) {
-    int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA);
+    int numBytesPacketHeader = populatePacketHeader(reinterpret_cast(_tempOutputBuffer), PacketTypeEnvironmentData);
     int envPacketLength = numBytesPacketHeader;
     int environmentsToSend = getSendMinimalEnvironment() ? 1 : getEnvironmentDataCount();
 
@@ -69,5 +69,5 @@ void VoxelServer::beforeRun() {
     }
     qDebug("Sending environments=%s", debug::valueOf(_sendEnvironments));
     
-    NodeList::getInstance()->addNodeTypeToInterestSet(NODE_TYPE_ANIMATION_SERVER);
+    NodeList::getInstance()->addNodeTypeToInterestSet(NodeType::AnimationServer);
 }
diff --git a/libraries/voxel-server/src/VoxelServer.h b/libraries/voxel-server/src/VoxelServer.h
index 06cea132c0..183224b2a7 100644
--- a/libraries/voxel-server/src/VoxelServer.h
+++ b/libraries/voxel-server/src/VoxelServer.h
@@ -25,7 +25,7 @@
 /// Handles assignments of type VoxelServer - sending voxels to various clients.
 class VoxelServer : public OctreeServer {
 public:
-    VoxelServer(const unsigned char* dataBuffer, int numBytes);
+    VoxelServer(const QByteArray& packet);
     ~VoxelServer();
 
     bool wantSendEnvironments() const { return _sendEnvironments; }
@@ -36,8 +36,8 @@ public:
     // Subclasses must implement these methods
     virtual OctreeQueryNode* createOctreeQueryNode();
     virtual Octree* createTree();
-    virtual unsigned char getMyNodeType() const { return NODE_TYPE_VOXEL_SERVER; }
-    virtual PACKET_TYPE getMyQueryMessageType() const { return PACKET_TYPE_VOXEL_QUERY; }
+    virtual unsigned char getMyNodeType() const { return NodeType::VoxelServer; }
+    virtual PacketType getMyQueryMessageType() const { return PacketTypeVoxelQuery; }
     virtual const char* getMyServerName() const { return VOXEL_SERVER_NAME; }
     virtual const char* getMyLoggingServerTargetName() const { return VOXEL_SERVER_LOGGING_TARGET_NAME; }
     virtual const char* getMyDefaultPersistFilename() const { return LOCAL_VOXELS_PERSIST_FILE; }
diff --git a/libraries/voxels/src/EnvironmentData.cpp b/libraries/voxels/src/EnvironmentData.cpp
index 935d4601e9..5cee88e127 100644
--- a/libraries/voxels/src/EnvironmentData.cpp
+++ b/libraries/voxels/src/EnvironmentData.cpp
@@ -61,8 +61,8 @@ int EnvironmentData::getBroadcastData(unsigned char* destinationBuffer) const {
     return destinationBuffer - bufferStart;
 }
 
-int EnvironmentData::parseData(unsigned char* sourceBuffer, int numBytes) {
-    unsigned char* startPosition = sourceBuffer;
+int EnvironmentData::parseData(const unsigned char* sourceBuffer, int numBytes) {
+    const unsigned char* startPosition = sourceBuffer;
     
     memcpy(&_id, sourceBuffer, sizeof(_id));
     sourceBuffer += sizeof(_id);
diff --git a/libraries/voxels/src/EnvironmentData.h b/libraries/voxels/src/EnvironmentData.h
index b801caba96..90cc0763fe 100644
--- a/libraries/voxels/src/EnvironmentData.h
+++ b/libraries/voxels/src/EnvironmentData.h
@@ -43,7 +43,7 @@ public:
     float getSunBrightness() const { return _sunBrightness; }
 
     int getBroadcastData(unsigned char* destinationBuffer) const;
-    int parseData(unsigned char* sourceBuffer, int numBytes);
+    int parseData(const unsigned char* sourceBuffer, int numBytes);
     
 private:
 
diff --git a/libraries/voxels/src/VoxelConstants.h b/libraries/voxels/src/VoxelConstants.h
index b57562f15e..d4cbc54f18 100644
--- a/libraries/voxels/src/VoxelConstants.h
+++ b/libraries/voxels/src/VoxelConstants.h
@@ -40,7 +40,7 @@ const glBufferIndex GLBUFFER_INDEX_UNKNOWN = ULONG_MAX;
 const float SIXTY_FPS_IN_MILLISECONDS = 1000.0f / 60.0f;
 const float VIEW_CULLING_RATE_IN_MILLISECONDS = 1000.0f; // once a second is fine
 
-const uint64_t CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS = 1000 * 5; // 1 packet every 50 milliseconds
+const quint64 CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS = 1000 * 5; // 1 packet every 50 milliseconds
 
 
 const int DEFAULT_MAX_VOXEL_PPS = 600; // the default maximum PPS we think a voxel server should send to a client
diff --git a/libraries/voxels/src/VoxelDetail.cpp b/libraries/voxels/src/VoxelDetail.cpp
new file mode 100644
index 0000000000..d4ab5cafcb
--- /dev/null
+++ b/libraries/voxels/src/VoxelDetail.cpp
@@ -0,0 +1,37 @@
+//
+//  VoxelDetail.cpp
+//  hifi
+//
+//  Created by Brad Hefta-Gaub on 1/29/2014
+//  Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
+//
+#include "VoxelDetail.h"
+
+void registerVoxelMetaTypes(QScriptEngine* engine) {
+    qScriptRegisterMetaType(engine, voxelDetailToScriptValue, voxelDetailFromScriptValue);
+}
+
+QScriptValue voxelDetailToScriptValue(QScriptEngine* engine, const VoxelDetail& voxelDetail) {
+    QScriptValue obj = engine->newObject();
+    obj.setProperty("x", voxelDetail.x * (float)TREE_SCALE);
+    obj.setProperty("y", voxelDetail.y * (float)TREE_SCALE);
+    obj.setProperty("z", voxelDetail.z * (float)TREE_SCALE);
+    obj.setProperty("s", voxelDetail.s * (float)TREE_SCALE);
+    obj.setProperty("red", voxelDetail.red);
+    obj.setProperty("green", voxelDetail.green);
+    obj.setProperty("blue", voxelDetail.blue);
+    return obj;
+}
+
+void voxelDetailFromScriptValue(const QScriptValue &object, VoxelDetail& voxelDetail) {
+    voxelDetail.x = object.property("x").toVariant().toFloat() / (float)TREE_SCALE;
+    voxelDetail.y = object.property("y").toVariant().toFloat() / (float)TREE_SCALE;
+    voxelDetail.z = object.property("z").toVariant().toFloat() / (float)TREE_SCALE;
+    voxelDetail.s = object.property("s").toVariant().toFloat() / (float)TREE_SCALE;
+    voxelDetail.red = object.property("red").toVariant().toInt();
+    voxelDetail.green = object.property("green").toVariant().toInt();
+    voxelDetail.blue = object.property("blue").toVariant().toInt();
+}
+
+
+
diff --git a/libraries/voxels/src/VoxelDetail.h b/libraries/voxels/src/VoxelDetail.h
new file mode 100644
index 0000000000..ca1ff3940b
--- /dev/null
+++ b/libraries/voxels/src/VoxelDetail.h
@@ -0,0 +1,36 @@
+//
+//  VoxelDetail.h
+//  hifi
+//
+//  Created by Brad Hefta-Gaub on 1/29/2014
+//  Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
+//
+//
+
+#ifndef __hifi__VoxelDetail__
+#define __hifi__VoxelDetail__
+
+#include 
+
+#include 
+#include "VoxelConstants.h"
+
+struct VoxelDetail {
+	float x;
+	float y;
+	float z;
+	float s;
+	unsigned char red;
+	unsigned char green;
+	unsigned char blue;
+};
+
+Q_DECLARE_METATYPE(VoxelDetail)
+
+void registerVoxelMetaTypes(QScriptEngine* engine);
+
+QScriptValue voxelDetailToScriptValue(QScriptEngine* engine, const VoxelDetail& color);
+void voxelDetailFromScriptValue(const QScriptValue &object, VoxelDetail& color);
+
+
+#endif /* defined(__hifi__VoxelDetail__) */
\ No newline at end of file
diff --git a/libraries/voxels/src/VoxelEditPacketSender.cpp b/libraries/voxels/src/VoxelEditPacketSender.cpp
index af3ae63377..fea75abf23 100644
--- a/libraries/voxels/src/VoxelEditPacketSender.cpp
+++ b/libraries/voxels/src/VoxelEditPacketSender.cpp
@@ -14,8 +14,106 @@
 #include 
 #include "VoxelEditPacketSender.h"
 
+//////////////////////////////////////////////////////////////////////////////////////////
+// Function:    createVoxelEditMessage()
+// Description: creates an "insert" or "remove" voxel message for a voxel code
+//              corresponding to the closest voxel which encloses a cube with
+//              lower corners at x,y,z, having side of length S.
+//              The input values x,y,z range 0.0 <= v < 1.0
+//              message should be either 'S' for SET or 'E' for ERASE
+//
+// IMPORTANT:   The buffer is returned to you a buffer which you MUST delete when you are
+//              done with it.
+//
+// HACK ATTACK: Well, what if this is larger than the MTU? That's the caller's problem, we
+//              just truncate the message
+//
+// Complaints:  Brad :)
+#define GUESS_OF_VOXELCODE_SIZE 10
+#define MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE 1500
+#define SIZE_OF_COLOR_DATA sizeof(rgbColor)
+bool createVoxelEditMessage(PacketType command, short int sequence,
+                            int voxelCount, VoxelDetail* voxelDetails, unsigned char*& bufferOut, int& sizeOut) {
+    
+    bool success = true; // assume the best
+    int messageSize = MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE; // just a guess for now
+    unsigned char* messageBuffer = new unsigned char[messageSize];
+    
+    int numBytesPacketHeader = populatePacketHeader(reinterpret_cast(messageBuffer), command);
+    unsigned short int* sequenceAt = (unsigned short int*) &messageBuffer[numBytesPacketHeader];
+    *sequenceAt = sequence;
+    
+    // pack in timestamp
+    quint64 now = usecTimestampNow();
+    quint64* timeAt = (quint64*)&messageBuffer[numBytesPacketHeader + sizeof(sequence)];
+    *timeAt = now;
+    
+    unsigned char* copyAt = &messageBuffer[numBytesPacketHeader + sizeof(sequence) + sizeof(now)];
+    int actualMessageSize = numBytesPacketHeader + sizeof(sequence) + sizeof(now);
+    
+    for (int i = 0; i < voxelCount && success; i++) {
+        // get the coded voxel
+        unsigned char* voxelData = pointToVoxel(voxelDetails[i].x,voxelDetails[i].y,voxelDetails[i].z,
+                                                voxelDetails[i].s,voxelDetails[i].red,voxelDetails[i].green,voxelDetails[i].blue);
+        
+        int lengthOfVoxelData = bytesRequiredForCodeLength(*voxelData)+SIZE_OF_COLOR_DATA;
+        
+        // make sure we have room to copy this voxel
+        if (actualMessageSize + lengthOfVoxelData > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) {
+            success = false;
+        } else {
+            // add it to our message
+            memcpy(copyAt, voxelData, lengthOfVoxelData);
+            copyAt += lengthOfVoxelData;
+            actualMessageSize += lengthOfVoxelData;
+        }
+        // cleanup
+        delete[] voxelData;
+    }
 
-void VoxelEditPacketSender::sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail) {
+    if (success) {
+        // finally, copy the result to the output
+        bufferOut = new unsigned char[actualMessageSize];
+        sizeOut = actualMessageSize;
+        memcpy(bufferOut, messageBuffer, actualMessageSize);
+    }
+
+    delete[] messageBuffer; // clean up our temporary buffer
+    return success;
+}
+
+/// encodes the voxel details portion of a voxel edit message
+bool encodeVoxelEditMessageDetails(PacketType, int voxelCount, VoxelDetail* voxelDetails,
+                                   unsigned char* bufferOut, int sizeIn, int& sizeOut) {
+    
+    bool success = true; // assume the best
+    unsigned char* copyAt = bufferOut;
+    sizeOut = 0;
+    
+    for (int i = 0; i < voxelCount && success; i++) {
+        // get the coded voxel
+        unsigned char* voxelData = pointToVoxel(voxelDetails[i].x,voxelDetails[i].y,voxelDetails[i].z,
+                                                voxelDetails[i].s,voxelDetails[i].red,voxelDetails[i].green,voxelDetails[i].blue);
+        
+        int lengthOfVoxelData = bytesRequiredForCodeLength(*voxelData)+SIZE_OF_COLOR_DATA;
+        
+        // make sure we have room to copy this voxel
+        if (sizeOut + lengthOfVoxelData > sizeIn) {
+            success = false;
+        } else {
+            // add it to our message
+            memcpy(copyAt, voxelData, lengthOfVoxelData);
+            copyAt += lengthOfVoxelData;
+            sizeOut += lengthOfVoxelData;
+        }
+        // cleanup
+        delete[] voxelData;
+    }
+    
+    return success;
+}
+
+void VoxelEditPacketSender::sendVoxelEditMessage(PacketType type, VoxelDetail& detail) {
     // allows app to disable sending if for example voxels have been disabled
     if (!_shouldSend) {
         return; // bail early
@@ -39,7 +137,7 @@ void VoxelEditPacketSender::sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail&
     }
 }
 
-void VoxelEditPacketSender::queueVoxelEditMessages(PACKET_TYPE type, int numberOfDetails, VoxelDetail* details) {
+void VoxelEditPacketSender::queueVoxelEditMessages(PacketType type, int numberOfDetails, VoxelDetail* details) {
     if (!_shouldSend) {
         return; // bail early
     }
@@ -54,4 +152,3 @@ void VoxelEditPacketSender::queueVoxelEditMessages(PACKET_TYPE type, int numberO
         }
     }    
 }
-
diff --git a/libraries/voxels/src/VoxelEditPacketSender.h b/libraries/voxels/src/VoxelEditPacketSender.h
index ec9b74dff8..90085635b0 100644
--- a/libraries/voxels/src/VoxelEditPacketSender.h
+++ b/libraries/voxels/src/VoxelEditPacketSender.h
@@ -12,25 +12,26 @@
 #define __shared__VoxelEditPacketSender__
 
 #include 
+#include "VoxelDetail.h"
 
 /// Utility for processing, packing, queueing and sending of outbound edit voxel messages.
 class VoxelEditPacketSender :  public OctreeEditPacketSender {
     Q_OBJECT
 public:
     /// Send voxel edit message immediately
-    void sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail);
+    void sendVoxelEditMessage(PacketType type, VoxelDetail& detail);
 
     /// Queues a single voxel edit message. Will potentially send a pending multi-command packet. Determines which voxel-server
     /// node or nodes the packet should be sent to. Can be called even before voxel servers are known, in which case up to 
     /// MaxPendingMessages will be buffered and processed when voxel servers are known.
-    void queueVoxelEditMessage(PACKET_TYPE type, unsigned char* codeColorBuffer, ssize_t length) {
+    void queueVoxelEditMessage(PacketType type, unsigned char* codeColorBuffer, ssize_t length) {
         queueOctreeEditMessage(type, codeColorBuffer, length);
     }
 
     /// Queues an array of several voxel edit messages. Will potentially send a pending multi-command packet. Determines 
     /// which voxel-server node or nodes the packet should be sent to. Can be called even before voxel servers are known, in 
     /// which case up to MaxPendingMessages will be buffered and processed when voxel servers are known.
-    void queueVoxelEditMessages(PACKET_TYPE type, int numberOfDetails, VoxelDetail* details);
+    void queueVoxelEditMessages(PacketType type, int numberOfDetails, VoxelDetail* details);
 
     /// call this to inform the VoxelEditPacketSender of the voxel server jurisdictions. This is required for normal operation.
     /// The internal contents of the jurisdiction map may change throughout the lifetime of the VoxelEditPacketSender. This map
@@ -44,6 +45,6 @@ public:
     bool voxelServersExist() const { return serversExist(); }
 
     // My server type is the voxel server
-    virtual unsigned char getMyNodeType() const { return NODE_TYPE_VOXEL_SERVER; }
+    virtual unsigned char getMyNodeType() const { return NodeType::VoxelServer; }
 };
 #endif // __shared__VoxelEditPacketSender__
diff --git a/libraries/voxels/src/VoxelPacketData.h b/libraries/voxels/src/VoxelPacketData.h
index 200bd7d30c..bf2a759225 100644
--- a/libraries/voxels/src/VoxelPacketData.h
+++ b/libraries/voxels/src/VoxelPacketData.h
@@ -28,17 +28,19 @@
 
 typedef unsigned char VOXEL_PACKET_FLAGS;
 typedef uint16_t VOXEL_PACKET_SEQUENCE;
-typedef uint64_t VOXEL_PACKET_SENT_TIME;
+typedef quint64 VOXEL_PACKET_SENT_TIME;
 typedef uint16_t VOXEL_PACKET_INTERNAL_SECTION_SIZE;
 const int MAX_VOXEL_PACKET_SIZE = MAX_PACKET_SIZE;
-const int VOXEL_PACKET_HEADER_SIZE = (sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION) + sizeof(VOXEL_PACKET_FLAGS) 
-                + sizeof(VOXEL_PACKET_SEQUENCE) + sizeof(VOXEL_PACKET_SENT_TIME));
+
+// this is overly conservative - uses 8 bytes for PacketType which could be as compact as a single byte
+const int VOXEL_PACKET_HEADER_SIZE = MAX_PACKET_HEADER_BYTES + sizeof(VOXEL_PACKET_FLAGS)
+                + sizeof(VOXEL_PACKET_SEQUENCE) + sizeof(VOXEL_PACKET_SENT_TIME);
 
 const int MAX_VOXEL_PACKET_DATA_SIZE = MAX_PACKET_SIZE - VOXEL_PACKET_HEADER_SIZE;
             
 const int MAX_VOXEL_UNCOMRESSED_PACKET_SIZE = MAX_VOXEL_PACKET_DATA_SIZE;
 
-/// Handles packing of the data portion of PACKET_TYPE_VOXEL_DATA messages.
+/// Handles packing of the data portion of PacketType_VOXEL_DATA messages.
 class VoxelPacketData : public OctreePacketData {
 public:
     VoxelPacketData(bool enableCompression = false, int maxFinalizedSize = MAX_OCTREE_PACKET_DATA_SIZE);
diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp
index 1a1f810661..90fdbb8a05 100644
--- a/libraries/voxels/src/VoxelTree.cpp
+++ b/libraries/voxels/src/VoxelTree.cpp
@@ -224,7 +224,7 @@ void VoxelTree::nudgeLeaf(VoxelTreeElement* element, void* extraData) {
     glm::vec3 nudge = args->nudgeVec;
 
     // delete the old element
-    args->voxelEditSenderPtr->sendVoxelEditMessage(PACKET_TYPE_VOXEL_ERASE, voxelDetails);
+    args->voxelEditSenderPtr->sendVoxelEditMessage(PacketTypeVoxelErase, voxelDetails);
 
     // nudge the old element
     voxelDetails.x += nudge.x;
@@ -232,7 +232,7 @@ void VoxelTree::nudgeLeaf(VoxelTreeElement* element, void* extraData) {
     voxelDetails.z += nudge.z;
 
     // create a new voxel in its stead
-    args->voxelEditSenderPtr->sendVoxelEditMessage(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE, voxelDetails);
+    args->voxelEditSenderPtr->sendVoxelEditMessage(PacketTypeVoxelSetDestructive, voxelDetails);
 }
 
 // Recurses voxel element with an operation function
@@ -508,32 +508,33 @@ void VoxelTree::readCodeColorBufferToTreeRecursion(VoxelTreeElement* node, ReadC
     }
 }
 
-bool VoxelTree::handlesEditPacketType(PACKET_TYPE packetType) const {
+bool VoxelTree::handlesEditPacketType(PacketType packetType) const {
     // we handle these types of "edit" packets
     switch (packetType) {
-        case PACKET_TYPE_VOXEL_SET:
-        case PACKET_TYPE_VOXEL_SET_DESTRUCTIVE:
-        case PACKET_TYPE_VOXEL_ERASE:
+        case PacketTypeVoxelSet:
+        case PacketTypeVoxelSetDestructive:
+        case PacketTypeVoxelErase:
             return true;
+        default:
+            return false;
+            
     }
-    return false;
 }
 
-int VoxelTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength,
-                    unsigned char* editData, int maxLength, Node* senderNode) {
-
-    int processedBytes = 0;
+int VoxelTree::processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
+                    const unsigned char* editData, int maxLength, Node* senderNode) {
+    
     // we handle these types of "edit" packets
     switch (packetType) {
-        case PACKET_TYPE_VOXEL_SET:
-        case PACKET_TYPE_VOXEL_SET_DESTRUCTIVE: {
-            bool destructive = (packetType == PACKET_TYPE_VOXEL_SET_DESTRUCTIVE);
+        case PacketTypeVoxelSet:
+        case PacketTypeVoxelSetDestructive: {
+            bool destructive = (packetType == PacketTypeVoxelSetDestructive);
             int octets = numberOfThreeBitSectionsInCode(editData, maxLength);
 
             if (octets == OVERFLOWED_OCTCODE_BUFFER) {
                 printf("WARNING! Got voxel edit record that would overflow buffer in numberOfThreeBitSectionsInCode(), ");
                 printf("bailing processing of packet!\n");
-                return processedBytes;
+                return 0;
             }
 
             const int COLOR_SIZE_IN_BYTES = 3;
@@ -543,7 +544,7 @@ int VoxelTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* pack
             if (voxelDataSize > maxLength) {
                 printf("WARNING! Got voxel edit record that would overflow buffer, bailing processing of packet!\n");
                 printf("bailing processing of packet!\n");
-                return processedBytes;
+                return 0;
             }
 
             readCodeColorBufferToTree(editData, destructive);
@@ -551,10 +552,11 @@ int VoxelTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* pack
             return voxelDataSize;
         } break;
 
-        case PACKET_TYPE_VOXEL_ERASE:
+        case PacketTypeVoxelErase:
             processRemoveOctreeElementsBitstream((unsigned char*)packetData, packetLength);
             return maxLength;
+        default:
+            return 0;
     }
-    return processedBytes;
 }
 
diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h
index c30bc82aa8..4f30293766 100644
--- a/libraries/voxels/src/VoxelTree.h
+++ b/libraries/voxels/src/VoxelTree.h
@@ -51,10 +51,10 @@ public:
 
     void readCodeColorBufferToTree(const unsigned char* codeColorBuffer, bool destructive = false);
 
-    virtual PACKET_TYPE expectedDataPacketType() const { return PACKET_TYPE_VOXEL_DATA; }
-    virtual bool handlesEditPacketType(PACKET_TYPE packetType) const;
-    virtual int processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength,
-                    unsigned char* editData, int maxLength, Node* senderNode);
+    virtual PacketType expectedDataPacketType() const { return PacketTypeVoxelData; }
+    virtual bool handlesEditPacketType(PacketType packetType) const;
+    virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
+                    const unsigned char* editData, int maxLength, Node* senderNode);
     void processSetVoxelsBitstream(const unsigned char* bitstream, int bufferSizeBytes);
 
 /**
diff --git a/libraries/voxels/src/VoxelTreeElement.h b/libraries/voxels/src/VoxelTreeElement.h
index 86732f4b66..028d2456eb 100644
--- a/libraries/voxels/src/VoxelTreeElement.h
+++ b/libraries/voxels/src/VoxelTreeElement.h
@@ -91,5 +91,4 @@ protected:
     nodeColor _currentColor; /// Client only, false color of this voxel, 4 bytes
 };
 
-
 #endif /* defined(__hifi__VoxelTreeElement__) */
\ No newline at end of file
diff --git a/libraries/voxels/src/VoxelsScriptingInterface.cpp b/libraries/voxels/src/VoxelsScriptingInterface.cpp
index 3f4e19f60a..d82912c44b 100644
--- a/libraries/voxels/src/VoxelsScriptingInterface.cpp
+++ b/libraries/voxels/src/VoxelsScriptingInterface.cpp
@@ -8,7 +8,7 @@
 
 #include "VoxelsScriptingInterface.h"
 
-void VoxelsScriptingInterface::queueVoxelAdd(PACKET_TYPE addPacketType, VoxelDetail& addVoxelDetails) {
+void VoxelsScriptingInterface::queueVoxelAdd(PacketType addPacketType, VoxelDetail& addVoxelDetails) {
     getVoxelPacketSender()->queueVoxelEditMessages(addPacketType, 1, &addVoxelDetails);
 }
 
@@ -19,7 +19,7 @@ void VoxelsScriptingInterface::setVoxelNonDestructive(float x, float y, float z,
                                     scale / (float)TREE_SCALE, red, green, blue};
 
     // queue the packet
-    queueVoxelAdd(PACKET_TYPE_VOXEL_SET, addVoxelDetail);
+    queueVoxelAdd(PacketTypeVoxelSet, addVoxelDetail);
 }
 
 void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale,
@@ -29,7 +29,7 @@ void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale,
                                     scale / (float)TREE_SCALE, red, green, blue};
 
     // queue the destructive add
-    queueVoxelAdd(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE, addVoxelDetail);
+    queueVoxelAdd(PacketTypeVoxelSetDestructive, addVoxelDetail);
 }
 
 void VoxelsScriptingInterface::eraseVoxel(float x, float y, float z, float scale) {
@@ -38,6 +38,6 @@ void VoxelsScriptingInterface::eraseVoxel(float x, float y, float z, float scale
     VoxelDetail deleteVoxelDetail = {x / (float)TREE_SCALE, y / (float)TREE_SCALE, z / (float)TREE_SCALE, 
                                         scale / (float)TREE_SCALE, 0, 0, 0};
 
-    getVoxelPacketSender()->queueVoxelEditMessages(PACKET_TYPE_VOXEL_ERASE, 1, &deleteVoxelDetail);
+    getVoxelPacketSender()->queueVoxelEditMessages(PacketTypeVoxelErase, 1, &deleteVoxelDetail);
 }
 
diff --git a/libraries/voxels/src/VoxelsScriptingInterface.h b/libraries/voxels/src/VoxelsScriptingInterface.h
index 97bdfb2c59..877383e0b0 100644
--- a/libraries/voxels/src/VoxelsScriptingInterface.h
+++ b/libraries/voxels/src/VoxelsScriptingInterface.h
@@ -22,7 +22,7 @@ class VoxelsScriptingInterface : public OctreeScriptingInterface {
 public:    
     VoxelEditPacketSender* getVoxelPacketSender() { return (VoxelEditPacketSender*)getPacketSender(); }
 
-    virtual NODE_TYPE getServerNodeType() const { return NODE_TYPE_VOXEL_SERVER; }
+    virtual NodeType_t getServerNodeType() const { return NodeType::VoxelServer; }
     virtual OctreeEditPacketSender* createPacketSender() { return new VoxelEditPacketSender(); }
 
 public slots:
@@ -54,23 +54,7 @@ public slots:
     void eraseVoxel(float x, float y, float z, float scale);
 
 private:
-    void queueVoxelAdd(PACKET_TYPE addPacketType, VoxelDetail& addVoxelDetails);
-};
-
-class VoxelDetailScriptObject  : public QObject {
-    Q_OBJECT
-public:
-    VoxelDetailScriptObject(VoxelDetail* voxelDetail) { _voxelDetail = voxelDetail; }
-
-public slots:
-    /// position in meter units
-    glm::vec3 getPosition() const { return glm::vec3(_voxelDetail->x, _voxelDetail->y, _voxelDetail->z) * (float)TREE_SCALE; }
-    xColor getColor() const { xColor color = { _voxelDetail->red, _voxelDetail->green, _voxelDetail->blue }; return color; }
-    /// scale in meter units
-    float getScale() const { return _voxelDetail->s * (float)TREE_SCALE; }
-
-private:
-    VoxelDetail* _voxelDetail;
+    void queueVoxelAdd(PacketType addPacketType, VoxelDetail& addVoxelDetails);
 };
 
 #endif /* defined(__hifi__VoxelsScriptingInterface__) */